CRUD and basic operations

This page contains examples of CRUD queries and other common database operations in Fauna Query Language (FQL) and Fauna Schema Language (FSL).

Collections

Create a collection

To create a collection, add an FSL collection schema to Fauna with a staged schema change using the Fauna Dashboard or the Fauna CLI.

// Defines the `Customer` collection.
collection Customer {
  // Field definitions.
  // Define the structure of the collection's documents.
  name: String
  email: String
  address: {
    street: String
    city: String
    state: String
    postalCode: String
    country: String
  }

  // Wildcard constraint.
  // Allows arbitrary ad hoc fields of any type.
  *: Any
  // If a collection schema has no field definitions
  // and no wildcard constraint, it has an implicit
  // wildcard constraint of `*: Any`.
}
See Schema

Edit a collection

Update a collection’s document type using a zero-downtime schema migration:

collection Customer {
  name: String
  email: String
  address: {
    street: String
    city: String
    state: String
    postalCode: String
    country: String
  }
  // Adds the `points` field. Accepts `int` or `null` values.
  // Accepting `null` means the field is not required.
  points: Int?
  // Adds the `typeConflicts` field as a catch-all field for
  // existing `points` values that aren't `Int` or `null`.
  typeConflicts: { *: Any }?
  *: Any

  migrations {
    // Adds the `typeConflicts` field.
    add .typeConflicts
    // Adds the `points` field.
    add .points
    // Nests non-conforming `points` and `typeConflicts`
    // field values in the `typeConflicts` catch-all field.
    move_conflicts .typeConflicts
  }
}
See Schema migrations

View collections

Collection.all()
Reference: Collection.all()

Delete a collection

To delete a collection, delete its schema using the Fauna Dashboard or the Fauna CLI's schema push command. Deleting a collection deletes its documents and indexes.

Documents

Create a document

// Creates a `Customer` collection document.
Customer.create({
  name: "John Doe",
  email: "john.doe@example.com",
  address: {
    street: "123 Main St",
    city: "San Francisco",
    state: "CA",
    postalCode: "12345",
    country: "United States"
  }
})
Reference: collection.create()

Get a single document

// Gets a `Customer` collection document.
// Replace `<DOCUMENT_ID>` with a document `id`.
Customer.byId("<DOCUMENT_ID>")
Reference: collection.byId()

Update a document

// Updates a `Customer` collection document.
Customer.byId("<DOCUMENT_ID>")?.update({
  // Updates the existing `name` field value.
  name: "Jonathan Doe",
  // Adds new `points` field.
  points: 42
})
Reference: document.update()

Remove a document field

// Updates a `Customer` collection document.
Customer.byId("<DOCUMENT_ID>")?.update({
  // Removes the `points` field.
  points: null
})
Reference: document.update()

Replace a document

// Replaces a `Customer` collection document.
Customer.byId("<DOCUMENT_ID>")?.replace({
  name: "Jane Doe",
  email: "jane.doe@example.com",
  address: {
    street: "87856 Mendota Court",
    city: "Washington",
    state: "DC",
    postalCode: "20220",
    country: "US"
  }
})
Reference: document.replace()

Delete a document

// Deletes a `Customer` collection document.
Customer.byId("<DOCUMENT_ID>")?.delete()
Reference: document.delete()

Bulk writes

Use set.forEach() to iteratively update each document in a Set:

// Get a Set of `Customer` collection documents with an
// `address` in the `state` of `DC`.
let customers = Customer.where(.address?.state == "DC")
// Use `forEach()` to update each document in the previous Set.
customers.forEach(doc => doc.update({
  address: {
    street: doc?.address?.street,
    city: doc?.address?.city,
    state: "District of Columbia",
    postalCode: doc?.address?.postalCode,
    country: doc?.address?.country,
  }
})) // `forEach()` returns `null`.

For more examples, see Bulk writes.

Reference: set.forEach()

Indexes and reads

Create an index

You define indexes in FSL as part of a collection schema:

collection Customer {
  ...
  index byEmail {
    // `terms` are document fields for exact match searches.
    // In this example, you get `Customer` collection documents
    // by their `email` field value.
    terms [.email]
    // `values` are document fields for sorting and range searches.
    // In this example, you sort or filter index results by their
    // descending `name` and `email` field values.
    values [.name, .email]
  }
}
See Define an index
// Runs an unindexed query.
Customer.where(.email == "alice.appleseed@example.com")

For better performance on large datasets, use an index with a term to run an exact match search.

Define the index in the collection schema:

collection Customer {
  ...
  // Defines the `byEmail()` index for the `Customer`
  // collection.
  index byEmail {
    // Includes the `email` field as an index term.
    terms [.email]
    values [.name, .email]
  }
}

You call an index as a method on its collection:

// Uses the `Customer` collection's `byEmail()` index
// to run an exact match search on an `email` field value.
Customer.byEmail("alice.appleseed@example.com")
See Run an exact match search

Sort collection documents

// Runs an unindexed query.
// Sorts `Product` collection documents by:
// - `price` (ascending), then ...
// - `name` (ascending), then ...
// - `description` (ascending), then ...
// - `stock` (ascending).
Product.all().order(.price, .name, .description, .stock)

For better performance on large datasets, use an index with values to sort collection documents.

Define the index in the collection schema:

collection Product {
  ...
  // Defines the `sortedByPriceLowToHigh()` index.
  index sortedByPriceLowToHigh {
    // `values` are document fields for sorting and range searches.
     values [.price, .name, .description, .stock]
  }
}

