Code snippets

Paper Code snippets

TwiceDB client libraries provide a convenient API for common operations. Below are some snippets to illustrate their use.

Most TwiceDB client methods support both synchronous and asynchronous execution. In Go, async methods return a chan, and in Java, async methods return a Future<>.

Methods marked with async support asynchronous execution.

Setup

For these snippets, let’s use the following objects:

type MyObject struct {
  api.IndexableF
  // Name is indexed. Methods will be
  // generated to do fast lookups by Name.
  Name *string `twicedb:"index"`
  // Code is uniquely indexed. Methods
  // will be generated to do fast lookups
  // by Code, and TwiceDB will enforce
  // that only 0 or 1 instances of
  // MyObject will have the same Code
  // at any given (tt, vt).
  Code *string `twicedb:"unique"`
  // ANumber is projected. Methods
  // will be generated to do aggregations
  // on ANumber.
  ANumber *int32 `twicedb:"project"`
  // OtherObject *MyObject
}

type MyEvent struct {
  api.EventF
  AString string
}
@TwiceDBObject
public class MyObject
  extends Indexable<MyObject>
  implements MyObjectRefSupport {
  // Name is indexed. Methods will be
  // generated to do fast lookups by Name.
  @TwiceDBIndex public String name;
  // code is uniquely indexed. Methods
  // will be generated to do fast lookups
  // by code, and TwiceDB will enforce
  // that only 0 or 1 instances of
  // MyObject will have the same code
  // at any given (tt, vt).
  @TwiceDBUnique public String code;
  // aNumber is projected. Methods
  // will be generated to do aggregations
  // on aNumber.
  @TwiceDBProject public Integer aNumber;
  @TwiceDBRef private final MyObject otherObject = null;

  public String getName() {
    return this.name;
  }
}

@TwiceDBEvent
public class MyEvent extends Event<MyEvent> {
  private final String aString;

  public String getAString() {
    return this.aString;
  }
}

Get server status

Get basic status information from either the reader or the writer to which the client is currently connected.

ss, _ := client.ServerStatus(
  client.Context(),
  cshared.ReaderServerType)
ServerStatus s = client
  .getServerStatus(
    ServerType.READER);

Entitlements

Data in TwiceDB is protected by read, write, and entitle entitlements. Learn more about entitlements.

Add Fqn entitlements

Add read and write entitlements.

_, err := client.Entitle(
  client.Context(),
  shared.EmailAddress("[email protected]"),
  shared.Fqn("my.class.fqn"),
  cshared.CanRead,
  cshared.CanWrite,
  cshared.CannotEntitle,
  shared.VtOf(time.Now()))
Tt tt = client.entitle(
  new EmailAddress("[email protected]"),
  new Fqn("my.class.fqn"),
  ReadEntitlement.CAN_READ,
  WriteEntitlement.CAN_WRITE,
  EntitleEntitlement.CANNOT_ENTITLE,
  Vt.of(LocalDate.now()));

Reads

TwiceDB supports a variety of query types to read data.

Read Event by EventId

async Find a single Event by its unique EventId.

foundEvent, _ := client.ReadEvent(
  client.Context(),
  shared.EventId("an event"),
  shared.Fqn("my.event.class"),
  client.Lsqt().Tt())
Event foundEvent =
  client.readEvent(
    new EventId("an event"),
    client.getLsqt().toTt());

Read Event for object

async Find the Event associated with the transaction in which an object was saved (if it exists).

// Suppose we have an object o.
ev, _ :=
  client.ReadEventForObject(
    client.Context(),
    o)
// Suppose we have an object o.
Event ev = client
  .readEventForObject(
    o);

Read object by ObjectId

async Find a single object by its unique ObjectId.

foundObject, _ := ReadMyObjectByObjectId(
  client,
  shared.NewTemporalCoordinates(
    client.Lsqt().Tt(),
    shared.VtOf(time.Now())),
  shared.ObjectId("findme"))
MyObject o =
  MyObjectFactory.getNewWithObjectId(
    "findme object",
    123,
    null,
    new ObjectId("findme"));
MyObject foundObject =
  IndexableI.readObject(
    client,
    o,
    new TemporalCoordinates(
      client.getLsqt().toTt(),
      Vt.of(LocalDate.now())));

Read objects by Event

async Use an Event to access all of the objects saved in a transaction.

// Suppose we have an Event ev.
ixblObjs, _ := client.ReadObjectsByEvent(
  client.Context(),
  ev)
// Suppose we have an Event ev.
List<Indexable> ixbls =
  client.readObjectsByEvent(
    ev);

Read objects by IndexedQuery

async Quickly find objects using indexed fields.

iquery := NewMyObjectIndexedQueryBuilder()
  .WhereNameEq("a name")
  .Build()
