Using temporality

Overview

Fauna documents are immutable, which means that once a document is created it never changes. If you update a document Fauna creates a new version of the document reflecting the changes and the original is left intact. With Fauna’s temporality features, it is possible to see the change history of a document, retrieve older versions, and perform queries based on document timestamps.

The history_days attribute

The history_days field of the collection determines how long Fauna retains old versions of updated documents. history_days defaults to 0, but it is possible to specify a different value when you create a collection:

try
{
    Value result = await client.Query(
        CreateCollection(
            Obj(
                "name", "fruit",
                "history_days", 10
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "fruit", collection = RefV(id = "collections")),ts: LongV(1659484863560000),history_days: LongV(10),name: StringV(fruit))
result, err := client.Query(
	f.CreateCollection(
		f.Obj{
			"name":         "fruit",
			"history_days": 10,
		},
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[history_days:10 name:fruit ref:{fruit 0x1400011d830 0x1400011d830 <nil>} ts:1659543812120000]
client.query(
  q.CreateCollection({ name: 'fruit', history_days: 10 })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Collection("fruit"),
  ts: 1641495115580000,
  history_days: 10,
  name: "fruit"
}
result = client.query(
  q.create_collection({ "name": "fruit", "history_days": 10 })
)
print(result)
{'ref': Ref(id=fruit, collection=Ref(id=collections)), 'ts': 1659634757630000, 'history_days': 10, 'name': 'fruit'}
CreateCollection({ name: "fruit", history_days: 10 })
{
  ref: Collection("fruit"),
  ts: 1659482501550000,
  history_days: 10,
  name: 'fruit'
}
Query metrics:
  •    bytesIn:   67

  •   bytesOut:  142

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  804

  • writeBytes:  324

  •  queryTime: 11ms

  •    retries:    0

You can also update the history_days attribute of an existing collection:

try
{
    Value result = await client.Query(
        Update(
            Collection("fruit"),
            Obj(
                "history_days", 5
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "fruit", collection = RefV(id = "collections")),ts: LongV(1659484870670000),history_days: LongV(5),name: StringV(fruit))
result, err := client.Query(
	f.Update(f.Collection("fruit"), f.Obj{"history_days": 5}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[history_days:5 name:fruit ref:{fruit 0x1400011d950 0x1400011d950 <nil>} ts:1659543814030000]
client.query(
  q.Update(q.Collection('fruit'), { history_days: 5 })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Collection("fruit"),
  ts: 1641495115580000,
  history_days: 5,
  name: "fruit"
}
result = client.query(
  q.update(q.collection("fruit"), { "history_days": 5 })
)
print(result)
{'ref': Ref(id=fruit, collection=Ref(id=collections)), 'ts': 1659634759800000, 'history_days': 5, 'name': 'fruit'}
Update(Collection("fruit"), { history_days: 5 })
{
  ref: Collection("fruit"),
  ts: 1659482502120000,
  history_days: 5,
  name: 'fruit'
}
Query metrics:
  •    bytesIn:  72

  •   bytesOut: 141

  • computeOps:   1

  •    readOps:   0

  •   writeOps:   1

  •  readBytes: 128

  • writeBytes:  94

  •  queryTime: 7ms

  •    retries:   0

After the period of time specified by history_days expires, Fauna removes any version of a document which has aged out. The most recent version of a document is not affected.

Documents might also include a ttl (time-to-live) field. Documents that have aged out of the database due to a ttl expiration cannot be retrieved with temporal queries. See Create for more information about the ttl attribute.

Document removal is handled by a background task, so once a document "expires" due to the history_days setting on a collection or the ttl field on a document, it could be some time (hours or days) before the removal occurs. There is no guarantee that removal actually occurs.

Document history

You can see a document’s change history with the Events function. To illustrate this feature, let’s create a new document in the fruit collection:

try
{
    Value result = await client.Query(
        Create(
            Ref(Collection("fruit"), "1"),
            Obj(
                "data", Obj(
                    "type", "apple",
                    "colors", Arr("red", "green"),
                    "quantity", 15
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "1", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484867110000),data: ObjectV(type: StringV(apple),colors: Arr(StringV(red), StringV(green)),quantity: LongV(15)))
result, err := client.Query(
	f.Create(
		f.Ref(f.Collection("fruit"), "1"),
		f.Obj{
			"data": f.Obj{
				"type":     "apple",
				"colors":   f.Arr{"red", "green"},
				"quantity": 15,
			},
		},
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[data:map[colors:[red green] quantity:15 type:apple] ref:{1 0x1400011dbf0 0x1400011dbf0 <nil>} ts:1659543813060000]
client.query(
  q.Create(
    q.Ref(q.Collection('fruit'), '1'),
    {
      data: {
        type: 'apple',
        colors: ['red', 'green'],
        quantity: 15,
      },
    },
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
    ref: Ref(Collection("fruit"), "1"),
    ts: 1641328117890000,
    data: { type: "apple", colors: ["red", "green"], quantity: 15 }
}
result = client.query(
  q.create(
    q.ref(q.collection("fruit"), "1"),
    {
      "data": {
        "type": "apple",
        "colors": ["red", "green"],
        "quantity": 15
      }
    }
  )
)
print(result)
{'ref': Ref(id=1, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659634758720000, 'data': {'type': 'apple', 'colors': ['red', 'green'], 'quantity': 15}}
Create(
  Ref(Collection("fruit"), "1"),
  {
    data: {
      type: "apple",
      colors: ["red", "green"],
      quantity: 15
    }
  }
)
{
  ref: Ref(Collection("fruit"), "1"),
  ts: 1659482501840000,
  data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 15 }
}
Query metrics:
  •    bytesIn:  146

  •   bytesOut:  205

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:   14

  • writeBytes:  224

  •  queryTime: 13ms

  •    retries:    0

Now let’s update the document:

try
{
    Value result = await client.Query(
        Update(
            Ref(Collection("fruit"), "1"),
            Obj(
                "data", Obj(
                    "quantity", 50
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "1", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484874200000),data: ObjectV(type: StringV(apple),colors: Arr(StringV(red), StringV(green)),quantity: LongV(50)))
result, err := client.Query(
	f.Update(
		f.Ref(f.Collection("fruit"), "1"),
		f.Obj{
			"data": f.Obj{
				"quantity": 50,
			},
		},
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[data:map[colors:[red green] quantity:50 type:apple] ref:{1 0x1400020a300 0x1400020a300 <nil>} ts:1659543814960000]
client.query(
  q.Update(
    q.Ref(q.Collection('fruit'), '1'),
    {
      data: {
        quantity: 50,
      },
    },
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
    ref: Ref(Collection("fruit"), "1"),
    ts: 1641328117890000,
    data: { type: "apple", colors: ["red", "green"], quantity: 50 }
}
result = client.query(
  q.update(
    q.ref(q.collection("fruit"), "1"),
    {
      "data": {
        "quantity": 50
      }
    }
  )
)
print(result)
{'ref': Ref(id=1, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659634760860000, 'data': {'type': 'apple', 'colors': ['red', 'green'], 'quantity': 50}}
Update(
  Ref(Collection("fruit"), "1"),
  {
    data: {
      quantity: 50
    }
  }
)
{
  ref: Ref(Collection("fruit"), "1"),
  ts: 1659482502400000,
  data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 50 }
}
Query metrics:
  •    bytesIn:  106

  •   bytesOut:  205

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:   88

  • writeBytes:  117

  •  queryTime: 13ms

  •    retries:    0

Now you can use Events to see the change history. The following example uses Events with the Paginate function to list all the document’s change events.

try
{
    Value result = await client.Query(
        Paginate(Events(Ref(Collection("fruit"), "1")))
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(data: Arr(ObjectV(ts: LongV(1659484867110000),action: StringV(create),document: RefV(id = "1", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),data: ObjectV(type: StringV(apple),colors: Arr(StringV(red), StringV(green)),quantity: LongV(15))), ObjectV(ts: LongV(1659484874200000),action: StringV(update),document: RefV(id = "1", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),data: ObjectV(quantity: LongV(50)))))
result, err := client.Query(
	f.Paginate(f.Events(f.Ref(f.Collection("fruit"), "1"))))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[data:[map[action:create data:map[colors:[red green] quantity:15 type:apple] document:{1 0x1400011db00 0x1400011db00 <nil>} ts:1659543813060000] map[action:update data:map[quantity:50] document:{1 0x1400011dd10 0x1400011dd10 <nil>} ts:1659543814960000]]]
client.query(
  q.Paginate(q.Events(q.Ref(q.Collection('fruit'), '1')))
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  data: [
    {
      ts: 1644607325240000,
      action: 'create',
      document: Ref(Collection("fruit"), "1"),
      data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 15 }
    },
    {
      ts: 1644607325510000,
      action: 'update',
      document: Ref(Collection("fruit"), "1"),
      data: { quantity: 50 }
    }
  ]
}
result = client.query(
  q.paginate(q.events(q.ref(q.collection("fruit"), "1")))
)
print(result)
{'data': [{'ts': 1659634758720000, 'action': 'create', 'document': Ref(id=1, collection=Ref(id=fruit, collection=Ref(id=collections))), 'data': {'type': 'apple', 'colors': ['red', 'green'], 'quantity': 15}}, {'ts': 1659634760860000, 'action': 'update', 'document': Ref(id=1, collection=Ref(id=fruit, collection=Ref(id=collections))), 'data': {'quantity': 50}}]}
Paginate(Events(Ref(Collection("fruit"), "1")))
{
  data: [
    {
      ts: 1659482501840000,
      action: 'create',
      document: Ref(Collection("fruit"), "1"),
      data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 15 }
    },
    {
      ts: 1659482502400000,
      action: 'update',
      document: Ref(Collection("fruit"), "1"),
      data: { quantity: 50 }
    }
  ]
}
Query metrics:
  •    bytesIn:  63

  •   bytesOut: 415

  • computeOps:   1

  •    readOps:   1

  •   writeOps:   0

  •  readBytes: 191

  • writeBytes:   0

  •  queryTime: 1ms

  •    retries:   0

You can retrieve a specific version of a document by including its timestamp with the Get function:

try
{
    Value result = await client.Query(
        Get(
            Ref(Collection("fruit"), "1"),
            ToInteger("1659484867110000")
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "1", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484867110000),data: ObjectV(type: StringV(apple),colors: Arr(StringV(red), StringV(green)),quantity: LongV(15)))
result, err := client.Query(
    f.Get(
        f.Ref(f.Collection("fruit"), "1"),
        f.TS(f.ToInteger("1659543813060000")),
    ))

if err != nil {
    fmt.Fprintln(os.Stderr, err)
} else {
    fmt.Println(result)
}
map[data:map[colors:[red green] quantity:15 type:apple] ref:{1 0x1400007fb30 0x1400007fb30 <nil>} ts:1659630709950000]
client.query(
  q.Get(q.Ref(q.Collection('fruit'), '1'), q.ToInteger('1644607325240000'))
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Ref(Collection("fruit"), "1"),
  ts: 1641838482800000,
  data: {
    type: "apple",
    colors: ["red", "green"],
    quantity: 15
  }
}
result = client.query(
  q.get(q.ref(q.collection("fruit"), "1"), q.to_integer("1659634758720000"))
)
print(result)
{'ref': Ref(id=1, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659634930250000, 'data': {'type': 'apple', 'colors': ['red', 'green'], 'quantity': 15}}
Get(Ref(Collection("fruit"), "1"), ToInteger("1659482501840000"))
{
  ref: Ref(Collection("fruit"), "1"),
  ts: 1659482760050000,
  data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 15 }
}
Query metrics:
  •    bytesIn:  86

  •   bytesOut: 205

  • computeOps:   1

  •    readOps:   1

  •   writeOps:   0

  •  readBytes:  88

  • writeBytes:   0

  •  queryTime: 1ms

  •    retries:   0

Replace the string <$TIMESTAMP> with the value for ts from the document creation result.

Timestamp queries

You can use the At function to retrieve data from a specified point in time. To see this feature in action, let’s add a few more documents to our collection.

try
{
    Value result = await client.Query(
        Map(
            Arr(
                Arr("2", "mango", Arr("green", "yellow"), 20),
                Arr("3", "melon", Arr("green"), 100),
                Arr("4", "pear", Arr("yellow", "brown"), 60)
            ),
            Lambda(
                Arr("id", "type", "colors", "quantity"),
                Create(
                    Ref(Collection("fruit"), Var("id")),
                    Obj(
                        "data", Obj(
                            "type", Var("type"),
                            "colors", Var("colors"),
                            "quantity", Var("quantity")
                        )
                    )
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
Arr(ObjectV(ref: RefV(id = "2", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484884950000),data: ObjectV(type: StringV(mango),colors: Arr(StringV(green), StringV(yellow)),quantity: LongV(20))), ObjectV(ref: RefV(id = "3", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484884950000),data: ObjectV(type: StringV(melon),colors: Arr(StringV(green)),quantity: LongV(100))), ObjectV(ref: RefV(id = "4", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484884950000),data: ObjectV(type: StringV(pear),colors: Arr(StringV(yellow), StringV(brown)),quantity: LongV(60))))
result, err := client.Query(
    f.Map(
        f.Arr{
            f.Arr{"2", "mango", f.Arr{"green", "yellow"}, 20},
            f.Arr{"3", "melon", f.Arr{"green"}, 100},
            f.Arr{"4", "pear", f.Arr{"yellow", "brown"}, 60},
        },
        f.Lambda(
            f.Arr{"id", "type", "colors", "quantity"},
            f.Create(
                f.Ref(f.Collection("fruit"), f.Var("id")),
                f.Obj{
                    "data": f.Obj{
                        "type": f.Var("type"),
                        "colors": f.Var("colors"),
                        "quantity": f.Var("quantity"),
                    },
                },
            ),
        ),
    ))

if err != nil {
    fmt.Fprintln(os.Stderr, err)
} else {
    fmt.Println(result)
}
[map[data:map[colors:[green yellow] quantity:20 type:mango] ref:{2 0x1400011def0 0x1400011def0 <nil>} ts:1659630714510000] map[data:map[colors:[green] quantity:100 type:melon] ref:{3 0x140001a0120 0x140001a0120 <nil>} ts:1659630714510000] map[data:map[colors:[yellow brown] quantity:60 type:pear] ref:{4 0x140001a0330 0x140001a0330 <nil>} ts:1659630714510000]]
client.query(
  q.Map(
    [
      ['2', 'mango', ['green', 'yellow'], 20],
      ['3', 'melon', ['green'], 100],
      ['4', 'pear', ['yellow', 'brown'], 60],
    ],
    q.Lambda(
      ['id', 'type', 'colors', 'quantity'],
      q.Create(
        q.Ref(q.Collection('fruit'), q.Var('id')),
        {
          data: {
            type: q.Var('type'),
            colors: q.Var('colors'),
            quantity: q.Var('quantity'),
          },
        }
      )
    )
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
[
  {
    ref: Ref(Collection("fruit"), "2"),
    ts: 1659480507940000,
    data: { type: 'mango', colors: [ 'green', 'yellow' ], quantity: 20 }
  },
  {
    ref: Ref(Collection("fruit"), "3"),
    ts: 1659480507940000,
    data: { type: 'melon', colors: [ 'green' ], quantity: 100 }
  },
  {
    ref: Ref(Collection("fruit"), "4"),
    ts: 1659480507940000,
    data: { type: 'pear', colors: [ 'yellow', 'brown' ], quantity: 60 }
  }
]
result = client.query(
  q.map_(
    q.lambda_(
      ["id", "type", "colors", "quantity"],
      q.create(
        q.ref(q.collection("fruit"), q.var("id")),
        {
          "data": {
            "type": q.var("type"),
            "colors": q.var("colors"),
            "quantity": q.var("quantity")
          }
        }
      )
    ),
    [
      ["2", "mango", ["green", "yellow"], 20],
      ['3', 'melon', ['green'], 100],
      ['4', 'pear', ['yellow', 'brown'], 60]
    ]
  )
)
print(result)
[{'ref': Ref(id=2, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659635026440000, 'data': {'type': 'mango', 'colors': ['green', 'yellow'], 'quantity': 20}}, {'ref': Ref(id=3, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659635026440000, 'data': {'type': 'melon', 'colors': ['green'], 'quantity': 100}}, {'ref': Ref(id=4, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659635026440000, 'data': {'type': 'pear', 'colors': ['yellow', 'brown'], 'quantity': 60}}]
Map(
  [
    ["2", "mango", ["green", "yellow"], 20],
    ["3", "melon", ["green"], 100],
    ["4", "pear", ["yellow", "brown"], 60],
  ],
  Lambda(
    ["id", "type", "colors", "quantity"],
    Create(
      Ref(Collection("fruit"), Var("id")),
      {
        data: {
          type: Var("type"),
          colors: Var("colors"),
          quantity: Var("quantity")
        }
      }
    )
  )
)
[
  {
    ref: Ref(Collection("fruit"), "2"),
    ts: 1659482827030000,
    data: { type: 'mango', colors: [ 'green', 'yellow' ], quantity: 20 }
  },
  {
    ref: Ref(Collection("fruit"), "3"),
    ts: 1659482827030000,
    data: { type: 'melon', colors: [ 'green' ], quantity: 100 }
  },
  {
    ref: Ref(Collection("fruit"), "4"),
    ts: 1659482827030000,
    data: { type: 'pear', colors: [ 'yellow', 'brown' ], quantity: 60 }
  }
]
Query metrics:
  •    bytesIn:  353

  •   bytesOut:  593

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    3

  •  readBytes:   42

  • writeBytes:  675

  •  queryTime: 11ms

  •    retries:    0

These new documents all have the same timestamp, because they were all added as part of the same transaction.

Now that you have several documents in our collection, create an index to retrieve them all at once:

try
{
    Value result = await client.Query(
        Map(
            Paginate(Documents(Collection("fruit"))),
            Lambda("ref", Get(Var("ref")))
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(data: Arr(ObjectV(ref: RefV(id = "1", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484874200000),data: ObjectV(type: StringV(apple),colors: Arr(StringV(red), StringV(green)),quantity: LongV(50))), ObjectV(ref: RefV(id = "2", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484884950000),data: ObjectV(type: StringV(mango),colors: Arr(StringV(green), StringV(yellow)),quantity: LongV(20))), ObjectV(ref: RefV(id = "3", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484884950000),data: ObjectV(type: StringV(melon),colors: Arr(StringV(green)),quantity: LongV(100))), ObjectV(ref: RefV(id = "4", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484884950000),data: ObjectV(type: StringV(pear),colors: Arr(StringV(yellow), StringV(brown)),quantity: LongV(60)))))
result, err := client.Query(
    f.Map(
        f.Paginate(f.Documents(f.Collection("fruit"))),
        f.Lambda("ref", f.Get(f.Var("ref"))),
    ))

if err != nil {
    fmt.Fprintln(os.Stderr, err)
} else {
    fmt.Println(result)
}
map[data:[map[data:map[colors:[red green] quantity:50 type:apple] ref:{1 0x1400007fd70 0x1400007fd70 <nil>} ts:1659630711770000] map[data:map[colors:[green yellow] quantity:20 type:mango] ref:{2 0x1400007ff80 0x1400007ff80 <nil>} ts:1659630714510000] map[data:map[colors:[green] quantity:100 type:melon] ref:{3 0x1400016a1b0 0x1400016a1b0 <nil>} ts:1659630714510000] map[data:map[colors:[yellow brown] quantity:60 type:pear] ref:{4 0x1400016a3c0 0x1400016a3c0 <nil>} ts:1659630714510000]]]
client.query(
  q.Map(
    q.Paginate(q.Documents(q.Collection('fruit'))),
    q.Lambda('ref', q.Get(q.Var('ref')))
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  data: [
    {
      ref: Ref(Collection("fruit"), "1"),
      ts: 1641578425510000,
      data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 50 }
    },
    {
      ref: Ref(Collection("fruit"), "2"),
      ts: 1641592193100000,
      data: { type: 'mango', colors: [ 'green', 'yellow' ], quantity: 20 }
    },
    {
      ref: Ref(Collection("fruit"), "3"),
      ts: 1641592193100000,
      data: { type: 'melon', colors: [ 'green' ], quantity: 100 }
    },
    {
      ref: Ref(Collection("fruit"), "4"),
      ts: 1641592193100000,
      data: { type: 'pear', colors: [ 'yellow', 'brown' ], quantity: 60 }
    },
  ]
}
result = client.query(
  q.map_(
    q.lambda_("ref", q.get(q.var("ref"))),
    q.paginate(q.documents(q.collection("fruit")))
  )
)
print(result)
{'data': [{'ref': Ref(id=1, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659635074010000, 'data': {'type': 'apple', 'colors': ['red', 'green'], 'quantity': 50}}, {'ref': Ref(id=2, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659635077080000, 'data': {'type': 'mango', 'colors': ['green', 'yellow'], 'quantity': 20}}, {'ref': Ref(id=3, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659635077080000, 'data': {'type': 'melon', 'colors': ['green'], 'quantity': 100}}, {'ref': Ref(id=4, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659635077080000, 'data': {'type': 'pear', 'colors': ['yellow', 'brown'], 'quantity': 60}}]}
Map(
  Paginate(Documents(Collection("fruit"))),
  Lambda("ref", Get(Var("ref")))
)
{
  data: [
    {
      ref: Ref(Collection("fruit"), "1"),
      ts: 1659482826190000,
      data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 50 }
    },
    {
      ref: Ref(Collection("fruit"), "2"),
      ts: 1659482827030000,
      data: { type: 'mango', colors: [ 'green', 'yellow' ], quantity: 20 }
    },
    {
      ref: Ref(Collection("fruit"), "3"),
      ts: 1659482827030000,
      data: { type: 'melon', colors: [ 'green' ], quantity: 100 }
    },
    {
      ref: Ref(Collection("fruit"), "4"),
      ts: 1659482827030000,
      data: { type: 'pear', colors: [ 'yellow', 'brown' ], quantity: 60 }
    }
  ]
}
Query metrics:
  •    bytesIn: 116

  •   bytesOut: 795

  • computeOps:   1

  •    readOps:  12

  •   writeOps:   0

  •  readBytes: 834

  • writeBytes:   0

  •  queryTime: 1ms

  •    retries:   0

To query a database at a particular point in time, you can use the At function. At adds a condition to a query which matches only documents which were created at or before a specified timestamp.

try
{
    Value result = await client.Query(
        At(
            ToInteger("1659484867110000"),
            Map(
                Paginate(Documents(Collection("fruit"))),
                Lambda("ref", Get(Var("ref")))
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(data: Arr(ObjectV(ref: RefV(id = "1", collection = RefV(id = "fruit", collection = RefV(id = "collections"))),ts: LongV(1659484867110000),data: ObjectV(type: StringV(apple),colors: Arr(StringV(red), StringV(green)),quantity: LongV(15)))))
result, err := client.Query(
    f.At(
        f.ToInteger("1659543813060000"),
        f.Map(
            f.Paginate(f.Documents(f.Collection("fruit"))),
            f.Lambda("ref", f.Get(f.Var("ref"))),
        ),
    ))

if err != nil {
    fmt.Fprintln(os.Stderr, err)
} else {
    fmt.Println(result)
}
map[data:[map[data:map[colors:[red green] quantity:15 type:apple] ref:{1 0x1400011df20 0x1400011df20 <nil>} ts:1659630845970000]]]
client.query(
  q.At(
    q.ToInteger('1644607325240000'),
    q.Map(
      q.Paginate(
        q.Documents(q.Collection('fruit'))
      ),
      q.Lambda('X', q.Get(q.Var('X')))
    )
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  data: [
    {
      ref: Ref(Collection("fruit"), "1"),
      ts: 1641578425510000,
      data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 15 }
    }
  ]
}
result = client.query(
  q.at(
    q.to_integer("1659634758720000"),
    q.map_(
      q.lambda_("ref", q.get(q.var("ref"))),
      q.paginate(q.documents(q.collection("fruit")))
    )
  )
)
print(result)
{'data': [{'ref': Ref(id=1, collection=Ref(id=fruit, collection=Ref(id=collections))), 'ts': 1659635021120000, 'data': {'type': 'apple', 'colors': ['red', 'green'], 'quantity': 15}}]}
At(
  ToInteger("1659482501840000"),
  Map(
    Paginate(Documents(Collection("fruit"))),
    Lambda("ref", Get(Var("ref")))
  )
)
{
  data: [
    {
      ref: Ref(Collection("fruit"), "1"),
      ts: 1659482825620000,
      data: { type: 'apple', colors: [ 'red', 'green' ], quantity: 15 }
    }
  ]
}
Query metrics:
  •    bytesIn: 160

  •   bytesOut: 216

  • computeOps:   1

  •    readOps:   9

  •   writeOps:   0

  •  readBytes: 464

  • writeBytes:   0

  •  queryTime: 1ms

  •    retries:   0

Replace the string <$TIMESTAMP> with the value for ts from the document creation result.

Any documents which have been removed from the database due to either history_days or ttl expiration cannot be retrieved with temporal queries.

Conclusion

Temporality offers developers a rich set of features for working with versioned documents and querying collections based on document timestamps. See the following resources for more information about temporality:

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!