History and revision paging
Use the query overview to choose a request and a write boundary.
Get/Scan Changes
Section titled “Get/Scan Changes”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
maxVersionsis 1; requesting more than 1 version or usingtoVersionrequires the store to enablekeepAllVersions. - 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. These checks include enclosing properties and the underlying fields of named search indexes, including normalized or split fields. A final child of an optional or mutable parent is not immutable.
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.ref { severity } with Severity.ERROR), order = ref { timeStamp }.descending(), limit = 50u, includeStart = true, fromVersion = 1000uL, toVersion = 2000uL, maxVersions = 100u, filterSoftDeleted = false)Get/Scan Update History
Section titled “Get/Scan Update History”Use getUpdateHistory(key, ...) for one known record and scanUpdateHistory(...)
for historical updates across a model. Both return an UpdatesResponse with
newest-first AdditionUpdate, ChangeUpdate, or RemovalUpdate entries. A record
may occur more than once. These are finite history queries, not live flows, and do
not include an OrderedKeysUpdate header.
limit counts matching (key, version) entries, rather than records or property
versions. All selected changes at one record version are returned together.
Both requests require keepAllVersions and a ready keepUpdateHistoryIndex.
The get request filters the key before materializing record data. Current engines
use the existing model-wide history index, so this does not promise a separate
record-key index lookup or constant scan work.
import maryk.core.query.requests.getUpdateHistoryimport maryk.core.query.requests.scanUpdateHistory
val query = Person.getUpdateHistory( personKey, limit = 25u, filterSoftDeleted = false)val firstPage = store.execute(query)val secondPage = firstPage.nextCursor?.let { cursor -> store.execute(query.copy(cursor = cursor))}
val scanQuery = Person.scanUpdateHistory(limit = 25u)val firstScanPage = store.execute(scanQuery)val secondScanPage = firstScanPage.nextCursor?.let { cursor -> store.execute(scanQuery.copy(cursor = cursor))}fromVersion and toVersion are inclusive event-version bounds and must satisfy
fromVersion <= toVersion when an upper bound is supplied. Filters are
applied at each event’s version; select limits the returned fields. Events with
no selected changes are omitted, while creation and deletion events remain
meaningful. Use filterSoftDeleted = false to include soft-delete transitions
and revisions whose record is soft-deleted at that version.
Forward nextCursor unchanged with the same request. A cursor binds the model,
record key for get requests, selection, filters, and version bounds. The page
size may change. Continuation is exclusive by version and then ascending record
key, so records sharing a version are neither skipped nor repeated. The cursor
also retains the first page’s version ceiling, excluding later higher-version
writes. nextCursor == null means no more matching entries were found; lookahead
checks one additional matching entry before returning a cursor.
A cursor does not retain erased history or isolate later imports of older
versions. Hard deletion can remove earlier values permanently. Where a retained
hard-delete marker exists, an unfiltered query can return its RemovalUpdate;
filters requiring erased values cannot be evaluated in persistent stores.
Memory can evaluate hard-delete filters against its archived pre-delete values.
Creation entries contain selected values, and subsequent change entries contain
deltas. History paging does not return a complete snapshot for every entry; use
get(key, toVersion = entry.version, ...) for a retained point-in-time state.
