Skip to content
Merged
Changes from 3 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
239 changes: 132 additions & 107 deletions modules/ROOT/pages/queries-aggregations/filtering.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,50 @@
: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.

== 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 types can be tested for either equality.
For non-equality, you *must* use the xref:/queries-aggregations/filtering.adoc#_logical_operators[`NOT`] logical operator.
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.
For example:

.Filtering all users named John
Expand All @@ -21,12 +59,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 +102,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

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.

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.
For a `Point` type, all filters take the following type as an argument:

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.
[source, graphql, indent=0]
----
input PointDistance {
point: Point!
distance: Float!
}
----

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:
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 {
actors(where: {
AND: [
{
OR: [
{ name_CONTAINS: "Keanu" },
{ NOT: { name_ENDS_WITH: "Pantoliano" } }
]
},
{
movies_SOME: { title: "The Matrix" }
}
]}
) {
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 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

The following case-sensitive comparison operators are only available for use on `String` and `ID` types:
=== 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 +220,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 Down Expand Up @@ -183,6 +263,7 @@ const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver });

For both `String` and `ID`:

// Is the following correct? would have expected "filters: { String : { MATCHES: true, ID: true}}" based on the two above

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

== Array comparison
=== Array comparison

// We should add examples in this section

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

Expand All @@ -212,84 +295,23 @@ Conversely, the following operator is available on array fields, and accepts a s

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

== Filtering spatial types

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:

[source, graphql, indent=0]
----
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
location {
longitude
latitude
}
}
}
----

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

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

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
}
}
}
----

== Querying an interface
== 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 +328,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 +358,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 +404,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 +489,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