SearchFilter#
The SearchFilter allows you to filter efficiently a list of items with several
common SQL operators (such as =, IN, EXISTS, LIKE, etc) using
readable query parameter keys. You can filter on nested relations/properties with
complex conditions.
Decorator#
You can register a SearchFilter on an entity using the @Search decorator.
You can directly pass an array of FilterProperty, which is an array of property/nested
property path or a tuple of property/nested property path with a specific default StrategyType. This
list contains the filterable properties.
Each StrategyType will allow you to filter a property through different conditions.
You can use any StrategyType on a filterable property. Specifying one for a property path in the
FilterProperty list just sets it as the default strategy to use for that property
path and allows you to omit it in query parameters.
Example of use with a property, a property path with another defaultWhereStrategy and another global
defaultWhereStrategy for that @Entity :
In this example the firstName property will default to STARTS_WITH unless it is overriden by specifiying another
StrategyType directly in the request, whereas the role.identifier property will default
to CONTAINS strategy, unless overriden directly in the request.
Property decorator#
TODO
Good to know#
Basic Usage#
Once you have enabled the properties you want to filter, you can use them as query parameters on list operation for
that @Entity.
On a User entity, by enabling only the firstName and role.identifier properties :
With these query parameters :
On the request: GET:/users/?id=123&firstName=Alex&role=789&role.identifier=abc456
Only the firstName & role.identifier query parameters will be used as filtered since they alone were explicitly
specified on @Search filterable properties.
firstName strategy is STARTS_WITH since it is the defaultWhereStrategy given in options and role.identifier
strategy is CONTAINS since it was provided in properties (in FilterProperty tuple).
Strategies#
These strategies can be used both as defaultWhereStrategy or directly in query parameter with the
advanced syntax.
| StrategyType | Shortcut | SQL generated | Inverse SQL | 
|---|---|---|---|
| EXACT | none | prop = value | != | 
| IS | none | prop IS value | IS NOT | 
| IN | none | prop IN value | NOT IN | 
| EXISTS | none | prop IS NOT NULL | IS NULL | 
| CONTAINS | none | prop LIKE %value% | NOT LIKE %value% | 
| STARTS_WITH | none | prop LIKE value% | NOT LIKE value% | 
| ENDS_WITH | none | prop LIKE %value | NOT LIKE %value | 
| BETWEEN | <> | prop IS BETWEEN value | IS NOT BETWEEN | 
| BETWEEN_STRICT | >< | (valueMin > prop AND prop < valueMax) | (valueMin <= prop AND prop >= valueMax) | 
| LESS_THAN | < | prop < value | >= | 
| LESS_THAN_OR_EQUAL | <| | prop <= value | prop > value | 
| GREATER_THAN | > | prop > value | <= | 
| GREATER_THAN_OR_EQUAL | >| | prop >= value | prop < value | 
If you're wondering why LESS_THAN_OR_EQUAL and GREATER_THAN_OR_EQUAL use the | instead of = character, it's
because the = is already used to define the limit between the query parameter key and value.
Multiple values#
If you need to filter on the same FilterProperty and
StrategyType but with different values, you can use comma-separated values as query param
value. Then each of these values will be trimmed and then wrapped in the same SQL condition with a OR conditions for
each value.
BETWEEN_STRICT is the only strategy that always requires exactly 2 values. Example: { "id><": "3, 8" } or
{ "role.id;betweenStrict": "3, 8" }
Relations#
- You can filter a relation property by enabling a FilterPropertyon its dot delimited path. Example:abc.def.ghi
- You can filter on a relation by either enabling as FilterPropertythe relation itself or by appending.id. Example:{ "role": 123 }is the same as{ "role.id": 123 }
- There is no limit to the nesting, be careful with the SQL performance of every LEFT JOIN made if you nest too far
Advanced usage#
Since any StrategyType is available for a filterable property, sometimes you might want
to use another one than the default one. To do so you just need to use the strategy part of the advanced syntax.
There is a syntax for query parameters keys that makes filters very powerful. There are 4 parts to a
FilterProperty query parameter. Each part name is wrapped with parenthesis below (for
readability, there are no parenthesis in usage) :
(complex condition)(propertyPath)(;StrategyType|shortcut)(!)
- complex condition: Optional. The complex condition part might be used if you have advanced needs, it allows you to nest and group conditions with the- and|- oroperators. More details here.
- propertyPath: Using a dot delimited string, you can target a (- shallow|- deeply nested) (- relation|- property) to filter on.
- StrategyType|shortcut: Optional. The StrategyType name (can be written camelCased or as UPPERCASE_SNAKE_CASE) prepended by a colon or just the shortcut (without the colon) if there is any available.
- !: Optional. The NOT operator, by appending a- !to a query parameter key, the StrategyType will be inversed.
Quick example#
Below is an example which makes no actual sense whatsoever, still, it remains valid and would produce the following query.
With these query parameters :
A query would be produced looking like this :
Full example#
On the user.list route scope (GET:/users/): Using all: true, you can filter on any property path from the User
entity. Here the default strategy used for every FilterProperty is EXACT since it's
the global default one and none were specified on the @Search decorator.
| Filter description | Query parameter key | Query parameter value | 
|---|---|---|
| Get all users with ids in list [1, 2, 3] | id | 1,2,3 | 
| Get all users with role equal to 111 | roleorrole.id | 111 | 
| Get all super_admin users | role.identifier | super_admin | 
| Get all admin users | role.identifier;endsWithorrole.identifier;ENDS_WITH | _admin | 
Complex conditions#
At some point, you might need to have more complex conditions to filter your list, requiring the SQL OR operator, or
with maybe some nested conditions. That is possible using the complex conditions syntax.
All you need for that is to prefix your condition with the and or or condition operator. When you have nested
conditions you should append a condition identifier wrapped in parenthesis after the condition operator
(and|or). That condition identifier can be any string you want, as long as you re-use the same consistently for
the same conditions you want to nest.
You can chain them indefinitely and add as much nested conditions as you want. It is recommended to use names that makes sense to make the query parameter key almost readable like an english sentence.
Example
Using the same entities as the example above.
For these query parameters :
You would have that query generated :
Full example
To get all users with first name that starts with "Alex" that are either in the list [1, 2, 3] or that are admin and with an id above 50 but not super_admin unless his first name is "Max" or "Nath". (Yes this is a complete unrealistic demand, I just needed an example of a complex query.)
The query parameters would look like this :
Reminder: The complex condition names (isAdmin) and (aboveId) are completely arbitrary, you just need to use the
same string when you want to nest conditions.
And the SearchFilter would generate a query like :
Default config#
You can get the default SearchFilter using the
getSearchFilterDefaultConfig function. It can be used for example to
retrieve the defaultWhereStrategy inside the options key.