From 5b1c6285c407c6f62a8e584e2a72cd9bd4379803 Mon Sep 17 00:00:00 2001 From: Richard Sill Date: Fri, 4 Oct 2024 17:57:25 +0200 Subject: [PATCH 1/4] rephrasing and section shifting --- .../pages/queries-aggregations/filtering.adoc | 85 +++++++++++-------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/modules/ROOT/pages/queries-aggregations/filtering.adoc b/modules/ROOT/pages/queries-aggregations/filtering.adoc index 4f56ddbe..e86b5dc5 100644 --- a/modules/ROOT/pages/queries-aggregations/filtering.adoc +++ b/modules/ROOT/pages/queries-aggregations/filtering.adoc @@ -3,12 +3,46 @@ :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. + +Operators can either be standalone operators (see xref:#_boolean_operators) or they are appended to field names (for example, xref:/queries-aggregations/filtering.adoc#_boolean_operators[`NOT`]). + +All operators can be combined using the Boolean operators `AND`, `OR`, and `NOT`. + +== 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 by the name of either "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 + } + } +} +---- == Equality operators -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 types can be tested for equality or non-equality. For example: .Filtering all users named John @@ -21,6 +55,18 @@ 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. @@ -52,39 +98,6 @@ 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 - -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. - -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. - -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: - -[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 - } - } -} ----- - == String comparison From d6c06853315494f218ef3d09a1b67c08b8c9324d Mon Sep 17 00:00:00 2001 From: Richard Sill Date: Tue, 8 Oct 2024 10:17:50 +0200 Subject: [PATCH 2/4] navigation structure, text updates --- .../pages/queries-aggregations/filtering.adoc | 181 +++++++++--------- 1 file changed, 95 insertions(+), 86 deletions(-) diff --git a/modules/ROOT/pages/queries-aggregations/filtering.adoc b/modules/ROOT/pages/queries-aggregations/filtering.adoc index e86b5dc5..ebf7cd5e 100644 --- a/modules/ROOT/pages/queries-aggregations/filtering.adoc +++ b/modules/ROOT/pages/queries-aggregations/filtering.adoc @@ -5,16 +5,18 @@ 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. -Operators can either be standalone operators (see xref:#_boolean_operators) or they are appended to field names (for example, xref:/queries-aggregations/filtering.adoc#_boolean_operators[`NOT`]). +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`. -== Boolean operators +== 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 by the name of either "Keanu" or not belonging to the "Pantoliano" family, who played in "The Matrix" movie, here is how you query that: +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] ---- @@ -40,7 +42,9 @@ query { } ---- -== Equality operators +`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: @@ -72,7 +76,7 @@ query { 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: @@ -98,10 +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. +==== 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. + +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 + } + } +} +---- + +== Type comparison -== String comparison +=== String comparison -The following case-sensitive comparison operators are only available for use on `String` and `ID` types: +The following case-sensitive comparison operators are available for `String` and `ID` types: * `_STARTS_WITH` * `_ENDS_WITH` @@ -154,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] ---- @@ -213,7 +280,7 @@ const features = { const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver }); ---- -== Array comparison +=== Array comparison The following operator is available on non-array fields, and accepts an array argument: @@ -225,84 +292,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] ---- @@ -319,10 +325,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 @@ -347,8 +355,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] @@ -392,8 +401,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: @@ -477,7 +486,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. From dd9c6bc631d02ece5470fbfe369a064cf7851cf6 Mon Sep 17 00:00:00 2001 From: Richard Sill Date: Tue, 8 Oct 2024 10:24:31 +0200 Subject: [PATCH 3/4] added two comments --- modules/ROOT/pages/queries-aggregations/filtering.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ROOT/pages/queries-aggregations/filtering.adoc b/modules/ROOT/pages/queries-aggregations/filtering.adoc index ebf7cd5e..813ef97d 100644 --- a/modules/ROOT/pages/queries-aggregations/filtering.adoc +++ b/modules/ROOT/pages/queries-aggregations/filtering.adoc @@ -263,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] ---- @@ -282,6 +283,8 @@ const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver }); === Array comparison +// We should add examples in this section + The following operator is available on non-array fields, and accepts an array argument: * `_IN` From 6ca8de461e1a35d30794015b760f6103bb998b84 Mon Sep 17 00:00:00 2001 From: Richard Sill Date: Tue, 8 Oct 2024 16:44:53 +0200 Subject: [PATCH 4/4] review suggestions --- .../pages/queries-aggregations/filtering.adoc | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/modules/ROOT/pages/queries-aggregations/filtering.adoc b/modules/ROOT/pages/queries-aggregations/filtering.adoc index 813ef97d..d1c62515 100644 --- a/modules/ROOT/pages/queries-aggregations/filtering.adoc +++ b/modules/ROOT/pages/queries-aggregations/filtering.adoc @@ -3,7 +3,7 @@ :page-aliases: filtering.adoc :description: This page describes filtering operators. -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. +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. 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[]). @@ -47,6 +47,7 @@ query { === Equality operators All types can be tested for equality or non-equality. + For example: .Filtering all users named John @@ -252,8 +253,8 @@ For `ID`: ---- const features = { filters: { - String: { - ID: true, + ID: { + MATCHES: true, } } }; @@ -263,8 +264,6 @@ 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] ---- const features = { @@ -283,17 +282,54 @@ const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver }); === Array comparison -// We should add examples in this section +Consider the following type definitions: + +[source, graphql, indent=0] +---- +type Movie { + id: ID! + title: String! + genres: [String!] + year: Int! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) +} + +type Actor { + id: ID! + name: String! + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) +} +---- + +The `_IN` operator is available on non-array fields, and accepts an array argument: -The following operator is available on non-array fields, and accepts an array argument: +[source, graphql, indent=0] +---- +query { + movies(where: { year_IN: [1999, 2000, 2001] }) { + title + year + } +} +---- -* `_IN` +The query returns all movies released in the years 1999, 2000 and 2001. -Conversely, the following operator is available on array fields, and accepts a single argument: +Conversely, the `_INCLUDES` operator is available on array fields, and accepts a single argument: + +[source, graphql, indent=0] +---- +query { + movies(where: { genres_INCLUDES: "Action" }) { + title + genres + } +} +---- -* `_INCLUDES` +The query returns all movies which have "Action" as one of their genres. -These operators are available for all types apart from `Boolean`. +`_IN` and `_INCLUDES` are available for all types except `Boolean`. == Interface filtering