The following is a specification of the supported syntax.
Table of contents:
- About
- Layers
- [L1] Base syntax
- [L2] Folded syntax
- [L3] Custom extension
- Example data
- Layers
- Filter Object
- Basic matching
- Nested keys
- [L2] Literal dot
- Missing keys
- Negation
- [L2] Double negation
- Root matching
- [L2] Simple matching
- [L2] Matching scalar
- [L2] Matching list
- [L2] Multiple matching
- Operators
- Comparators
- Combinators
- $and combinator
- $and list
- [L2] $and object
- $or combinator
- $or list
- [L2] $or object
- [L2] $not combinator
- [L2] $not list
- [L2] $not object
- [L3] Additional combinators
- [L3] Common combinators
- $and combinator
- Nesting
The specifiation is split into several layers.
Layer 1 (L1) is the default and defines the base syntax that must be supported.
Layer 2 (L2) is a folded syntax that is more convenient to produce when matching multiple keys (in particular when the filters are not machine-produced).
This syntax can be transformed into the more explicit base syntax (unfolding). The folded syntax is often assumed to be easier to produce and consume. However, it often turns out to be limited and/or ambiguous when it comes to more complex nested filters.
An implementation of this specification is expected to implement this syntax (though not strictly demanded).
Layer 3 (L3) defines custom extension points. An implementation may choose to extend the mandated base syntax by providing custom extensions in these situations.
This document pro
[
{ "id" : 100, "name" : "Test", "age" : 20 },
{ "id" : 200, "name" : "Peter", "age" : 25 }
]
The core of this specification is that each and every filter is always expressed as simple JSON object like this:
{
}
Additional keys should be added to this filter object as per the following specification.
The basic syntax for matching the value of a key to a given value always looks like this:
{
"key" : { "$comparator" : value }
}
The possible comparators (among with examples) are defined in the comparator section.
Nested keys are supported using dot notation like this:
{
"name.first" : { "$comparator" : value }
}
This filter matches every object that has a "name" sub-object where the value of the key "first" matches the comparator.
In the case that your object keys actually include a dot in the name,
you can use the \
prefix to escape the dot.
Because the backslash has to be escaped by another backslash, the resulting filter looks like this:
{
"dotted\\.key" : { "$comparator" : value }
}
Accessing the value of a key that does not exist will always yield a null
value.
{
"unknown" : { "$comparator" : value }
}
Will evaluate as if the key had a null
value if the key "unknown" does not exist.
This will check if the value null
matches against the value of the comparator.
If you need to tell a non-existant key apart from a key that actually holds a null value,
you can use the $contains
comparator (object) as described in the section about root matching.
Every comparator can be negated by prefixing it with !
like this:
{
"key" : { "!$comparator" : value }
}
This filter matches every object where the given comparator does NOT match the given value.
Prefixing every comparator with a !
results in a negated comparator. Because
of this, it's legal to double-negate comparators like this:
{
"key" : { "!!!$comparator" : value }
}
Double-negation is effectively a NO-OP. Because of this, the above example is equivalent to this:
{
"key" : { "!$comparator" : value }
}
Despite matching keys within an object, one can also apply operators to the root object like this:
{
"$comparator" : value
}
This filter matches if the object matches the comparator value.
This is often used with the $contains
comparator to check if a given key exists:
{
"$contains" : "unknown"
}
Matches if the given key "unknown" exists within the root object.
This convenient shortcut syntax allows one to leave out the $is
comparator for scalar values to compare against.
{
"id" : 100
}
This is equivalent to the longer form
{
"id" : {
"$is" : 100
}
}
This filter matches every object that has id=100.
This convenient shortcut syntax allows one to leave out the $in
comparator for a list of values to compare against.
{
"id" : [
100,
200,
300
]
}
This is equivalent to the longer form
{
"id" : {
"$in" : [
100,
200,
300
]
}
}
This filter matches every object that has either of id=100 OR id=200 OR id=300.
An empty list will never match.
{
"id" : 100,
"name" : "Test"
}
Matches every object that has both id=100 AND name=Test.
The same matching and comparator rules as above apply.
See also following chapter about combinators. The above example is a shorthand syntax for the following:
{
"$and" : [
{
"id" : 100
},
{
"name" : "Test"
}
]
}
Because of these unfolding rules, an empty object will always match.
{
"age" : {
"$gte" : 20,
"$lte" : 30
}
}
Matches every object that has both age>=20 AND age<=30.
See also following chapter about combinators. The above example is a shorthand syntax for the following:
{
"$and" : [
{
"age" : { "$gte" : 20 }
},
{
"age" : { "$lte" : 30 }
}
]
}
Because of these unfolding rules, an empty object will always match.
This query language supports two classes of operators further described below:
You can use any of the following comparison operators (comparators)
The $is
comparator checks if the value of the key is strictly equal (type and value) to the given value.
{ "id" : { "$is" : 100 } }
This filter matches every object that has id=100.
Note that this comparators checks for strictly equal values and types.
{ "id" : { "$is" : "100" } }
This filter matches every object that has an id attribute of type string and id=100. In the above example, this does not match any object, because id is always of type number.
If you want to support multiple types, consider using the $in
comparator and explicitly list all possible types.
The $in
comparator checks if the value of the key is "in" (either of) the given list of values.
{ "id" : { "$in" : [ 100, 101, 102 ] } }
This filter matches every object that has either of id=100 OR id=101 OR id=102.
Note that this comparator checks for strictly equal values and types, just like the $is
comparator.
{ "id" : { "$in" : [ "100", "101" ] } }
This filter matches every object that has an id attribute of type string and a value of either id=100 or id=101. In the above example, this does not match any object, because id is always of type number.
If you want to support multiple types, you can explicitly list all possible types like this:
{ "registered" : { "$in" : [ false, 0, null ] } }
This matches every object that has either of registered=false OR registered=0 or registered=null.
An empty list will never match.
This comparator exclusively accepts an array of possible values, passing anything else (e.g. single scalar or object etc.) is a syntax error.
This is not to be confused with the
$contains
comparator (array) which works the other way around: It checks if an array key value contains a single expected value.
The $contains
comparator checks if the given value is contained in the value of the key.
{ "key" : { "$contains" : value } }
This can be used for three things:
- Check if a string/scalar key value contains the given substring value
- Check if an array key value contains the given value element
- Check if an object key value contains the given value key
This comparator expects a single possible value to match against. Passing an array of values will literally check for this array as a single value. If you want to accept multiple values, see below for the combinators.
{ "name" : { "$contains" : "ter" } }
This filter matches if the "name" string contains the substring "ter" (case-sensitive matching).
{ "tags" : { "$contains" : "new" } }
This filter matches if the "tags" array contains an element with the value "new".
This is not to be confused with the
$in
comparator which works the other way around: It checks if a key value is contained in a list of expected values.
{ "location" : { "$contains" : "name" } }
This filter matches if the "location" object contains a key with the name "name".
The $contains
comparator can also be used to check if a key exists in the root object.
{
"$contains" : "unknown"
}
See also the above section about root matching and the difference between matching missing keys.
Checking for nested keys is currently not supported (see issue #14). If you want to check if key "a.b.c" exists, you can use the following workaround:
{
"a.b" : {
"$contains" : "c"
}
}
The $lt
comparator checks if the value of the key is "less than" the given value.
{ "id" : { "$lt" : 100 } }
This filter matches every object that has an id of less than 100, i.e. it matches 99, but does not match 100.
The $lte
comparator checks if the value of the key is "less than or equal to" the given value.
{ "id" : { "$lte" : 100 } }
This filter matches every object that has an id of less than or equal to 100, i.e. it matches 99, it matches 100 but does not match 101.
The $gt
comparator checks if the value of the key is "greater than" the given value.
{ "id" : { "$gt" : 100 } }
This filter matches every object that has an id of greater than 100, i.e. it matches 101, but does not match 100.
The $gte
comparator checks if the value of the key is "greater than or equal to" the given value.
{ "id" : { "$gte" : 100 } }
This filter matches every object that has an id of greater than or equal to 100, i.e. it matches 101, it matches 100, but does not match 99.
The $not
comparator is a shorthand for negated matching.
It accepts either a scalar value or a list of values. Every other type (e.g. object etc.) is a syntax error.
The $not
comparator can be used for scalar values like this:
{ "id" : { "$not" : 100 } }
The above example can also be written explicitly like this:
{ "id" : { "!$is" : 100 } }
This filter matches every object that does NOT have id=100.
The $not
comparator can be used for lists like this:
{ "id" : { "$not" : [ 100, 200 ] } }
The above example can also be written explicitly like this:
{ "id" : { "!$in" : [ 100, 200 ] } }
This filter matches every object that does NOT have (id=100 OR id=200).
An implementation may choose to define additional custom operators like $regex
, $starts
, $ends
and others.
An implementation may choose to define some of the common operators as a fallback:
{ "id" : { ">=" : 100 } }
{ "id" : { "$gt" : 100 } }
Combinators allow one to combine multiple filters to a bigger filter rule.
Expects a list of filters like this:
{ "$and" : [ filter, … ] }
Only matches if each and every of the given filters in the list do match. Does not match if any of the given filters does not match.
{
"$and" : [
{
"id" : 100
},
{
"name" : "Test"
}
]
}
An empty $and
list will always match.
Providing only a single filter expression is supported.
Also accepts a folded L2 object like this:
{
"$and" : {
"id" : 100,
"name" : "Test"
}
}
Matches every object that has both id=100 AND name=Test.
The above example can be unfolded to the explicit L1 list form:
{
"$and" : [
{
"id" : 100
},
{
"name" : "Test"
}
]
}
Because of these unfolding rules, an empty object will always match.
Expects a list of filters like this:
{ "$or" : [ filter, … ] }
Matches it one of the given filters in the list matches. Does not match if none of the given filters in the list match.
An empty $or
list will never match.
Providing only a single filter expression is supported.
Also accepts a folded L2 object like this:
{
"$or" : {
"id" : 100,
"name" : "Test"
}
}
Matches every object that has either id=100 OR name=Test.
The above example can be unfolded to the explicit L1 list form:
{
"$or" : [
{
"id" : 100
},
{
"name" : "Test"
}
]
}
Because of these unfolding rules, an empty object will never match.
Negating combinators is achieved by prefixing them with !
.
The $not
combinator is a convenience shorthand for !$and
.
Expects a list of filter like this:
{
"$not" : [ filter, filter ]
}
Only matches if one of the given filters in the list does NOT match. Does not match if each and every of the given filters does match.
The above example can also be written like this:
{
"!$and" : [ filter, filter ]
}
Because of these unfolding rules, an empty list will never match.
Can also be used with a filter object like this:
{
"$not" : filter
}
Only matches if the filter does not match. Does not match if the filter matches.
Accepts an object like this:
{
"$not" : {
"id" : {
"$is" : 100
}
}
}
Matches every object that does NOT have id=100.
The above example can also be written like this:
{
"!$and" : {
"id" : {
"$is" : 100
}
}
}
This is equivalent to negated matching like this:
{
"id" : {
"!$is" : 100
}
}
Also accepts a folded L2 object like this:
{
"$not" : {
"id" : 100,
"name" : "Test"
}
}
Matches every object that does NOT have (id=100 AND name=Test).
The above example can be unfolded to the explicit L1 basic matching form:
{
"$not" : {
"id" : {
"$is" : 100
},
"name" : {
"$is" : "Test"
}
}
}
Which can then be transformed to the explicit negation form:
{
"!$and" : {
"id" : {
"$is" : 100
},
"name" : {
"$is" : "Test"
}
}
}
Due to De Morgan's laws this is equivalent to:
{
"$or" : {
"id" : {
"!$is" : 100
},
"name" : {
"!$is" : "Test"
}
}
}
Because of these unfolding rules, an empty object will never match.
An implementation may choose to implement additional combinators like $xor
, $xnor
and others.
An implementation may choose to implement some common combinators.
A common alias of the L2 $not
combinator:
{ "$nand" : filters }
{ "!$and" : filters }
Or the disjunctive equivalent of the L2 $not
combinator:
{ "$nor" : filters }
{ "!$or" : filters }
Or common names for the negated possible L3 $xor
combinator:
{ "$xnor" : filters }
{ "!$xor" : filters }
Nesting filters is supported.