Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 160 additions & 99 deletions modules/ROOT/pages/queries-aggregations/filtering.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,51 @@
:page-aliases: filtering.adoc
:description: This page describes filtering operators.

When querying for data, a number of operators are available for different types in the `where` argument of a query or mutation.
When querying for data, a number of operators are available for the types in the `where` argument of a query or mutation, allowing you to filter query results or specify the set of objects a mutation applies to.

== Equality operators
Operators can either be standalone operators (see xref:#_boolean_operators[]) or they are appended to field names (for example, xref:/queries-aggregations/filtering.adoc#_string_comparison[]).

All operators can be combined using the Boolean operators `AND`, `OR`, and `NOT`.

== Operators

=== Boolean operators

As standalone operators, Boolean operators accept an array argument with items of the same format as the `where` argument.
This way, they can be nested to form complex Boolean expressions.

For example, if you want to match all actors either by the name of "Keanu" or not belonging to the "Pantoliano" family, who played in "The Matrix" movie, here is how you query that:

[source, graphql, indent=0]
----
query {
actors(where: {
AND: [
{
OR: [
{ name_CONTAINS: "Keanu" },
{ NOT: { name_ENDS_WITH: "Pantoliano" } }
]
},
{
movies_SOME: { title: "The Matrix" }
}
]}
) {
name
movies {
title
}
}
}
----

`name_CONTAINS` and `name_ENDS_WITH` are xref:/queries-aggregations/filtering.adoc#_string_comparison[String comparisons] while `movies_SOME` is a xref:/queries-aggregations/filtering.adoc#_relationship_filtering[relationship filter].

=== Equality operators

All types can be tested for equality or non-equality.

All types can be tested for either equality.
For non-equality, you *must* use the xref:/queries-aggregations/filtering.adoc#_logical_operators[`NOT`] logical operator.
For example:

.Filtering all users named John
Expand All @@ -21,12 +60,24 @@ query {
}
----

For non-equality, you must use the xref:/queries-aggregations/filtering.adoc#_boolean_operators[`NOT`] logical operator.

.Filtering all users which are not named John
[source, graphql, indent=0]
----
query {
users(where: { NOT: {name: "John" }})
id
name
}
----

[NOTE]
====
For the `Boolean` type, equality operators are the only ones available.
====

== Numerical operators
=== Numerical operators

These are the operators available for numeric (`Int`, `Float`, xref::/types/scalar.adoc[`BigInt`]), xref::/types/temporal.adoc[temporal] and xref::/types/spatial.adoc[spatial] types:

Expand All @@ -52,43 +103,72 @@ query {
Spatial types use numerical filtering differently and they also have additional options.
See xref:filtering.adoc#_filtering_spatial_types[Filtering spatial types] for more information.

== Logical operators
==== Spatial type filtering

All operators can be combined using the logical operators `AND`, `OR`, and `NOT`.
They can also be standalone operators, which means that they can be used as such and not be appended to field names.
Both the `Point` and the `CartesianPoint` types use xref::queries-aggregations/filtering.adoc#_numerical_operators[numerical operators] and have an additional `_DISTANCE` filter.
Here is a list of what each filter does for the two types:

These operators accept an array argument with items of the same format as the `where` argument, which means they can also be nested to form complex combinations.
* `_LT`: checks if a point is less than the distance in the `distance` field away (in meters) from the point specified by the `point` field.
* `_LTE`: checks if a point is less than or equal to the distance in the `distance` field away (in meters) from the point specified by the `point` field.
* `_DISTANCE`: checks if a point is the exact distance in the `distance` field away (in meters) from the point specified by the `point` field.
* `_GT`: checks if a point is greater than the distance in the `distance` field away (in meters) from the point specified by the `point` field.
* `_GTE`: checks if a point is greater than or equal to the distance in the `distance` field away (in meters) from the point specified by the `point` field.

For example, if you want to match all actors by the name of either "Keanu" or not belonging to the "Pantoliano" family, that played in "The Matrix" movie, here is how you can query that:
For a `Point` type, all filters take the following type as an argument:

[source, graphql, indent=0]
----
query {
actors(where: {
AND: [
{
OR: [
{ name_CONTAINS: "Keanu" },
{ NOT: { name_ENDS_WITH: "Pantoliano" } }
]
},
{
movies_SOME: { title: "The Matrix" }
}
]}
) {
input PointDistance {
point: Point!
distance: Float!
}
----

In practice, you can construct queries like the following, which finds all users within a 5km (5000m) radius of a `Point`:

[source, graphql, indent=0]
----
query CloseByUsers($longitude: Float!, $latitude: Float!) {
users(where: { location_LTE: { point: { longitude: $longitude, latitude: $latitude }, distance: 5000 } }) {
name
movies {
title
location {
longitude
latitude
}
}
}
----

Similarly, for a `CartesianPoint` type, all filters take the following type as an argument:

== String comparison
[source, graphql, indent=0]
----
input CartesianPointDistance {
point: CartesianPoint!
distance: Float!
}
----

The following case-sensitive comparison operators are only available for use on `String` and `ID` types:
The same query for a `CartesianPoint`:

[source, graphql, indent=0]
----
query CloseByUsers($x: Float!, $y: Float!) {
users(where: { location_LTE: { point: { x: $x, y: $y }, distance: 5000 } }) {
name
location {
x
y
}
}
}
----

== Type comparison

=== String comparison

The following case-sensitive comparison operators are available for `String` and `ID` types:

* `_STARTS_WITH`
* `_ENDS_WITH`
Expand Down Expand Up @@ -141,16 +221,17 @@ const features = {
const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver });
----

== RegEx matching
=== RegEx matching

The filter `_MATCHES` is also available for comparison of `String` and `ID` types.
The filter `_MATCHES` is available for comparison of `String` and `ID` types.
It accepts RegEx strings as an argument and returns any matches.


Note that RegEx matching filters are **disabled by default**.
Note that RegEx matching filters are disabled by default.
This is because, on an unprotected API, they could potentially be used to execute a https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS[ReDoS attack^] against the backing Neo4j database.

If you want to enable them, set the features configuration object for each:
If you want to enable RegEx matching, update the `features` configuration object.

For `String`:

[source, javascript, indent=0]
----
Expand All @@ -172,8 +253,8 @@ For `ID`:
----
const features = {
filters: {
String: {
ID: true,
ID: {
MATCHES: true,
}
}
};
Expand All @@ -183,7 +264,6 @@ const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver });

For both `String` and `ID`:


[source, javascript, indent=0]
----
const features = {
Expand All @@ -200,96 +280,74 @@ const features = {
const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver });
----

== Array comparison

The following operator is available on non-array fields, and accepts an array argument:

* `_IN`

Conversely, the following operator is available on array fields, and accepts a single argument:

* `_INCLUDES`

These operators are available for all types apart from `Boolean`.

== Filtering spatial types
=== Array comparison

Both the `Point` and the `CartesianPoint` types use xref::queries-aggregations/filtering.adoc#_numerical_operators[numerical operators] and have an additional `_DISTANCE` filter.
Here is a list of what each filter does for the two types:

* `_LT`: checks if a point is less than the distance in the `distance` field away (in meters) from the point specified by the `point` field.
* `_LTE`: checks if a point is less than or equal to the distance in the `distance` field away (in meters) from the point specified by the `point` field.
* `_DISTANCE`: checks if a point is the exact distance in the `distance` field away (in meters) from the point specified by the `point` field.
* `_GT`: checks if a point is greater than the distance in the `distance` field away (in meters) from the point specified by the `point` field.
* `_GTE`: checks if a point is greater than or equal to the distance in the `distance` field away (in meters) from the point specified by the `point` field.

For a `Point` type, all filters take the following type as an argument:
Consider the following type definitions:

[source, graphql, indent=0]
----
input PointDistance {
point: Point!
distance: Float!
type Movie {
id: ID!
title: String!
genres: [String!]
year: Int!
actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN)
}
----

In practice, you can construct queries like the following, which finds all users within a 5km (5000m) radius of a `Point`:

[source, graphql, indent=0]
----
query CloseByUsers($longitude: Float!, $latitude: Float!) {
users(where: { location_LTE: { point: { longitude: $longitude, latitude: $latitude }, distance: 5000 } }) {
name
location {
longitude
latitude
}
}
type Actor {
id: ID!
name: String!
movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
}
----

Similarly, for a `CartesianPoint` type, all filters take the following type as an argument:
The `_IN` operator is available on non-array fields, and accepts an array argument:

[source, graphql, indent=0]
----
input CartesianPointDistance {
point: CartesianPoint!
distance: Float!
query {
movies(where: { year_IN: [1999, 2000, 2001] }) {
title
year
}
}
----

The same query for a `CartesianPoint`:
The query returns all movies released in the years 1999, 2000 and 2001.

Conversely, the `_INCLUDES` operator is available on array fields, and accepts a single argument:

[source, graphql, indent=0]
----
query CloseByUsers($x: Float!, $y: Float!) {
users(where: { location_LTE: { point: { x: $x, y: $y }, distance: 5000 } }) {
name
location {
x
y
}
query {
movies(where: { genres_INCLUDES: "Action" }) {
title
genres
}
}
----

== Querying an interface
The query returns all movies which have "Action" as one of their genres.

`_IN` and `_INCLUDES` are available for all types except `Boolean`.

== Interface filtering

You can use the `typename_IN` filter to filter interfaces.
Refer to xref:types/interfaces.adoc#type-definitions-interfaced-types-querying[Type definitions -> Type -> Interface] for more details and an example.

== Relationship filtering

Relationship filtering depends on the type of relationship that you have:
Relationship filtering depends on the type of relationship:

* `n..1`: filtering done on equality or inequality of the related nodes by specifying a filter on `field`.
* `n..m`: filtering is done on the list of related nodes and is based on the https://neo4j.com/docs/cypher-manual/current/functions/predicate/[list predicates] available in Cypher:
* `n..1`: the filtering is done on equality or inequality of the related nodes by specifying a filter on `field`.
* `n..m`: the filtering is done on the list of related nodes and is based on the https://neo4j.com/docs/cypher-manual/current/functions/predicate/[list predicates] available in Cypher:
** `field_ALL` - https://neo4j.com/docs/cypher-manual/current/functions/predicate/#functions-all[all]
** `field_NONE` - https://neo4j.com/docs/cypher-manual/current/functions/predicate/#functions-none[none]
** `field_SOME` - https://neo4j.com/docs/cypher-manual/current/functions/predicate/#functions-any[any]
** `field_SINGLE` - https://neo4j.com/docs/cypher-manual/current/functions/predicate/#functions-single[single]

As an example, take these type definitions:
For example, take these type definitions:

[source, graphql, indent=0]
----
Expand All @@ -306,10 +364,12 @@ type Post {
likes: [User!]! @relationship(type: "LIKES", direction: IN)
}
----

=== `n..1` relationships

An `author` represents an `n..1` relationship on `Post`, where a given `Post` is authored by one, and only one, `author`.
The available filters here will be `author`.
In the type definitions example, an `author` represents an `n..1` relationship on `Post`, where a given `Post` is authored by one, and only one, `author`.
The available filter is `author`.

For example:

.Find all posts by a desired author
Expand All @@ -334,8 +394,9 @@ query {

=== `n..m` relationships

In the previous example, `posts` represents a `n..m` relationship on `User`, where a given `User` can have any number of `posts`.
Here are some query examples:
In the type definitions example, `posts` represents an `n..m` relationship on `User`, where a given `User` can have any number of `posts`.

For example:

.Find all users where all of their posts contain search term: `"neo4j"`
[source, graphql, indent=0]
Expand Down Expand Up @@ -379,8 +440,8 @@ query {

== Aggregation filtering

This library offers, for each relationship, an aggregation key inside the `where` argument.
It can be used both on the `node` and `edge` of a relationship.
The Neo4j GraphQL library offers an aggregation key inside the `where` argument of each relationship.
You can use it both on the `node` and `edge` of a relationship.

Here are some examples on how to apply this kind of filtering:

Expand Down Expand Up @@ -464,7 +525,7 @@ query {
}
----

=== Operators
=== With operators

Aggregation filtering can also be done with operators.
They provide autogenerated filters available for each type on the `node` and `edge` of the specified relationship.
Expand Down
Loading