Type and value order in AQL
AQL uses a set of rules for equality checks and comparisons that takes both the data types and the actual values into account
When checking for equality or inequality, or when determining the sort order of values, AQL uses a deterministic algorithm for the comparison.
The compared operands are first compared by their data types, and only by their data values if the operands have the same data types.
The following type order is used when comparing data types:
null < bool < number < string < array (or list) < object (or document)
This means null
is the smallest type in AQL and object is the type with
the highest order. If the compared operands have a different type, then the
comparison result is determined and the comparison is finished.
For example, the boolean true
value is always less than any numeric or
string value, any array (even an empty array), and any object. Additionally, any
string value (even an empty string) is always greater than any numeric
value and a boolean value (true
and false
).
null < false
null < true
null < 0
null < ''
null < ' '
null < '0'
null < 'abc'
null < [ ]
null < { }
false < true
false < 0
false < ''
false < ' '
false < '0'
false < 'abc'
false < [ ]
false < { }
true < 0
true < ''
true < ' '
true < '0'
true < 'abc'
true < [ ]
true < { }
0 < ''
0 < ' '
0 < '0'
0 < 'abc'
0 < [ ]
0 < { }
'' < ' '
'' < '0'
'' < 'abc'
'' < [ ]
'' < { }
[ ] < { }
If the two compared operands have the same data types, then the operands values are compared. For the primitive types (null, boolean, number, and string), the result is defined as follows:
- null:
null
is equal tonull
- boolean:
false
is less thantrue
- number: numeric values are ordered by their cardinal value
- string: string values are ordered using a localized comparison, using the configured server language for sorting according to the alphabetical order rules of that language
Note: unlike in SQL, null
can be compared to any value, including null
itself, without the result being converted into null
automatically.
For compound types (array and object), the following special rules are applied:
Two array values are compared by comparing their individual elements position by
position, starting at the first element. For each position, the element types
are compared first. If the types are not equal, the comparison result is
determined, and the comparison is finished. If the types are equal, then the
values of the two elements are compared. If one of the arrays is finished and
the other array still has an element at a compared position, then null
is
used as the element value of the fully traversed array.
If an array element is itself a compound value (an array or an object), then the comparison algorithm checks the element’s sub-values recursively. The element’s sub-elements are compared recursively.
[ ] < [ 0 ]
[ 1 ] < [ 2 ]
[ 1, 2 ] < [ 2 ]
[ 99, 99 ] < [ 100 ]
[ false ] < [ true ]
[ false, 1 ] < [ false, '' ]
Two object operands are compared by checking attribute names and value. The attribute names are compared first. Before attribute names are compared, a combined array of all attribute names from both operands is created and sorted lexicographically. This means that the order in which attributes are declared in an object is not relevant when comparing two objects.
The combined and sorted array of attribute names is then traversed, and the
respective attributes from the two compared operands are then looked up. If one
of the objects does not have an attribute with the sought name, its attribute
value is considered to be null
. Finally, the attribute value of both
objects is compared using the aforementioned data type and value comparison.
The comparisons are performed for all object attributes until there is an
unambiguous comparison result. If an unambiguous comparison result is found, the
comparison is finished. If there is no unambiguous comparison result, the two
compared objects are considered equal.
{ } == { "a" : null }
{ } < { "a" : 1 }
{ "a" : 1 } < { "a" : 2 }
{ "b" : 1 } < { "a" : 0 }
{ "a" : { "c" : true } } < { "a" : { "c" : 0 } }
{ "a" : { "c" : true, "a" : 0 } } < { "a" : { "c" : false, "a" : 1 } }
{ "a" : 1, "b" : 2 } == { "b" : 2, "a" : 1 }