Call the index in a query:

// Uses the `Product` collection's `sortedByPriceLowToHigh()` index
// to sort `Product` collection documents by:
// - `price` (ascending), then ...
// - `name` (ascending), then ...
// - `description` (ascending), then ...
// - `stock` (ascending).
Product.sortedByPriceLowToHigh()
See Sort collection documents
// Runs an unindexed query.
// Get `Product` collection documents with a `price` between
// 10_00 and 100_00 (inclusive).
Product.where(.price >= 10_00 && .price <= 100_00)
  .order(.price, .name, .description, .stock)

For better performance on large datasets, use an index with values to run range searches on collection documents,

Define the index in the collection schema:

collection Product {
  ...
  // Defines the `sortedByPriceLowToHigh()` index.
  index sortedByPriceLowToHigh {
    // `values` are document fields for sorting and range searches.
    values [.price, .name, .description, .stock]
  }
}

Call the index in a query:

// Get `Product` collection documents with a `price` between
// 10_00 and 100_00 (inclusive).
Product.sortedByPriceLowToHigh({ from: 10_00, to: 100_00 })
See Run a range search

Projection

// Projects the `name`, `description`, and `price` fields.
Product.sortedByPriceLowToHigh() {
  name,
  description,
  price
}
{
  data: [
    {
      name: "single lime",
      description: "Conventional, 1 ct",
      price: 35
    },
    {
      name: "cilantro",
      description: "Organic, 1 bunch",
      price: 149
    },
    ...
  ]
}
Reference: Projection and field aliasing

Paginate results

Fauna automatically paginates result Sets with 16 or more elements.

When a query returns paginated results, Fauna materializes a subset of the Set with a after pagination cursor:

// Uses the `Product` collection's `sortedByPriceLowToHigh()` index to
// return all `Product` collection documents.
// The collection contains more than 16 documents.
Product.sortedByPriceLowToHigh()
{
  // The result Set contains 16 elements.
  data: [
    {
      id: "404851181399048225",
      coll: Product,
      ts: Time("2099-07-30T15:57:03.730Z"),
      name: "single lime",
      description: "Conventional, 1 ct",
      price: 35
    },
    {
      id: "404851181404291105",
      coll: Product,
      ts: Time("2099-07-30T15:57:03.730Z"),
      name: "cilantro",
      description: "Organic, 1 bunch",
      price: 149
    }
  ],
 // Use the `after` cursor to get the next page of results.
  after: "hdW..."
}

To get the next page of results, pass the after cursor to Set.paginate():

Set.paginate("hdW...")

The Fauna client drivers also include methods for automatically iterating through pages. See:

Document relationships

Create a document relationship

To create a document relationship, include the document as a field value:

// Gets a `Category` collection document.
let produce = Category.byName("produce").first()

// Creates a `Product` collection document.
Product.create({
  name: "lemons",
  description: "Organic, 16 ct",
  price: 2_49,
  stock: 200,
  // Adds the previous `Category` collection document as
  // a `category` field value.
  category: produce
})
See Create a document relationship

Traverse document relationships

You can project a field that contains a document to dynamically traverse the document relationship:

let produce = Category.byName("produce").first()

// Projects the `name`, `description`, `price`,
// and `category` fields.
Product.byCategory(produce) {
  name,
  description,
  price,
  category
}
{
  data: [
    {
      name: "avocados",
      description: "Conventional Hass, 4ct bag",
      price: 399,
      // Traverses the `Category` collection document in
      // the `category` field.
      category: {
        id: "<CATEGORY_DOCUMENT_ID>",
        coll: Category,
        ts: Time("2099-07-29T21:18:48.680Z"),
        products: "hdW...",
        name: "produce",
        description: "Fresh Produce"
      }
    },
    {
      name: "single lime",
      description: "Conventional, 1 ct",
      price: 35,
      category: {
        id: "<CATEGORY_DOCUMENT_ID>",
        coll: Category,
        ts: Time("2099-07-29T21:18:48.680Z"),
        products: "hdW...",
        name: "produce",
        description: "Fresh Produce"
      }
    },
    ...
  ]
}
See Traverse document relationships with projection

Event Streaming

Track changes to a collection

// Track changes to `Product` collection documents.
Product.all().toStream()
Reference: Collection streams

Track changes to an index

// Track changes to the Set of documents
//  returned by the `byCategory()` index call.
let produce = Category.byName("produce").first()
Product.byCategory(produce).toStream()
Reference: Index streams

Track changes to a single document

// Track changes on a single `Product` collection document.
let product = Product.byId("<DOCUMENT_ID>")!
Set.single(product).toStream()
Reference: Document streams

Track changes on specific fields

// Only track changes to the `category` field
// of the Set of documents returned by `Product.all()`.
let produce = Category.byName("produce").first()
Product.all().changesOn(.category, .name)
Reference: Event Feeds and Event Streaming reference

Child databases

Create a child database

// Creates the `childDB` child database.
Database.create({
  name: "childDB",
  // Enables typechecking for the database.
  typechecked: true
})
Reference: Database.create()

Get a child database

// Gets the `childDB` child database.
Database.byName("childDB")
Reference: Database.byName()

Update a child database

// Updates the `childDB` child database's
// `typechecked` field.
Database.byName("childDB")?.update({typechecked: false})
Reference: database.update()

Delete a child database

// Deletes the `childDB` child database.
Database.byName("childDB")?.delete()
Reference: database.delete()

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!