Property References
To filter on a property you need a property reference. For example, to find all people named Smith you need a reference to the firstName property. Other databases might call this a column. Any DataModel can supply these references.
These operations can be defined in Kotlin or any of the serialization formats Maryk supports.
Example models
Section titled “Example models”This Person model has two top level fields (firstName, lastName) and an embedded Address:
object Person : RootDataModel<Person>() { val firstName by string(index = 1u) val lastName by string(index = 2u) val livingAddress by embed( index = 3u, dataModel = { Address } )}object Address : DataModel<Address>() { val street by string(index = 1u) val city by string(index = 2u)}Creating property references
Section titled “Creating property references”With compiled models Kotlin produces type strict property definitions. This means your IDE will help to validate and autocomplete them.
RootDataModel, DataModel, and ObjectDataModel inherit ref. Return the property you want; no
helper import is needed:
// Reference to firstName propertyPerson.ref { firstName }
// Reference to street through a PersonPerson.ref { livingAddress { street } }Each block retains its concrete model receiver, so the IDE can suggest Person properties in the outer block and Address properties in the embedded block. The selected property’s value type is preserved for typed query operands:
Equals(Person.ref { firstName } with "Jane")This syntax uses the existing model receivers; it does not hide their other public members from completion or require generated scope classes.
For deeper embedding, chain blocks and return the leaf property:
Model.ref { property { property { property { leaf } } } }Direct selection preserves the property’s concrete reference type, including
through embedded models, map navigation, referenced models, and multi-type
branches. It supports index definitions, reference-specific change operations,
and storage-byte operations without casts or an inner ::ref.
val index: IsIndexable = Person.ref { livingAddress { street } }val change = Model.ref { incMap }.change(addValues = listOf("new"))Property wrappers expose this relationship through IsReferenceCreator<R>.
Custom wrappers can implement that interface using their existing concrete
ref(parentRef) override. Generic helpers should retain this contract when
they need to preserve the concrete return type.
The original callable-reference selectors remain supported for compatibility.
The outer Person { ... } reference syntax is deprecated in favor of Person.ref.
Person.ref { firstName }Person.ref { livingAddress { street } }For generic code whose model is typed only as an IsDataModel interface, and for
framework models based directly on TypedObjectDataModel (including
DefinitionModel), the extension remains available through
import maryk.core.properties.references.dsl.ref.
Existing imports can remain; concrete models use their inherited member.
Embedded values and embedded objects support direct property selection. Map
at(key) and any { ... }, referenced-model blocks, and multi-type withType
and atType blocks also support returning the child property directly.
A typed child mutation requires its case to be active; selecting a case in a
reference does not activate it. Deleting an inactive child leaves the active case
unchanged. To switch cases, replace the multi-type parent with a typed value
first. For portable datastore writes to an inline scalar case, replace the
parent rather than assigning a direct atType case qualifier. Memory, RocksDB
and FoundationDB reject that qualifier shape because it cannot represent an
inline value in their cell layout.
Creating property references with String notation
Section titled “Creating property references with String notation”When defined in YAML or JSON, property references can use a simple string format without including the model name.
To refer to the firstName on Person you use firstName and for the street on Address within Person you
use livingAddress.street
Referring to values of maps
Section titled “Referring to values of maps”It is also possible to refer to a value inside a map to filter or order:
// Model contains map with Time as a key// Refer to the value at 12:23Model.ref { map.at(Time(12, 23)) }In string notation this becomes map.@12:23. References to the key itself use map.#12:23.
Wildcards for maps
Section titled “Wildcards for maps”Maps support wildcard references for both values and keys:
anyValue() supports scalar, embedded and multi-type value definitions. For maps
whose values are lists, sets or maps, use an explicit at(key) reference to the
collection. Collection-valued map wildcards are rejected with a request error.
// Any key in the mapModel.ref { map.anyKey() }
// Any value in the mapModel.ref { map.anyValue() }String notation:
map.~= any map keymap.*= any map value
Validation errors for proposed incrementing-map additions use map.^0 for the
first item in the request’s addition list. Nested errors retain child segments,
such as map.^0.@1 for an item in an added list. These references identify request
inputs; persisted entries use map.@key with the allocated key instead.
Referring to specific values in sets
Section titled “Referring to specific values in sets”Items in sets can be selected by value
// Model with a set property of Time// Refer to the value at Time(12, 23)Model.ref { set.item(Time(12, 23)) }In string notation this is set.#12:23.
Wildcards for sets
Section titled “Wildcards for sets”Sets support wildcard references to match any item value:
Model.ref { set.any() }String notation:
set.*= any set value
Referring to index of lists
Section titled “Referring to index of lists”Items in lists can also be selected by index
// Model with list property// Refer to the value at index 5Model.ref { list.at(5u) }In string notation this is list.@5.
list.@<index> and list.* references are usable for filtering and changes.
They are not valid for RootDataModel index definitions.
Stored reference identity
Section titled “Stored reference identity”Storage references resolve the complete path, including embedded leaves below map entries, list items and multi-type cases. Invalid trailing components are rejected.
Empty String map keys and set members retain their discriminated text form:
map.@, map.#, and set.#. Bare property names must remain nonempty.
