Advanced URL Filtering DSL
Introduction
To enable user to directly set up the OData filters using the Visualizer Module settings UI, we're developing a simplified syntax for defining the filters. Said syntax must be human readable and will be directly transformed into its OData counterpart to be saved in the ModuleSettings table.
Query filter syntax
A query can be composed of one or more simple expressions A operator B joined using logical operators AND, OR, NOT and enclosed in parentheses. First, here's a more complex example
contentName starts with [name] and (any contentTag equals "PC" or any contentTag equals "mac")
Expression
The left side of expression is always a field name, using the same nomenclature as Visualizer syntax. As a consequence of OData feature constraints we support the contentName and contantTags fields and the dynamic fields.
The right side of the expression can be a constant, enclosed in double quotes or query parameter, enclosed in square brackets.
Transformation to OData
A parsed query filter must be transformed to valid OData for consumption by the visualizer API. Here are some examples of said transformation for individual expressions
Query filter expressions | OData filter expression | remarks |
---|---|---|
color equals "red" | details/color eq 'red' | query syntax does not need the details/ prefix |
any of contentTags equals "PC" | tags/any(tag: tag eq 'PC') | |
price equals 10 | details/price eq 10 | numeric constants do not need enclosing quotes |
any manufacturer.contentSlug equals "mercedes-benz" | details/manufacturer(r: r/slug eq 'mercedes-benz') | dot notation like in visualizers. currently only slug is suppported for reference object fields |
date greater than "2017-10-10" | details/date gt DateTime'2017-10-10' | dates must use ISO format. transformation must include type cast |
any of categories equals "RPG" | details/categories/any(c: c equals 'RPG') | it's possible to write either any or any of |
color equals [color] | details/color eq '[color]' | parameter don't need enclosing quotes |
color not equals "blue" | details/color ne "blue" | negation translates into specialized operators |
contentName starts with "[OT]" | startswith(name, '[OT]') | startswith function. note that right hand value is enclosed in quotes making it a literal not a query parameter |
Transforming operators
query filter operator | OData operator |
---|---|
equals | eq |
not equals | ne |
greater than | gt |
greater than or equal | ge |
less than | lt |
less than or equal | le |
Formal grammar
Below is the grammar of the query DSL written in Augmented Backus-Naur Form.
QueryFilter = Expression 1*LWSP *(LogicalOperator 1*LWSP Expression)
Expression = "(" QueryFilter ")" / RootExpression
LogicalOperator = "and" / "or"
RootExpression = [ AnyModifier 1*LWSP ] FieldAccessor 1*LWSP Operator 1*LWSP ( Variable / Constant )
AnyModifier = "any" / "any of"
Operator = "starts with" / ArithmeticOperator
ArithmeticOperator = [ "is" 1*LWSP ] [ "not" 1*LWSP ] "equal" [ "s" ]
ArithmeticOperator =/ "greater than" / "greater than or equal"
ArithmeticOperator =/ "less than" / "less than or equal"
FieldAccessor = Identifier [ "." Identifier ]
Constant = Number / StringLiteral
Number = [ "-" ] *DIGIT [ "." 1*DIGIT ]
StringLiteral = """ 1*CHAR """ / "'" 1*CHAR "'"
Variable = "[" Identifier "]"
Identifier = ALPHA *(ALPHA / DIGIT)
Example
1) In Content Library, create 5 content items of Person content type:
First Name | Last Name |
---|---|
Ben | Zhong |
Dinesh | Kumar Karmankar |
Mike | Bigun |
Manuel | Gonzalez |
Daniel | Aguilera |
2) Create a blank page and add following Visualizer:
Type | List |
Content Type | Person |
Visualizer | Square image Right w/ Name |
3) In the visualizer settings screen, enable URL Filtering:
4) Click Advanced Settings and enter following filter expression:
firstName equals 'Manuel'
5) Click Save and then Apply. Page should look like this:
6) Hover the visualizer and click Manage Visualizer Settings:
7) Click Next on all steps of the wizard up to the Set Visualizer Settings page. Click Advanced Settings and change the filter expression to:
firstName equals 'Dinesh'
8) Click Save, and then Apply. The visualizer will now render a new person based on above criteria.
Processing query filters
The user can create multiple filters for a given Visualizer Instance. They will be stored as-is along other module settings. When the Visualizer Instance is saved, the service validates the syntax and also the use of fields (ie. all fields names exist in the content type) as well as correct use of data types to ensure that numeric field isn't compared to strings and date fields are used with properly formatted dates.
Here's a sequence diagram describing that interaction:
Validating query against the queried Content Type
After being successfully parsed, a query filter must be validated against the queried Content Type. Here are rules governing content type fields
Number field
Can only be used with numbers
Valid examples
field equals 5
Invalid examples
field equals "a"
field equals "5"
Date field
Can only be used with properly formatted ISO date.
Valid examples
date equals "2017-10-10"
date less than "2018-01-01T10:20:10"
Invalid examples
date equals "2017/09/07"
Date field (sub-type time)
Can only be used with a properly formatted time string
Valid examples
time less than "10:10:00"
time equals "12:00"
Invalid examples
time equals "99:00"
time less than "noon"
Reference object field
- Equality operators cannot be used with reference object fields
- Only slug is allowed when querying reference object fields
- Single ref can use dot notation to access slug
- Multiple ref must use any (of) modifier
- starts with cannot be used with ultipl
Valid examples
singleRef.slug equals "my-page"
any multipleRef.slug equals "my-page"
Invalid examples
singleRef equals "some id"
multipleRef.slug equals "my-page"
singleRef.name equals "Tomasz"
any multipleRef.slug starts with "my-page"
Multiple choice field
Can only be queried with conjunction the any of modifier.
Cannot be used with starts with.
Valid example
any of choices equal "YES"
any engineType equals "diesel"
Invalid examples
engineType equals "diesel"
any of choices starts with "medium"
Extracting query string parameters
The query syntax allows two types of values to be used: constants and variables. The latter are alphanumeric identifiers enclosed in square brackets. For example the expression below uses a variable name.
contentName starts with [name]
When such variable is parsed it will be transformed into a query string variable in the OData filter definition as defined.
[{"QueryStringParams": ["name"], "ODataFilterQueryFormat": "name eq '[name]'"}]
Note that a variable must not be enclosed in quotation.
If a query is written as follows
threadTitle starts with "[OT]"
Then the value "[OT]" will be used as a plain string literal, and will not generate a query string parameter:
[{"QueryStringParams": [], "ODataFilterQueryFormat": "details/threadTitle eq '[OT]'"}]