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 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) |
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" ] |