Best practices

This section describes best practices that we recommend, covering a variety of Fauna use cases.

Counters and other frequently updated fields

  • Counter data is not well-suited for database storage. In Fauna, each document update stores a new version of the document. For frequently-updated fields, a lot of document history can be collected very quickly.

    If a frequently-updated counter is an essential part of your application, you can make some adjustments to improve performance:

    1. Set a collection’s history_days field to a small value (zero is best). Document history is still collected, but is removed far sooner than the default of 30 days. See Collections for details.

    2. Periodically run a query that calls Remove to explicitly remove document history.

  • Fauna processes transactions in 10 millisecond batches (100 times per second). Each batch can contain many transactions, but only one transaction per batch can modify a specific field, so throughput could be limited by contention on the field updates.

  • Indexes must evaluate document history when searching for matching entries. Where possible, do not include frequently-updated fields in an index’s terms or values definitions.

  • Where possible, set a collection’s history_days field to 0. Removal of older versions is handled by a background task, so versions accumulate until that task has a chance to execute. See Collections for details.

  • Consider setting a collection’s ttl_days to a small value. Once the ttl_days limit has been reached, "old" documents and all of their history are removed. See Collections for details.

  • Instead of attempting to implement a real-time counter solution, consider storing countable documents as a cache and periodically analyzing the cache’s contents to update a "report" document.