foundObjects, _ := (&myObject{})
  .ReadIQuery(
    client,
    iquery,
    shared.NewTemporalCoordinates(
      client.Lsqt().Tt(),
      shared.VtOf(time.Now())))
IndexedQuery iquery =
  new MyObjectIndexedQueryBuilder()
    .whereNameEqualTo("a name")
    .build();
List<MyObject> objs =
  IndexableI.readObjectsByIndexedQuery(
    client,
    iquery,
    new TemporalCoordinates(
      client.getLsqt().toTt(),
      Vt.of(LocalDate.now())));

Read single object by unique index

async Quickly find a single object using uniquely indexed fields.

foundObject, _ := NewMyObjectIndexedQueryBuilder()
  .ReadObjectByCode(
    "a code",
    client,
    shared.NewTemporalCoordinates(
      client.Lsqt().Tt(),
      shared.VtOf(time.Now())))
MyObject foundObject =
  new MyObjectIndexedQueryBuilder()
    .readObjectByCode(
      "a code",
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Read all objects by Fqn

Read all objects of the given Fqn.

it := newMyObjectIterator(
  client,
  shared.BatchSize(10),
  client.Lsqt().Tt())
for {
  hn, _ := it.HasNext()
  if !hn {
    break
  }
  o, _ := it.Next()
}
Iterator<MyObject> it =
  MyObjectIterator.getNew(
    client,
    client.getLsqt().toTt(),
    new BatchSize(10));
while (it.hasNext()) {
  MyObject o = it.next();
}

Read referenced objects (foreign keys)

async Load referenced objects at the (tt, vt) at which the parent object was loaded.

otherObject, _ := o.OtherObject(client)
MyObject otherObject = o.getOtherObject(client);

Timelines

TwiceDB supports timelines that allow you to fix the tt and to see how objects changed along the vt or to fix the vt and to see how objects changed along the tt.

Read Tt timeline

async Read all versions of an object at the given vt in tt order.

o := NewMyObjectWithObjectId(
  "some object",
  int32(8),
  nil,
  shared.ObjectId("findme"))
objs, _ := o.ReadObjectsAtVt(
  client,
  shared.NewTemporalCoordinates(
    shared.Tt(91),
    shared.Vt(95)))
MyObject o =
  MyObjectFactory.getNewWithObjectId(
    "findme object",
    8,
    null,
    new ObjectId("findme"));
List<MyIndexableObject> objs =
  IndexableI.readObjectsAtVt(
    client,
    o,
    new TemporalCoordinates(
      new Tt(91),
      new Vt(95));

Read Vt timeline

async Read all versions of an object at the given tt in vt order.

o := NewMyObjectWithObjectId(
  "some object",
  int32(8),
  nil,
  shared.ObjectId("findme"))
objs, _ := o.ReadObjectsAtTt(
  client,
  shared.Tt(91))
MyObject o =
  MyObjectFactory.getNewWithObjectId(
    "findme object",
    8,
    null,
    new ObjectId("findme"));
List<MyIndexableObject> objs =
  IndexableI.readObjectsAtTt(
    client,
    o,
    new Tt(91));

Subscribing to data changes

TwiceDB will notify subscribers of data changes in a publish-subscribe (“Pub/Sub”) manner.

Subscribe to IndexedQuery

Subscribe to an IndexedQuery to receive a notification each time an object matching the query is written.

iq := NewMyObjectIndexedQueryBuilder()
  .WhereNameEq("a name")
  .WhereANumberGt(int32(9))
  .Build()
subscriptionId, ch, _ :=
  client.SubscribeToIndexedQuery(ctx, iq)
for {
  subscribeResponse := <-ch
  fmt.Printf(
    "Update received for SubscriptionId %d",
    subscribeResponse.SubscriptionId)
}
IndexedQuery<MyIndexableObject> iq =
  MyObjectIndexedQueryBuilder
    .getNew()
    .whereNameEqual("a name")
    .whereANumberGreaterThan(9)
    .build();
client.subscribeToIndexedQuery(
  iq,
  new Function<SubscribeResponse, Boolean>() {
    @Override
    public Boolean apply(
      SubscribeResponse subscribeResponse
    ) {
      System.out.println(
        "Update received for SubscriptionId " +
          subscribeResponse.subscriptionId());
      return true;
    }
  });

Subscribe to ObjectId

Subscribe to an ObjectId to receive a notification each time it is written.

// Suppose we have an ObjectId oId.
subscriptionId, ch, _ :=
  client.SubscribeToObjectId(
    ctx,
    oId)
for {
  subscribeResponse := <-ch
  fmt.Printf(
    "Update received for SubscriptionId %d",
    subscribeResponse.SubscriptionId)
}
// Suppose we have an ObjectId oId.
client.subscribeToObjectId(
  oId,
  new Function<SubscribeResponse, Boolean>() {
    @Override
    public Boolean apply(
      SubscribeResponse subscribeResponse
    ) {
      System.out.println(
        "Update received for SubscriptionId " +
          subscribeResponse.subscriptionId());
      return true;
    }
  });

Writes

TwiceDB supports a variety of query types to write, or save, data.

Delete single object

async Delete a single object.

// Suppose we have an object o.
wo, _ := o.DeleteObject(
  client.Context(),
  shared.VtOf(time.Now()))
// Suppose we have an object o.
WrittenObject wo =
  IndexableI.deleteObject(
    client,
    o,
    Vt.of(LocalDate.now()));

Reindex single object

async Reindex a single object after modifying its indexed and/or projected fields.

// Suppose we have an object o.
wo, _ := o.ReindexObject(
  client)
// Suppose we have an object o.
WrittenObject wo =
  client.ReindexObject(o);

Reinstate single object

async Reinstate a single deleted object exactly as it was before it was deleted.

// Suppose we have an object o.
wo, _ := o.ReinstateObject(
  client,
  shared.VtOf(time.Now()))
// Suppose we have an object o.
WrittenObject wo =
  IndexableI.reinstateObject(
    client,
    o,
    Vt.of(LocalDate.now()));

Write single object

async Write a single object with a user-assigned vt and a database-assigned tt.

o := NewMyObject(
  "object to write",
  int32(246),
  nil)
w, _ := o.PutObject(
  client,
  shared.VtOf(time.Now()))
MyObject o =
  MyObjectFactory.getNew(
    "object to write",
    246,
    null);
WrittenObject w =
  IndexableI.putObject(
    client,
    o,
    Vt.of(LocalDate.now()));

Write multiple objects (transactions)

async Use a transaction to save multiple objects together at the same tt.

o := NewMyObject(
  "object to write in a transaction",
  int32(13),
  other)
other := NewMyObject(
  "other object to write in a transaction",
  int32(11),
  nil)
vt := shared.VtOf(time.Now())
tx := txn.NewTxn()
tx.PutObject(o, vt)
tx.PutObject(other, vt)
w, _ := client.CommitTxn(tx)
MyObject o =
  MyObjectFactory.getNew(
    "object to write in a transaction",
    13,
    other);
MyObject other =
  MyObjectFactory.getNew(
    "other object to write in a transaction",
    11,
    null);
Vt vt = Vt.of(LocalDate.now());
Txn tx = new Txn();
tx.putObject(o, vt);
tx.putObject(other, vt);
WrittenTransaction w = client.commitTxn(tx);

Write objects with an Event

async Attach an Event to a transaction to link the objects in the transaction and/or to add metadata.

o := NewMyObject(
  "object to bind to an Event",
  int32(-3),
  nil)
e := NewMyEvent(
  AString: "remember this",
)
tx := txn.NewEventTxn(e)
tx.PutObject(o, shared.VtOf(time.Now()))
w, _ := client.CommitTxn(tx)
MyObject o =
  MyObjectFactory.getNew(
    "object to bind to an Event",
    -3,
    null);
Txn tx = Txn.eventTxn(
  MyEventFactory.getNew(
    "remember this"));
tx.putObject(o, Vt.of(LocalDate.now()));
WrittenTransaction w = client.commitTxn(tx);

Aggregations

TwiceDB supports a variety of descriptive statistics for data.

Count objects

async Count objects matching an IndexedQuery.

count, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .Count(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .count(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Max of a field

async Max of a field of objects matching an IndexedQuery.

max, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .MaxANumber(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .maxANumber(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Mean of a field

async Mean of a field of objects matching an IndexedQuery.

mean, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .MeanANumber(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .meanANumber(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Median of a field

async Median of a field of objects matching an IndexedQuery.

median, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .MedianANumber(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .medianANumber(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Min of a field

async Min of a field of objects matching an IndexedQuery.

min, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .MinANumber(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .minANumber(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Percentile of a field

async Percentile of a field of objects matching an IndexedQuery.

percentile, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .PercentileANumber(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .percentileANumber(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

StdDev of a field

async StdDev of a field of objects matching an IndexedQuery.

stddev, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .StdDevANumber(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .stddevANumber(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Sum of a field

async Sum of a field of objects matching an IndexedQuery.

sum, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .SumANumber(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .sumANumber(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Variance of a field

async Variance of a field of objects matching an IndexedQuery.

v, aggregationExists, _ :=
  NewMyObjectIndexedQueryBuilder()
    .WhereNameEq("An important name")
    .VarANumber(
      client,
      shared.NewTemporalCoordinates(
        client.Lsqt().Tt(),
        shared.VtOf(time.Now())))
AggregationResponse r =
  MyObjectIndexedQueryBuilder.getNew()
    .whereNameEqual("An important name")
    .varANumber(
      client,
      new TemporalCoordinates(
        client.getLsqt().toTt(),
        Vt.of(LocalDate.now())));

Copyright © 2024 TwiceDB TwiceDB