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.

A Maryk query is a typed request object created from a RootDataModel.

val request = Person.get(key)
val response = store.execute(request)

The same request shape works against every store engine. That is why the CLI, App, Remote Store and embedded stores can share behavior.

Use:

  • Add, Change, Delete for writes.
  • Get when you know keys.
  • Scan when you need a range, filter, index order or page.
  • GetChanges / ScanChanges for object history.
  • GetUpdates / ScanUpdates for chronological sync.
  • executeFlow(...) for live views.

Use reference graphs with reads to fetch only the fields you need.

TaskRequest to start with
Insert a new objectModel.add(...)
Update known objectsModel.change(key.change(...))
Fetch by keyModel.get(key)
Browse a range or pageModel.scan(...)
Fetch only a few fieldsAdd select with a reference graph
Build a live viewexecuteFlow(Model.scanUpdates(...))
Inspect historyModel.getChanges(...) or Model.scanChanges(...)

Start with add, get, and scan. Add filters, ordering, reference graphs, and update streams when the access pattern needs them.

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