Defensive query strategies

  • Relationships in Fauna typically involve storing a document’s Reference in another document. For example, document A might store a reference to document B. When document B is deleted, the reference to B stored within A is not modified. So, queries that process document A and attempt to fetch the now-deleted document B would fail.

    You can test for the existence of a document before attempting to fetch it by using the If and Exists functions.

    For example, the following query fails because the related document does not exist:

    try
    {
        Value result = await client.Query(
            Let(
                "spellbook", Get(Ref(Collection("spellbooks"), "101")),
                "owner_ref", Select(Arr("data", "owner"), Var("spellbook")),
                "owner",     Get(Var("owner_ref"))
            ).In(
                Obj(
                    "spellbook", Select(Arr("data", "name"), Var("spellbook")),
                    "owner",     Select(Arr("data", "name"), Var("owner"))
                )
            )
        );
    
        Console.WriteLine(result);
    }
    catch (Exception e)
    {
        Console.WriteLine($"ERROR: {e.Message}");
    }
    ERROR: instance not found: Document not found.
    result, err := client.Query(
    	f.Let().Bind(
    		"spellbook", f.Get(f.Ref(f.Collection("spellbooks"), "101")),
    	).Bind(
    		"owner_ref", f.Select(f.Arr{"data", "owner"}, f.Var("spellbook")),
    	).Bind(
    		"owner",     f.Get(f.Var("owner_ref")),
    	).In(
    		f.Obj{
    			"spellbook": f.Select(f.Arr{"data", "name"}, f.Var("spellbook")),
    			"owner":     f.Select(f.Arr{"data", "name"}, f.Var("owner")),
    		},
    	))
    
    if err != nil {
    	fmt.Fprintln(os.Stderr, err)
    } else {
    	fmt.Println(result)
    }
    Response error 404. Errors: [let//owner](instance not found): Document not found., details: []
    try {
        System.out.println(
            client.query(
                Let(
                    "spellbook", Get(Ref(Collection("spellbooks"), "101")),
                    "owner_ref", Select(Path("data", "owner"), Var("spellbook")),
                    "owner",     Get(Var("owner_ref"))
                ).in(
                    Obj(
                        "spellbook", Select(Path("data", "name"), Var("spellbook")),
                        "owner",     Select(Path("data", "name"), Var("owner"))
                    )
                )
            ).get());
    } catch (Exception e) {
        System.out.println("ERROR " + e.getMessage());
    }
    ERROR com.faunadb.client.errors.NotFoundException: instance not found: Document not found.
    client.query(
      q.Let(
        {
          spellbook: q.Get(q.Ref(q.Collection('spellbooks'), '101')),
          owner_ref: q.Select(['data', 'owner'], q.Var('spellbook')),
          owner: q.Get(q.Var('owner_ref')),
        },
        {
          spellbook: q.Select(['data', 'name'], q.Var('spellbook')),
          owner: q.Select(['data', 'name'], q.Var('owner')),
        }
      )
    )
    .then((ret) => console.log(ret))
    .catch((err) => console.error('Error: %s', err))
    Error: [NotFound: instance not found] {
      description: 'Document not found.',
      requestResult: [RequestResult]
    }
    try:
      result = client.query(
        q.let(
          {
            "spellbook": q.get(q.ref(q.collection("spellbooks"), "101")),
            "owner_ref": q.select(["data", "owner"], q.var("spellbook")),
            "owner":     q.get(q.var("owner_ref")),
          },
          {
            "spellbook": q.select(["data", "name"], q.var("spellbook")),
            "owner":     q.select(["data", "name"], q.var("owner"))
          }
        )
      )
      print(result)
    except:
      print("Error: ", sys.exc_info()[0], sys.exc_info()[1])
    Error:  <class 'faunadb.errors.NotFound'> ErrorData(code='instance not found', description='Document not found.', position=['let', 2, 'owner'], failures=None)
    try {
      println(Await.result(
        client.query(
          Let(
            Seq(
              "spellbook" -> Get(Ref(Collection("spellbooks"), "101")),
              "owner_ref" -> Select(Arr("data", "owner"), Var("spellbook")),
              "owner"     -> Get(Var("owner_ref"))
            ),
            Obj(
              "spellbook" -> Select(Arr("data", "name"), Var("spellbook")),
              "owner"     -> Select(Arr("data", "name"), Var("owner"))
            )
          )
        ),
        5.seconds
      ))
    } catch {
      case unknown: Throwable => println("Error: " + unknown.getMessage())
    }
    Error: instance not found: Document not found.
    Not available in this language, yet.

    Whereas the following query succeeds because we now check for the existence of the related document, and since it does not exist we substitute with an object including the DISAPPEARED name:

    try
    {
        Value result = await client.Query(
            Let(
                "spellbook", Get(Ref(Collection("spellbooks"), "101")),
                "owner_ref", Select(Arr("data", "owner"), Var("spellbook")),
                "owner",     If(
                    Exists(Var("owner_ref")),
                    Get(Var("owner_ref")),
                    Obj("data", Obj("name", "DISAPPEARED"))
                )
            ).In(
                Obj(
                    "spellbook", Select(Arr("data", "name"), Var("spellbook")),
                    "owner",     Select(Arr("data", "name"), Var("owner"))
                )
            )
        );
    
        Console.WriteLine(result);
    }
    catch (Exception e)
    {
        Console.WriteLine($"ERROR: {e.Message}");
    }
    ObjectV(spellbook: StringV(Dallben's Spellbook),owner: StringV(DISAPPEARED))
    result, err := client.Query(
    	f.Let().Bind(
    		"spellbook", f.Get(f.Ref(f.Collection("spellbooks"), "101")),
    	).Bind(
    		"owner_ref", f.Select(f.Arr{"data", "owner"}, f.Var("spellbook")),
    	).Bind(
    		"owner",     f.If(
    			f.Exists(f.Var("owner_ref")),
    			f.Get(f.Var("owner_ref")),
    			f.Obj{"data": f.Obj{"name": "DISAPPEARED"}},
    		),
    	).In(
    		f.Obj{
    			"spellbook": f.Select(f.Arr{"data", "name"}, f.Var("spellbook")),
    			"owner":     f.Select(f.Arr{"data", "name"}, f.Var("owner")),
    		},
    	))
    
    if err != nil {
    	fmt.Fprintln(os.Stderr, err)
    } else {
    	fmt.Println(result)
    }
    map[owner:DISAPPEARED spellbook:Dallben's Spellbook]
    try {
        System.out.println(
            client.query(
                Let(
                    "spellbook", Get(Ref(Collection("spellbooks"), "101")),
                    "owner_ref", Select(Path("data", "owner"), Var("spellbook")),
                    "owner",     If(
                        Exists(Var("owner_ref")),
                        Get(Var("owner_ref")),
                        Obj("data", Obj("name", Value("DISAPPEARED")))
                    )
                ).in(
                    Obj(
                        "spellbook", Select(Path("data", "name"), Var("spellbook")),
                        "owner",     Select(Path("data", "name"), Var("owner"))
                    )
                )
            ).get());
    } catch (Exception e) {
        System.out.println("ERROR " + e.getMessage());
    }
    {spellbook: "Dallben's Spellbook", owner: "DISAPPEARED"}
    client.query(
      q.Let(
        {
          spellbook: q.Get(q.Ref(q.Collection('spellbooks'), '101')),
          owner_ref: q.Select(['data', 'owner'], q.Var('spellbook')),
          owner: q.If(
            q.Exists(q.Var('owner_ref')),
            q.Get(q.Var('owner_ref')),
            { data: { name: 'DISAPPEARED' } },
          ),
        },
        {
          spellbook: q.Select(['data', 'name'], q.Var('spellbook')),
          owner: q.Select(['data', 'name'], q.Var('owner')),
        }
      )
    )
    .then((ret) => console.log(ret))
    .catch((err) => console.error('Error: %s', err))
    { spellbook: "Dallben's Spellbook", owner: 'DISAPPEARED' }
    try:
      result = client.query(
        q.let(
          {
            "spellbook": q.get(q.ref(q.collection("spellbooks"), "101")),
            "owner_ref": q.select(["data", "owner"], q.var("spellbook")),
            "owner":     q.if_(
              q.exists(q.var("owner_ref")),
              q.get(q.var("owner_ref")),
              { "data": { "name": "DISAPPEATED" }}
            )
          },
          {
            "spellbook": q.select(["data", "name"], q.var("spellbook")),
            "owner":     q.select(["data", "name"], q.var("owner"))
          }
        )
      )
      print(result)
    except:
      print("Error: ", sys.exc_info()[0], sys.exc_info()[1])
    {'spellbook': "Dallben's Spellbook", 'owner': 'DISAPPEATED'}
    try {
      println(Await.result(
        client.query(
          Let(
            Seq(
              "spellbook" -> Get(Ref(Collection("spellbooks"), "101")),
              "owner_ref" -> Select(Arr("data", "owner"), Var("spellbook")),
              "owner"     -> If(
                Exists(Var("owner_ref")),
                Get(Var("owner_ref")),
                Obj("data" -> Obj("name" -> "DISAPPEARED"))
              )
            ),
            Obj(
              "spellbook" -> Select(Arr("data", "name"), Var("spellbook")),
              "owner"     -> Select(Arr("data", "name"), Var("owner"))
            )
          )
        ),
        5.seconds
      ))
    } catch {
      case unknown: Throwable => println("Error: " + unknown.getMessage())
    }
    {spellbook: "Dallben's Spellbook", owner: "DISAPPEARED"}
    Let(
      {
        spellbook: Get(Ref(Collection('spellbooks'), '101')),
        owner_ref: Select(['data', 'owner'], Var('spellbook')),
        owner: If(
          Exists(Var('owner_ref')),
          Get(Var('owner_ref')),
          { data: { name: 'DISAPPEARED' } },
        ),
      },
      {
        spellbook: Select(['data', 'name'], Var('spellbook')),
        owner: Select(['data', 'name'], Var('owner')),
      }
    )
    { spellbook: "Dallben's Spellbook", owner: 'DISAPPEARED' }
  • Fauna’s flexible data model allows documents with different structures to exist side-by-side in a single collection, so two documents in a single collection might have entirely different structures. When evaluating a document where the field structure is not consistent, attempting to access a non-existent field causes your query to fail.

    You can test for the existence of a field within a document by using the If and ContainsPath functions.

    For example, the following query fails because only some documents contain the extra field:

    try
    {
        Value result = await client.Query(
            Map(
                Paginate(Documents(Collection("Letters"))),
                Lambda(
                    "ref",
                    Let(
                        "doc", Get(Var("ref"))
                    ).In(
                        Obj(
                            "letter", Select(Arr("data", "letter"), Var("doc")),
                            "extra", Concat(
                                Arr(
                                    "Extra",
                                    ToStringExpr(
                                        Select(Arr("data", "extra"), Var("doc"))
                                    )
                                ),
                                "-"
                            )
                        )
                    )
                )
            )
        );
    
        Console.WriteLine(result);
    }
    catch (Exception e)
    {
        Console.WriteLine($"ERROR: {e.Message}");
    }
    ERROR: value not found: Value not found at path [data,extra].
    result, err := client.Query(
    	f.Map(
    		f.Paginate(f.Documents(f.Collection("Letters"))),
    		f.Lambda(
    			"ref",
    			f.Let().Bind(
    				"doc", f.Get(f.Var("ref")),
    			).In(
    				f.Obj{
    					"letter": f.Select(f.Arr{"data", "letter"}, f.Var("doc")),
    					"extra":  f.Concat(
    						f.Arr{
    							"Extra",
    							f.ToString(
    								f.Select(f.Arr{"data", "extra"}, f.Var("doc")),
    							),
    						},
    						f.Separator("-"),
    					),
    				},
    			),
    		),
    	))
    
    if err != nil {
    	fmt.Fprintln(os.Stderr, err)
    } else {
    	fmt.Println(result)
    }
    Response error 404. Errors: [map/expr/in/object/extra/concat//to_string/from](value not found): Value not found at path [data,extra]., details: []
    try {
        System.out.println(
            client.query(
                Map(
                    Paginate(Documents(Collection("Letters"))),
                    Lambda(
                        "ref",
                        Let(
                            "doc", Get(Var("ref"))
                        ).in(
                            Obj(
                                "letter", Select(Path("data", "letter"), Var("doc")),
                                "extra",  Concat(
                                    Arr(
                                        Value("Extra"),
                                        ToString(
                                            Select(
                                                Path("data", "extra"),
                                                Var("doc")
                                            )
                                        )
                                    ),
                                    Value("-")
                                )
                            )
                        )
                    )
                )
            ).get());
    } catch (Exception e) {
        System.out.println("ERROR " + e.getMessage());
    }
    ERROR com.faunadb.client.errors.NotFoundException: value not found: Value not found at path [data,extra].
    client.query(
      q.Map(
        q.Paginate(q.Documents(q.Collection('Letters'))),
        q.Lambda(
          'ref',
          q.Let(
            { doc: q.Get(q.Var('ref')) },
            {
              letter: q.Select(['data', 'letter'], q.Var('doc')),
              extra: q.Concat(
                [
                  'Extra',
                  q.ToString(
                    q.Select(['data', 'extra'], q.Var('doc'))
                  ),
                ],
                '-'
              ),
            }
          )
        )
      )
    )
    .then((ret) => console.log(ret))
    .catch((err) => console.error('Error: %s', err))
    Error: [NotFound: value not found] {
      description: 'Value not found at path [data,extra].',
      requestResult: [RequestResult]
    }
    try:
      result = client.query(
        q.map_(
          q.lambda_(
            "ref",
            q.let(
              { "doc": q.get(q.var("ref")) },
              {
                "letter": q.select(["data", "letter"], q.var("doc")),
                "extra":  q.concat(
                  [
                    "Extra",
                    q.to_string(
                      q.select(["data", "extra"], q.var("doc"))
                    ),
                  ],
                  "-"
                )
              }
            )
          ),
          q.paginate(q.documents(q.collection("Letters"))),
        )
      )
      print(result)
    except:
      print("Error: ", sys.exc_info()[0], sys.exc_info()[1])
    Error:  <class 'faunadb.errors.NotFound'> ErrorData(code='value not found', description='Value not found at path [data,extra].', position=['map', 'expr', 'in', 'object', 'extra', 'concat', 1, 'to_string', 'from'], failures=None)
    try {
      println(Await.result(
        client.query(
          Map(
            Paginate(Documents(Collection("Letters"))),
            Lambda(
              "ref",
              Let(
                Seq("doc" -> Get(Var("ref"))),
                Obj(
                  "letter" -> Select(Arr("data", "letter"), Var("doc")),
                  "extra"  -> Concat(
                    Arr(
                      "Extra",
                      ToString(
                        Select(Arr("data", "extra"), Var("doc"))
                      )
                    ),
                    "-"
                  )
                )
              )
            )
          )
        ),
        5.seconds
      ))
    } catch {
      case unknown: Throwable => println("Error: " + unknown.getMessage())
    }
    Error: value not found: Value not found at path [data,extra].
    Not available in this language, yet.

    Whereas the following query succeeds because we now check for the existence of the extra field, and since it does not exist we substitute with UNDEFINED:

    try
    {
        Value result = await client.Query(
            Map(
                Paginate(Documents(Collection("Letters"))),
                Lambda(
                    "ref",
                    Let(
                        "doc", Get(Var("ref"))
                    ).In(
                        Obj(
                            "letter", Select(Arr("data", "letter"), Var("doc")),
                            "extra", If(
                                ContainsPath(Arr("data", "extra"), Var("doc")),
                                Concat(
                                    Arr(
                                        "Extra",
                                        ToStringExpr(Select(Arr("data", "extra"), Var("doc")))
                                    ),
                                    "-"
                                ),
                                "UNDEFINED"
                            )
                        )
                    )
                )
            )
        );
    
        Console.WriteLine(result);
    }
    catch (Exception e)
    {
        Console.WriteLine($"ERROR: {e.Message}");
    }
    ObjectV(data: Arr(ObjectV(letter: StringV(A),extra: StringV(Extra-First)), ObjectV(letter: StringV(B),extra: StringV(Extra-second)), ObjectV(letter: StringV(C),extra: StringV(Extra-third)), ObjectV(letter: StringV(D),extra: StringV(Extra-4th)), ObjectV(letter: StringV(E),extra: StringV(Extra-fifth)), ObjectV(letter: StringV(F),extra: StringV(Extra-sixth)), ObjectV(letter: StringV(G),extra: StringV(Extra-seventh)), ObjectV(letter: StringV(H),extra: StringV(Extra-eighth)), ObjectV(letter: StringV(I),extra: StringV(Extra-9th)), ObjectV(letter: StringV(J),extra: StringV(Extra-tenth)), ObjectV(letter: StringV(K),extra: StringV(Extra-11)), ObjectV(letter: StringV(L),extra: StringV(Extra-)), ObjectV(letter: StringV(M),extra: StringV(UNDEFINED)), ObjectV(letter: StringV(N),extra: StringV(Extra-14th)), ObjectV(letter: StringV(O),extra: StringV(Extra-fifteenth)), ObjectV(letter: StringV(P),extra: StringV(Extra-16th)), ObjectV(letter: StringV(Q),extra: StringV(Extra-seventeenth)), ObjectV(letter: StringV(R),extra: StringV(Extra-18th)), ObjectV(letter: StringV(S),extra: StringV(Extra-19th)), ObjectV(letter: StringV(T),extra: StringV(Extra-20th)), ObjectV(letter: StringV(U),extra: StringV(Extra-21st)), ObjectV(letter: StringV(V),extra: StringV(Extra-22nd)), ObjectV(letter: StringV(W),extra: StringV(Extra-twenty-third)), ObjectV(letter: StringV(X),extra: StringV(Extra-24)), ObjectV(letter: StringV(Y),extra: StringV(Extra-24 + 1)), ObjectV(letter: StringV(Z),extra: StringV(UNDEFINED))))
    result, err := client.Query(
    	f.Map(
    		f.Paginate(f.Documents(f.Collection("Letters"))),
    		f.Lambda(
    			"ref",
    			f.Let().Bind(
    				"doc", f.Get(f.Var("ref")),
    			).In(
    				f.Obj{
    					"letter": f.Select(f.Arr{"data", "letter"}, f.Var("doc")),
    					"extra": f.If(
    						f.ContainsPath(f.Arr{"data", "extra"}, f.Var("doc")),
    						f.Concat(
    							f.Arr{
    								"Extra",
    								f.ToString(f.Select(f.Arr{"data", "extra"}, f.Var("doc"))),
    							},
    							f.Separator("-"),
    						),
    						"UNDEFINED",
    					),
    				},
    			),
    		),
    	))
    
    if err != nil {
    	fmt.Fprintln(os.Stderr, err)
    } else {
    	fmt.Println(result)
    }
    map[data:[map[extra:Extra-First letter:A] map[extra:Extra-second letter:B] map[extra:Extra-third letter:C] map[extra:Extra-4th letter:D] map[extra:Extra-fifth letter:E] map[extra:Extra-sixth letter:F] map[extra:Extra-seventh letter:G] map[extra:Extra-eighth letter:H] map[extra:Extra-9th letter:I] map[extra:Extra-tenth letter:J] map[extra:Extra-11 letter:K] map[extra:Extra- letter:L] map[extra:UNDEFINED letter:M] map[extra:Extra-14th letter:N] map[extra:Extra-fifteenth letter:O] map[extra:Extra-16th letter:P] map[extra:Extra-seventeenth letter:Q] map[extra:Extra-18th letter:R] map[extra:Extra-19th letter:S] map[extra:Extra-20th letter:T] map[extra:Extra-21st letter:U] map[extra:Extra-22nd letter:V] map[extra:Extra-twenty-third letter:W] map[extra:Extra-24 letter:X] map[extra:Extra-24 + 1 letter:Y] map[extra:UNDEFINED letter:Z]]]
    try {
        System.out.println(
            client.query(
                Map(
                    Paginate(Documents(Collection("Letters"))),
                    Lambda(
                        "ref",
                        Let(
                            "doc", Get(Var("ref"))
                        ).in(
                            Obj(
                                "letter", Select(Path("data", "letter"), Var("doc")),
                                "extra", If(
                                    ContainsPath(Path("data", "extra"), Var("doc")),
                                    Concat(
                                        Arr(
                                            Value("Extra"),
                                            ToString(
                                                Select(
                                                    Path("data", "extra"),
                                                    Var("doc")
                                                )
                                            )
                                        ),
                                        Value("-")
                                    ),
                                    Value("UNDEFINED")
                                )
                            )
                        )
                    )
                )
            ).get());
    } catch (Exception e) {
        System.out.println("ERROR " + e.getMessage());
    }
    {data: [{letter: "A", extra: "Extra-First"}, {letter: "B", extra: "Extra-second"}, {letter: "C", extra: "Extra-third"}, {letter: "D", extra: "Extra-4th"}, {letter: "E", extra: "Extra-fifth"}, {letter: "F", extra: "Extra-sixth"}, {letter: "G", extra: "Extra-seventh"}, {letter: "H", extra: "Extra-eighth"}, {letter: "I", extra: "Extra-9th"}, {letter: "J", extra: "Extra-tenth"}, {letter: "K", extra: "Extra-11"}, {letter: "L", extra: "Extra-"}, {letter: "M", extra: "UNDEFINED"}, {letter: "N", extra: "Extra-14th"}, {letter: "O", extra: "Extra-fifteenth"}, {letter: "P", extra: "Extra-16th"}, {letter: "Q", extra: "Extra-seventeenth"}, {letter: "R", extra: "Extra-18th"}, {letter: "S", extra: "Extra-19th"}, {letter: "T", extra: "Extra-20th"}, {letter: "U", extra: "Extra-21st"}, {letter: "V", extra: "Extra-22nd"}, {letter: "W", extra: "Extra-twenty-third"}, {letter: "X", extra: "Extra-24"}, {letter: "Y", extra: "Extra-24 + 1"}, {letter: "Z", extra: "UNDEFINED"}]}
    client.query(
      q.Map(
        q.Paginate(q.Documents(q.Collection('Letters'))),
        q.Lambda(
          'ref',
          q.Let(
            { doc: q.Get(q.Var('ref')) },
            {
              letter: q.Select(['data', 'letter'], q.Var('doc')),
              extra: q.If(
                q.ContainsPath(['data', 'extra'], q.Var('doc')),
                q.Concat(
                  [
                    'Extra',
                    q.ToString(
                      q.Select(['data', 'extra'], q.Var('doc'))
                    ),
                  ],
                  '-'
                ),
                'UNDEFINED',
              ),
            }
          )
        )
      )
    )
    .then((ret) => console.log(ret))
    .catch((err) => console.error('Error: %s', err))
    {
      data: [
        { letter: 'A', extra: 'Extra-First' },
        { letter: 'B', extra: 'Extra-second' },
        { letter: 'C', extra: 'Extra-third' },
        { letter: 'D', extra: 'Extra-4th' },
        { letter: 'E', extra: 'Extra-fifth' },
        { letter: 'F', extra: 'Extra-sixth' },
        { letter: 'G', extra: 'Extra-seventh' },
        { letter: 'H', extra: 'Extra-eighth' },
        { letter: 'I', extra: 'Extra-9th' },
        { letter: 'J', extra: 'Extra-tenth' },
        { letter: 'K', extra: 'Extra-11' },
        { letter: 'L', extra: 'Extra-' },
        { letter: 'M', extra: 'UNDEFINED' },
        { letter: 'N', extra: 'Extra-14th' },
        { letter: 'O', extra: 'Extra-fifteenth' },
        { letter: 'P', extra: 'Extra-16th' },
        { letter: 'Q', extra: 'Extra-seventeenth' },
        { letter: 'R', extra: 'Extra-18th' },
        { letter: 'S', extra: 'Extra-19th' },
        { letter: 'T', extra: 'Extra-20th' },
        { letter: 'U', extra: 'Extra-21st' },
        { letter: 'V', extra: 'Extra-22nd' },
        { letter: 'W', extra: 'Extra-twenty-third' },
        { letter: 'X', extra: 'Extra-24' },
        { letter: 'Y', extra: 'Extra-24 + 1' },
        { letter: 'Z', extra: 'UNDEFINED' }
      ]
    }
    try:
      result = client.query(
        q.map_(
          q.lambda_(
            "ref",
            q.let(
              { "doc": q.get(q.var("ref")) },
              {
                "letter": q.select(["data", "letter"], q.var("doc")),
                "extra": q.if_(
                  q.contains_path(["data", "extra"], q.var("doc")),
                  q.concat(
                    [
                      "Extra",
                      q.to_string(
                        q.select(["data", "extra"], q.var("doc"))
                      ),
                    ],
                    "-"
                  ),
                  "UNDEFINED"
                )
              }
            )
          ),
          q.paginate(q.documents(q.collection("Letters"))),
        )
      )
      print(result)
    except:
      print("Error: ", sys.exc_info()[0], sys.exc_info()[1])
    {'data': [{'letter': 'A', 'extra': 'Extra-First'}, {'letter': 'B', 'extra': 'Extra-second'}, {'letter': 'C', 'extra': 'Extra-third'}, {'letter': 'D', 'extra': 'Extra-4th'}, {'letter': 'E', 'extra': 'Extra-fifth'}, {'letter': 'F', 'extra': 'Extra-sixth'}, {'letter': 'G', 'extra': 'Extra-seventh'}, {'letter': 'H', 'extra': 'Extra-eighth'}, {'letter': 'I', 'extra': 'Extra-9th'}, {'letter': 'J', 'extra': 'Extra-tenth'}, {'letter': 'K', 'extra': 'Extra-11'}, {'letter': 'L', 'extra': 'Extra-'}, {'letter': 'M', 'extra': 'UNDEFINED'}, {'letter': 'N', 'extra': 'Extra-14th'}, {'letter': 'O', 'extra': 'Extra-fifteenth'}, {'letter': 'P', 'extra': 'Extra-16th'}, {'letter': 'Q', 'extra': 'Extra-seventeenth'}, {'letter': 'R', 'extra': 'Extra-18th'}, {'letter': 'S', 'extra': 'Extra-19th'}, {'letter': 'T', 'extra': 'Extra-20th'}, {'letter': 'U', 'extra': 'Extra-21st'}, {'letter': 'V', 'extra': 'Extra-22nd'}, {'letter': 'W', 'extra': 'Extra-twenty-third'}, {'letter': 'X', 'extra': 'Extra-24'}, {'letter': 'Y', 'extra': 'Extra-24 + 1'}, {'letter': 'Z', 'extra': 'UNDEFINED'}]}
    try {
      println(Await.result(
        client.query(
          Map(
            Paginate(Documents(Collection("Letters"))),
            Lambda(
              "ref",
              Let(
                Seq("doc" -> Get(Var("ref"))),
                Obj(
                  "letter" -> Select(Arr("data", "letter"), Var("doc")),
                  "extra"  -> If(
                    ContainsPath(Arr("data", "extra"), Var("doc")),
                    Concat(
                      Arr(
                        "Extra",
                        ToString(
                          Select(Arr("data", "extra"), Var("doc"))
                        )
                      ),
                      "-"
                    ),
                    "UNDEFINED"
                  )
                )
              )
            )
          )
        ),
        5.seconds
      ))
    } catch {
      case unknown: Throwable => println("Error: " + unknown.getMessage())
    }
    {data: [{letter: "A", extra: "Extra-First"}, {letter: "B", extra: "Extra-second"}, {letter: "C", extra: "Extra-third"}, {letter: "D", extra: "Extra-4th"}, {letter: "E", extra: "Extra-fifth"}, {letter: "F", extra: "Extra-sixth"}, {letter: "G", extra: "Extra-seventh"}, {letter: "H", extra: "Extra-eighth"}, {letter: "I", extra: "Extra-9th"}, {letter: "J", extra: "Extra-tenth"}, {letter: "K", extra: "Extra-11"}, {letter: "L", extra: "Extra-"}, {letter: "M", extra: "UNDEFINED"}, {letter: "N", extra: "Extra-14th"}, {letter: "O", extra: "Extra-fifteenth"}, {letter: "P", extra: "Extra-16th"}, {letter: "Q", extra: "Extra-seventeenth"}, {letter: "R", extra: "Extra-18th"}, {letter: "S", extra: "Extra-19th"}, {letter: "T", extra: "Extra-20th"}, {letter: "U", extra: "Extra-21st"}, {letter: "V", extra: "Extra-22nd"}, {letter: "W", extra: "Extra-twenty-third"}, {letter: "X", extra: "Extra-24"}, {letter: "Y", extra: "Extra-24 + 1"}, {letter: "Z", extra: "UNDEFINED"}]}
    Map(
      Paginate(Documents(Collection('Letters'))),
      Lambda(
        'ref',
        Let(
          { doc: Get(Var('ref')) },
          {
            letter: Select(['data', 'letter'], Var('doc')),
            extra: If(
              ContainsPath(['data', 'extra'], Var('doc')),
              Concat(
                [
                  'Extra',
                  ToString(
                    Select(['data', 'extra'], Var('doc'))
                  ),
                ],
                '-'
              ),
              'UNDEFINED',
            ),
          }
        )
      )
    )
    {
      data: [
        { letter: 'A', extra: 'Extra-First' },
        { letter: 'B', extra: 'Extra-second' },
        { letter: 'C', extra: 'Extra-third' },
        { letter: 'D', extra: 'Extra-4th' },
        { letter: 'E', extra: 'Extra-fifth' },
        { letter: 'F', extra: 'Extra-sixth' },
        { letter: 'G', extra: 'Extra-seventh' },
        { letter: 'H', extra: 'Extra-eighth' },
        { letter: 'I', extra: 'Extra-9th' },
        { letter: 'J', extra: 'Extra-tenth' },
        { letter: 'K', extra: 'Extra-11' },
        { letter: 'L', extra: 'Extra-' },
        { letter: 'M', extra: 'UNDEFINED' },
        { letter: 'N', extra: 'Extra-14th' },
        { letter: 'O', extra: 'Extra-fifteenth' },
        { letter: 'P', extra: 'Extra-16th' },
        { letter: 'Q', extra: 'Extra-seventeenth' },
        { letter: 'R', extra: 'Extra-18th' },
        { letter: 'S', extra: 'Extra-19th' },
        { letter: 'T', extra: 'Extra-20th' },
        { letter: 'U', extra: 'Extra-21st' },
        { letter: 'V', extra: 'Extra-22nd' },
        { letter: 'W', extra: 'Extra-twenty-third' },
        { letter: 'X', extra: 'Extra-24' },
        { letter: 'Y', extra: 'Extra-24 + 1' },
        { letter: 'Z', extra: 'UNDEFINED' }
      ]
    }

Errors

  • 503 response codes typically indicate that a timeout has been triggered, which could be due to contended queries, queries with high compute operations, etc.

    The best practice to handle 503 responses is to retry the query. The standard recommendation for retries is to apply a smaller query timeout initially so that your query can fail faster and then retry, and to apply exponential back-off to that timeout so that you are not creating a situation where your queries can never succeed but you keep executing queries.

Indexes

  • Instead of using "collection indexes" — indexes with no terms or values specified — consider using the Documents function. See Indexes for details.

Security

  • Periodically, revoke existing keys then generate and distribute new keys. See Keys for details.

  • Use Admin keys sparingly. Keys with custom roles that determine specific privileges are safer. See User-defined role for details.

Temporality

  • Temporality is the Fauna feature that allows you to perform point-in-time queries. A point-in-time query is executed using a specific timestamp in the past, using the At function. The results are based on the document versions available at that timestamp.

    When you are not using temporality on a collection’s documents, document versions increase your storage costs and reduce the performance of indexes covering those documents. Reduce the collection’s history_days setting to reduce the period of time document versions should exist.

Backup

  • Fauna stores data in geographically-distributed clusters of replicas, each involving multiple nodes. This means that there are always multiple copies of your stored documents.

    Fauna does not currently offer a backup and restore solution.

Was this article helpful?

We're sorry to hear that.
Tell us how we can improve!
Visit Fauna's Discourse forums or email docs@fauna.com

Thank you for your feedback!