Skip to content

Query Data

Maryk stores support a range of query actions. Requests can run locally or be serialized and sent to a remote store.

Maryk exposes CRUD-style actions and query variants:

  • Add, Change, Delete – mutate objects.
  • Get, Scan – fetch current values.
  • Get/Scan Updates – stream additions, changes, removals over time.
  • Get/Scan Changes – fetch versioned changes per object from history.

With AddRequest, objects can be added to a store. When applied, it will deliver an AddResponse with a status on each object added.

Example:

Kotlin:

val addRequest = Person.add(
Person.create {
firstName with "Jurriaan"
lastName with "Mous"
},
Person.create {
firstName with "John"
lastName with "Smith"
}
)

With ChangeRequest, objects can be modified in a store. When applied, it will deliver an ChangeResponse with a status on each change.

Refer to property operations to see how to apply changes to properties.

Example:

val person1Key // Key of person 1 to change
val person2Key // Key of person 2 to change
val changeRequest = Person.change(
person1Key.change(
Check(Person { firstName::ref } with "Jane"),
Change(Person { lastName::ref } with "Doe")
),
person2Key.change(
Change(Person { lastName::ref } with "Smith")
)
)

Simplify references with Kotlin run{}:

val person1Key // Key of person 1 to change
val person2Key // Key of person 2 to change
val changeRequest = Person.run {
change(
person1Key.change(
Check(ref { firstName } with "Jane"),
Change(ref { lastName } with "Doe")
),
person2Key.change(
Change(ref { lastName } with "Smith")
)
)
}

With DeleteRequest, objects can be deleted from a store. The objects can be deleted by passing a list of object keys. Objects can either be hard or soft deleted. With a hard delete, the data is permanently removed; with a soft delete (the default), it remains in the store but is not viewable unless specifically requested. When applied, it will deliver an DeleteResponse with a status on each deletion.

Example:

val person1Key // Key of person 1 to change
val person2Key // Key of person 2 to change
val deleteRequest = Person.delete(
person1Key,
person2Key,
hardDelete = true
)

With GetRequest, multiple specific objects can be queried by their key. To select a subset of values in the query, use select with a graph. It is possible to filter the results with filters or include soft-deleted results by passing filterSoftDeleted=false.

You can also view the objects at a certain version with toVersion if the store supports viewing past versions.

When applied, it will deliver an ValuesResponse with a list of ValuesWithMetaData containing the key, object, firstVersion, lastVersion, and isDeleted.

Example:

val person1Key // Key of person 1 to change
val person2Key // Key of person 2 to change
val getRequest = Person.get(
person1Key,
person2Key
)

With all parameters:

val person1Key // Key of person 1 to change
val person2Key // Key of person 2 to change
val getRequest = Person.run {
get(
person1Key,
person2Key,
where = And(
Equals(ref { firstName } with "Clark"),
Exists(ref { lastName })
),
toVersion = 2uL,
filterSoftDeleted = false
)
}

With ScanRequest, multiple objects can be queried by passing a startKey to scan from and filters on key parts to end it. To select a subset of values in the query, use select with a graph. It is possible to filter the results with filters, order, or limit the results (default= 100). You can also include soft-deleted results by passing filterSoftDeleted=false.

Additionally, you can view the objects at a certain version with toVersion if the store supports historical views.

When applied, it will deliver an ValuesResponse with a list of ValuesWithMetaData containing the key, object, firstVersion, lastVersion, and isDeleted.

val timedKey // Key starting at a certain time
val scanRequest = Logs.scan(
startKey = timedKey
)

With all parameters:

val timedKey // Key starting at a certain time
val scanRequest = Logs.run {
scan(
startKey = timedKey,
where = GreaterThanEquals(
ref { severity } with Severity.ERROR
),
order = ref { timeStamp }.descending(),
filterSoftDeleted = false,
limit = 50,
toVersion = 2uL
)
}

Use GetChangesRequest and ScanChangesRequest to fetch versioned changes as stored in the data store, grouped per object.

Response shape: a ChangesResponse containing a list of DataObjectVersionedChange entries with the object key, optional sortingKey (for index scans), and a list of VersionedChanges items. Each VersionedChanges contains a version and a list of field-level changes at that version (for example: ObjectCreate, Change, ObjectDelete).

Notes:

  • Default maxVersions is 1; requesting more than 1 version or using toVersion requires the store to enable keepAllVersions.
  • Aggregations are not supported for changes requests.
  • For ScanChanges, filters cannot use mutable (non-final or non-required) properties. Ordering is allowed only on the primary key order or on required, final properties.

Get changes with all parameters:

val person1Key // Key representing person 1.
val person2Key // Key representing person 2.
val getRequest = Person.run {
getChanges(
person1Key,
person2Key,
select = graph { listOf(firstName, lastName) },
where = And(
Equals(ref { firstName } with "Clark"),
Exists(ref { lastName })
),
fromVersion = 1000uL,
toVersion = 2000uL,
maxVersions = 100u,
filterSoftDeleted = false
)
}

Scan changes with all parameters:

val timedKey // Key that indicates the starting point for scanning.
val scanRequest = Logs.scanChanges(
startKey = timedKey,
select = graph { listOf(timeStamp, severity, message) },
where = GreaterThanEquals(Logs { severity::ref } with Severity.ERROR),
order = ref { timeStamp }.descending(),
limit = 50u,
includeStart = true,
fromVersion = 1000uL,
toVersion = 2000uL,
maxVersions = 100u,
filterSoftDeleted = false
)

You can request updates on objects ordered by version with GetUpdatesRequest or ScanUpdatesRequest. maxVersions (default=1) can be used to control how many versions are returned at maximum. To return more than one version, the DataStore needs to have keepAllVersions set to true.

When applied, it will deliver an UpdatesResponse with a list of IsUpdatesResponse which are either AdditionUpdate, ChangeUpdate, RemovalUpdate, and always starts with OrderedKeysUpdate, reflecting the initial ordering of keys.

This request type can also be used inside executeFlow to listen to updates as they happen in the data store.

Optionally, you can pass an orderedKeys parameter to a ScanUpdatesRequest. This allows you to receive any changes to that list as well. This way, you are assured to receive hard-deleted values or added values that had their values changed, ensuring they are within range of the passed order/limit.

Get updates with all parameters:

val person1Key // Key of person 1 to change
val person2Key // Key of person 2 to change
val getRequest = Person.run {
getUpdates(
person1Key,
person2Key,
select = graph {
listOf(
firstName,
lastName
)
},
where = And(
Equals(ref { firstName } with "Clark"),
Exists(ref { lastName })
),
order = ref { lastName }.ascending(),
toVersion = 2000uL,
filterSoftDeleted = false,
fromVersion = 1000uL,
maxVersions = 100
)
}

Scan updates with all parameters:

val timedKey // Key starting at a certain time
val scanRequest = Logs.scanUpdates(
startKey = timedKey,
select = graph {
listOf(
timeStamp,
severity,
message
)
},
where = GreaterThanEquals(
Logs { severity::ref } with Severity.ERROR
),
order = ref { timeStamp }.descending(),
filterSoftDeleted = false,
limit = 50,
fromVersion = 1000uL,
toVersion = 2000uL,
maxVersions = 100,
orderedKeys = listOf(log1Key, log2Key, log3Key)
)