entity-routes logo
Docs

SearchFilter#

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.

Example of use with a property, a property path with another defaultWhereStrategy and another global defaultWhereStrategy for that @Entity :

#
typescript
1@Search(
2 ["firstName", ["role.identifier", "CONTAINS"]],
3 { defaultWhereStrategy: "STARTS_WITH" }
4)

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 :

#
typescript
1class AbstractEntity {
2 @PrimaryGeneratedColumn()
3 id: number;
4}
6@Entity()
7class Role extends AbstractEntity {
8 @Column()
9 identifier: string;
12@Search(["firstName", ["role.identifier", "CONTAINS"]], { defaultWhereStrategy: "STARTS_WITH" })
13@Entity()
14class User extends AbstractEntity {
15 @Column()
16 firstName: string;
18 @ManyToOne(() => Role)
19 role: Role;

With these query parameters :

#
json
1{ "id": "123", "firstName": "Alex", "role": "789", "role.identifier": "abc456" }

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.

StrategyTypeShortcutSQL generatedInverse SQL
EXACTnoneprop = value!=
ISnoneprop IS valueIS NOT
INnoneprop IN valueNOT IN
EXISTSnoneprop IS NOT NULLIS NULL
CONTAINSnoneprop LIKE %value%NOT LIKE %value%
STARTS_WITHnoneprop LIKE value%NOT LIKE value%
ENDS_WITHnoneprop LIKE %valueNOT LIKE %value
BETWEEN<>prop IS BETWEEN valueIS NOT BETWEEN
BETWEEN_STRICT><(valueMin > prop AND prop < valueMax)(valueMin <= prop AND prop >= valueMax)
LESS_THAN<prop < value>=
LESS_THAN_OR_EQUAL<|prop <= valueprop > value
GREATER_THAN>prop > value<=
GREATER_THAN_OR_EQUAL>|prop >= valueprop < 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.

Relations#

  • You can filter a relation property by enabling a FilterProperty on its dot delimited path. Example: abc.def.ghi
  • You can filter on a relation by either enabling as FilterProperty the 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) :

  1. 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|or operators. More details here.
  2. propertyPath: Using a dot delimited string, you can target a (shallow|deeply nested) (relation|property) to filter on.
  3. 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.
  4. !: 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 :

#
json
1{
2 "id!": "123",
3 "id;greaterThan!": "456",
4 "owner.id!": "789,321,654",
5 "and(key0):owner.role.identifier": "admin",
6 "and(key0):owner.role.name;endsWith": "@gmail.com",
7 "or(key1):owner.birthDate<>": "1996-01-01T00:00:00, 2000-01-01T00:00:00",
8 "or(key1)and(nestedKey2):owner.id>": "987",
9 "or(key1)or(nestedKey2):owner.email;STARTS_WITH!": "abc"

A query would be produced looking like this :

#
sql
1SELECT ... from teams team
2LEFT JOIN users team_owner ON team.owner_id = users.id
3LEFT JOIN roles team_owner_role ON team_owner_role.id = team_owner.role_id
4WHERE
5 team.id != 123
6AND team.id <= 456
7AND team_owner.id NOT IN (789, 321, 654)
8AND (
9 team_owner_role.identifier = "admin"
10 AND team_owner_role.name LIKE '%@gmail.com'
12OR (
13 (team_owner.birthDate BETWEEN "1996-01-01T00:00:00" AND "2000-01-01T00:00:00")
14 AND (
15 team_owner.id > 987
16 OR team_owner.email NOT LIKE 'abc%'
17 )

Full example#

#
typescript
1class AbstractEntity {
2 @PrimaryGeneratedColumn()
3 id: number;
4}
6@Entity()
7class Role extends AbstractEntity {
8 @Column()
9 identifier: string;
12@Search({ all: true })
13@EntityRoute({ operations: ["list"] })
14@Entity()
15class User extends AbstractEntity {
16 @Column()
17 firstName: string;
19 @ManyToOne(() => Role)
20 role: Role;
Filter descriptionQuery parameter keyQuery parameter value
Get all users with ids in list [1, 2, 3]id1,2,3
Get all users with role equal to 111role or role.id111
Get all super_admin usersrole.identifiersuper_admin
Get all admin usersrole.identifier;endsWith or role.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

For these query parameters :

#
json
1{
2 "id!": 123,
3 "or(isAdminNotSuperOrNameLike)and:role.identifier;contains": "admin",
4 "or(isAdminNotSuperOrNameLike)and:role.identifier!": "super_admin",
5 "or(isAdminNotSuperOrNameLike)or:firstName;startsWith": "Alex"
6}

You would have that query generated :

#
sql
1SELECT ... from users user
2LEFT JOIN roles user_role ON user_role.id = user.role_id
3WHERE
4 user.id != 123
5OR (
6 user_role.identifier LIKE '%admin%'
7 AND user_role.identifier != "super_admin"
8 OR user.firstName LIKE 'Alex%'
9)

Full example

The query parameters would look like this :

#
json
1{
2 "id": "1,2,3",
3 "firstName;startsWith": "Alex",
4 "or(isAdmin)and(aboveId):role.identifier;endswith": "_admin",
5 "or(isAdmin)and(aboveId):id>": "50",
6 "or(isAdmin)or(notSuperOrInList):role.identifier!": "super-admin",
7 "or(isAdmin)or(notSuperOrInList)or:firstName": "Max, Nath"
8}

And the SearchFilter would generate a query like :

#
sql
1SELECT ... from users user
2LEFT JOIN roles user_role ON user_role.id = user.role_id
3WHERE
4 user.id IN (1,2,3)
5AND user.firstName LIKE 'Alex%'
6OR (
7 (
8 user_role.identifier LIKE '%_admin'
9 AND user.id > 50
10 )
11 OR (
12 user_role.identifier != "super_admin"
13 OR user.firstName IN ("Max", "Nath")
14 )

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.

Prev
Introduction
Next
PaginationFilter