Query inputs: AST vs String vs Mix Rewriting

Intro

The query layer of Mix is designed as a filter to projection pipeline: a series of filter and projection statements. The string form of the query uses the pipeline operator | to separate statements, while the AST form is built as an array of query operations.

Some examples:

String AST Mix
`<filter> <projection>` [<filter>, <projection> ]
`<filter1> ... <filtern>
`<filter> <projection> <filter>
`<filter> <projection> <aggregation>`

Actually executing queries, including rewriting queries based on db.filter expressions, is discussed on the top-level query page. In Mix, query AST can be generated implicitly via rewriting of db.filter and db.map expressions, explicitly created as plain data like ["comparison", "==", "name", "chris"], or created with the helper functions like query.eq("name", "chris").

Filter

Filter statements are boolean expressions that apply tests to the records in the database.

Operation Opcode String AST Mix ( db.filter(r -> ___ ))
== == key == val [ "comparison", "==", key, val ] r.key == val
!= != key != val [ "comparison", "!=", key, val ] r.key != val
> > key > val [ "comparison", ">", key, val ] r.key > val
>= >= key >= val [ "comparison", ">=", key, val ] r.key >= val
< < key < val [ "comparison", "<", key, val ] r.key < val
<= <= key <= val [ "comparison, "<=", key, val ] r.key <= val
Contains contains key contains val [ "comparison", "contains", key, val ] string.contains(val, r.key) or array.contains(val, r.key) or array.contains(.key, val) if val is an array
Like like key like val [ "comparison", "like", key, val ] string.hasPrefix(val, .key)
Exists exists exists key [ "exists", key ] r.key?
After after after val [ "after", val ] not supported

For example

[
  "and",
  [ "comparison", "==", "entity", "relationship" ],
  [ "comparison", "featuresOne", "to", [ "list", {ref1}, {ref2} ]  ]
]

In the string form, keys and string values do not need to be quoted in all cases, but the rules are not consistent across implementations and not guaranteed not to change, so it is best to keep everything quoted to avoid problems.

Additionally, there are array filtering operations defined that can identify membership of a key (note that either form of the opcode is allowed in the AST):

Operation Opcode String AST Mix
in - any value in the key is a member of the array in, featuresOne key in [...] ["comparison", "in", key, [...] ] array.featuresOne(r.key, [...])
all in - the values of the key include all members of the array all_in, featuresAll key all in [...] ["comparison", "all_in", key, [...] ] array.featuresAll(r.key, [...])
not in - the value of the key is not a member of the array not_in, notFeaturesOne key not in [...] ["comparison", "not_in", key, [...] ] not array.featuresOne(r.key, [...])
not all in - no values of the key are members of the array not_all_in, notFeaturesAll key not all in [...] ["comparison", "not_all_in", key, [...] not array.featuresAll(r.key, [...])

And, of course, the standard and, or, and not operators (also spelled &&, ||, and !, but only &&/||/not are allowed in Mix) -

Operation String AST Mix
and comp1 and comp2 and comp3 [ "and", comp1, comp2, comp3 ] db.filter(r -> expr1 && expr2)
or comp1 or comp2 or comp3 [ "or", comp1, comp2, comp3 ] db.filter(r -> expr1 \|\| expr2)
not not comp [ "not", comp ] db.filter(r -> not expr1)
no-op. (empty string) [ "nullFilter" ] db.filter(r -> true)

Key Lookups, Fields and Values

For the key in the comparison, and later to apply values into projections, the following field values are acceptable:

Key Type String AST
Bare String "field" "field"
Bare Token field [ "token", "field" ]
Member of object .field [ "objectMember", null, ["token", field"] ]
Member of object (as string) ."field" [ "objectMember", null, "field" ]
Member of inner object inner.field [ "objectMember", [ "objectMember", null, "inner"], ["token", "field"] ]
Parameter $field [ "param", "field" ]