Documentation‎ > ‎

JSONQuery

JSONQuery/JSONPath support is based on the specification for JSONPath and has been greatly expanded with numerous additional features for more powerful querying capabilities. This expanded set of functionality is called JSONQuery. A JSONQuery starts with object URL that is indicates the list being queried. JSONQuery can then be used to find objects by different property values using various operators. For example:

/Customer/?lastName='Smith'
This will find all the customers that have a last name of "Smith". We could do more sophisticated queries as well:
/Product/?price<15.00 & rating>3

This will find all the products with a price less than 15.00 and a rating more than 3. You can enclose queries in brackets to perform successive operations:

/Product/[?price<15.00][0:10]

This will return the first ten products that have a price less than 15.00. JSONQuery can also extract properties from a list of objects to return a set of property values:

/Product/[?price<15.00][=name]
This will return a list of the names of the products less than 15.00. This is called mappings, and you can do more sophisticated mappings as well:
/Customer/[={name:firstName + ' ' + lastName, address: street + state}]

This will return a list of objects that where the name is the first name plus the last name, and the address is the street plus the state.

We can also sort results:

/Product/[\rating]
This would return a list of products sorted in descending order by their rating. [/expr] indicates ascending and [\expr] indicates descending. You can also use multiple sort orders:
/Product/[\rating, /price]
This would sort by descending order for rating, and ties would be sorted by ascending order by price.

Another capability of JSONQuery is the recursive decent operator, “..”. One can recursively search data with the recursive operator. For example the query:

/Customer/objectid..name
This would search through all the children of the provided object, as well as the sub children and so on for the name properties and return their values.
You can search for property values that match a specific object by using the object id literal syntax:
/Friend/?bestFriend=/Friend/33
We can also search for matches within an array using the contains method:
/PurchaseOrder/?productsPurchased.contains(/Product/21)

The full set of operators that can be used in JSONQuery expressions are:

Operator Meaning
.property This will return the provided property of the object, behaving exactly like JavaScript.
[expression] This returns the property name/index defined by the evaluation of the provided expression, behaving exactly like JavaScript.
[?expression] This will perform a filter operation on an array, returning all the items in an array that match the provided expression. This operator does not need to be in brackets, you can simply use ?expression, but since it does not have any containment, no operators can be used afterwards when used without brackets.
[/expression], [\expression], [/expression, /expression] This performs a sort operation on an array, with sort based on the provide expression. Multiple comma delimited sort expressions can be provided for multiple sort orders (first being highest priority).
[=expression] This performs a map operation on an array, creating a new array with each item being the evaluation of the expression for each item in the source array.
[start:end:step] This performs an array slice/range operation, returning the elements from the optional start index to the optional end index, stepping by the optional step number.
[expr,expr] This a union operator, returning an array of all the property/index values from the evaluation of the comma delimited expressions.
.* or [*] This returns the values of all the properties of the current object.
$ This is the root object, If a JSONQuery expression does not being with a $, it will be auto-inserted at the beginning.
@ This is the current object in filter, sort, and map expressions. This is generally not necessary, names are auto-converted to property references of the current object in expressions.
..property Performs a recursive search for the given property name, returning an array of all values with such a property name in the current object and any subobjects
expr = expr Performs a comparison (like JS's ==). When comparing to a string, the comparison string may contain wildcards * (matches any number of characters) and ? (matches any single character).
+, -, /, *, &, |, %, (, ), <, >, <=, >=, != These operators behave just as they do in JavaScript.
 /Table/id This is an object id literal, and this represents the object with the given id, and can be used to match on that object in expressions
date(<ISO date string>) or date(<epoch milliseconds>)
This represents a date and can be used to match and compare dates in expressions. For example:
?born=date("Sat, 07 Feb 2009 22:51:26 GMT") or ?created=date(1234047109376)
.method(arguments) Performs a method call. You can add new methods to objects and arrays to have them be available for execution from queries, but they must have their safe attribute set to true in their method definition.
.method(?expr)
By using ?expr as a parameter to a method call, a function is passed to method where the function returns the result of the provided JSONQuery expression. For example:
/Table/3.someMethod(?retailPrice-wholesalePrice)
This would provide a function that calculates the expression (using the properties of the array item) for each array item.
.distinct()  Removes duplicates from the current result set
.contains(values...)
Returns true if one the values in the arguments matches on of the values in the array. Expressions can also be passed to this method, and if the expression returns true for any of the array values, than this method returns true.
.sum(?expr) Calculates the sum of the expression computed for each object in the array. For example, this would calculate the sum of the values of the quantity property of all the Product instances:
/Product/.sum(?quantity)
.max(?expr)
Calculates the maximum of the expression computed for each object in the array.
.min(?expr)
Calculates the minimum of the expression computed for each object in the array.
 .length  Returns the total number of items in the result set
RegExp("regex").test(expr) Matches on the regular expression. For example to find all the author names that start with "J":
/Book/[?RegExp('J.*').test(authorName)]

Remember to always properly encode your JSONQuery queries when using them in URLs, leaving queries unencoded is a very common mistake.

Note that it is recommended that you use the HTTP Range header for paging rather than the slice operator.

JSONQuery uses a safe subset of the sub-expressions which is highly portable and is safe from arbitrary code execution. The following operators (separated by commas) may be used in sub-expressions with safe-evaluation enabled:

&, |, =, !=, >=, <=, +, -, /, *, ?, :

Limiting Querying

All queries are executed through the "query" method on the containing class. The default implementation of querying is in Object.query, and all classes will inherit this query method unless they override it. The implementation of Object.query is very simple, and the actual complexity on parsing queries is handled elsewhere:

query: function(query, target){
    return query.execute(target);
}

The query function is passed two arguments. The first is the query string (a special type of string), and the second is the target that is being queried (typically the table/set of instances of the class). The query string is an instance of (instanceof) QueryString, and is a subclass String. A QueryString includes an execute() method that executes the query string through JSONquery parsing (to JavaScript). However, it is also features some special regex extensions to make it easier to work with.

The QueryString class, which is the type for the query parameter, includes several special expressions that can be used in regex to limit query forms. For example, to limit a query to simple name-value filtering, you could do:
query: function(query, target){
    if (query.match(/^\?$prop=$value$/) {
        return query.execute(target);
    }
    throw new AccessError("Query to complex, not allowed");
}

There are several of these special regex expressions for use in queries. Each of them is a captured group (and returns what it matches, unless otherwise noted):
$prop - Matches a property name. In ?foo=, it would match foo. In ?@.foo=, it would match @.foo, and return foo.
$value - Maches a value. In ?foo='bar', it would match 'bar', and ?foo=44, it would match 44
$comparator - Matches a comparison operator, including =, >, <, <=, >=, !=.
$operator - Matches a value operator, including +, -, *, /, &, |, ?, :
$logic - Matches a logic operator, including &, |
$expression - Matches an expression (within a filter or sort operation). In [?foo='bar'&price<34], it would match foo='bar'&price<34
$filter - Matchs a filter and returns the expression in it. In [?foo='bar'&price<34], it would match the whole string, and returns the expression
$sort - Matches a sort operation

Class({
  id:"MyClass",
  query: function(query, target){
    if (query.match(/^\?$prop=$value$/) {
        return query.execute(target);
    }
    throw new AccessError("Query to complex, not allowed");
  },
...

One could also augment a query to add your own internal constraints:
  query: function(query, target){
    if (query.match(/^\?$prop=$value$/) {
        return (new QueryString(query + "&active=true")).execute(target);
    }
    throw new AccessError("Query to complex, not allowed");
  },