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())));