Ordering
OrderByDSL- Multi-field ordering
- Ordering on nested fields
- Direction
- Ordering and
Instant - Where to next
select and selectAll accept an orderBy: List<OrderBy<T>> parameter. The
list order is the precedence — first entry sorts first, ties broken by the
second, and so on.
OrderBy DSL
merchants.selectAll(
orderBy = listOf(
OrderBy(Merchant::score, OrderDirection.DESC),
),
).first()
OrderBy has two reified factory functions:
OrderBy(property: KProperty1<T, V>, direction: OrderDirection? = null)— for top-level fields.OrderBy(builder: JsonPathBuilder<T>, direction: OrderDirection? = null)— for nested fields built with.then(...).
Both produce the same OrderBy<T> value, which the store turns into a
json_tree(...) join plus an ORDER BY ... clause.
Multi-field ordering
val ordered = merchants.selectAll(
orderBy = listOf(
OrderBy(Merchant::category, OrderDirection.ASC),
OrderBy(Merchant::score, OrderDirection.DESC),
),
).first()
This sorts by category ascending, then breaks ties by score descending —
the same semantics as SQL’s ORDER BY category ASC, score DESC. The list is
1:1 with the SQL clause, so put the most important sort key first.
This pattern is exercised verbatim in selectAll_orderBy_EntityValueThenName:
testObjectStorage.selectAll(
orderBy = listOf(
OrderBy(TestObject::value, direction = OrderDirection.ASC),
OrderBy(TestObject::name, direction = OrderDirection.ASC),
),
).first()
Ordering on nested fields
Pass any JsonPathBuilder you’d pass to a Where operator:
merchants.selectAll(
orderBy = listOf(
OrderBy(Merchant::location.then(Location::city)),
),
).first()
Reference: selectAll_orderBy_EntityChildAddedBy orders by
TestObject::child.then(TestObjectChild::createdAt) descending.
Direction
OrderDirection is a simple enum:
enum class OrderDirection(val value: String) {
ASC(value = "ASC"),
DESC(value = "DESC"),
}
The direction parameter is nullable — pass null (or omit it) and
Sqkon emits no direction keyword, leaving SQLite to apply its default of
ASC. The KDoc on the field calls this out explicitly:
internal val direction: OrderDirection? = null,
// Sqlite defaults to ASC when not specified
So these two are equivalent today:
OrderBy(Merchant::score) // SQLite default → ASC
OrderBy(Merchant::score, OrderDirection.ASC) // Explicit ASC
Be explicit when sort order matters for tests or for readers — the explicit form survives any future change to defaults.
Ordering and Instant
The same string-comparison rule from Querying
applies — ISO-8601 timestamps sort lexicographically the same as
chronologically, so ordering on createdAt “just works” without any
conversion:
merchants.selectAll(
orderBy = listOf(
OrderBy(Merchant::createdAt, OrderDirection.DESC),
),
).first()
Where to next
- Querying — combine ordering with filters.
- Paging — keyset paging requires a stable, total order.
- Performance — un-indexed
ORDER BYon large tables.