Skip to content

A query is a boolean expression over attributes, optionally followed by a sort. The SDK’s condition and sort functions write it for you. You write it by hand when you pass a string to query().

category = str('news') AND score > dec(4) AND EXISTS(summary)
SyntaxMatches when the attribute
name = valueEquals the value
name < value, <=, >, >=Compares as given
name BETWEEN low AND highIs between the two values, both included. Both need one type
name STARTSWITH str('prefix')Is a str that starts with the prefix
EXISTS(name)Is set, with any type
TYPEOF(name) = typeIs set with the given type, such as i32

A comparison also checks the type: score = i32(4) doesn’t match a score stored as a dec. It only sees indexed attributes, so it never matches an attribute stored without an index.

Every type can be compared and ranged. Numbers compare by value, and false comes before true. str, addr and key compare byte by byte, with no collation. Only str takes STARTSWITH.

The AND inside BETWEEN belongs to it. a BETWEEN 1 AND 5 AND b = 2 means (a BETWEEN 1 AND 5) AND b = 2.

Write every value with its type, except booleans:

TypeExample
booltrue, false
i32i32(-7)
u64u64(1700000000)
u256u256(1000000000000000000)
decdec(4.5)
strstr('it''s'). A quote inside is doubled
addraddr(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045)
keykey(0x9f2c…), 32 bytes

A bare number is an i32. bytes values can’t be compared, since bytes is never indexed.

Four entity fields can be filtered and sorted on, each with a fixed type:

FieldType
$keykey
$owneraddr
$creatoraddr
$expiresAtu64

AND and OR combine conditions. AND binds tighter than OR. Use parentheses to group:

(category = str('news') OR category = str('blog')) AND score >= dec(3)

ORDER BY at the end of a query sorts the entities it matches by one attribute. It names the attribute, the type to sort it as, and a direction:

category = str('news') ORDER BY score:dec DESC
  • ASC sorts smallest first and DESC largest first. Without either, the sort is ASC. The SDK always writes one
  • An attribute needs its type after the :. An entity field has a fixed type, so ORDER BY $expiresAt needs none, and a : can repeat it but not change it
  • A sort sees only the attribute stored with its type. An entity without the attribute, or with it stored as another type, sorts below every value: first with ASC, last with DESC
  • A query sorts by one attribute only. Entities that tie come in the node’s own order
  • Unlike a condition, a sort reads an attribute whether or not it’s indexed. $createdAt and $updatedAt can’t be sorted on. Values sort in the order they compare
  • ORDER BY applies to the whole condition, so a = i32(1) OR b = i32(2) ORDER BY a:i32 ASC needs no parentheses
  • Keywords are case-insensitive: and and AND are the same
  • The keywords AND, OR, NOT, TRUE, FALSE, BETWEEN, STARTSWITH, EXISTS, TYPEOF, ORDER, BY, ASC and DESC, and the type names, can’t be attribute names
  • -- starts a comment that runs to the end of the line
  • A named schema’s tag is the str attribute arkiv.schema. arkiv.schema = str('my-app.posts') matches every entity written through a schema named my-app.posts

The node rejects a query it can’t run with a QueryError. Its kind says why:

kindCause
parseThe query doesn’t parse
typeA condition uses the wrong type for its operator, such as STARTSWITH on an i32, or BETWEEN bounds of two types
literalA value is invalid, such as an i32 out of range
limitsThe query goes over a node limit, such as its length or nesting depth