From aa734126ad548a109f224f736781174ab2280143 Mon Sep 17 00:00:00 2001 From: Richard Sill Date: Tue, 1 Apr 2025 15:07:52 +0200 Subject: [PATCH] moved the filtering page to make it more prominent, tidied up the queries-aggregations section a bit --- modules/ROOT/content-nav.adoc | 6 +- .../{queries-aggregations => }/filtering.adoc | 14 +- modules/ROOT/pages/index.adoc | 4 +- modules/ROOT/pages/neo4jgraphql-class.adoc | 2 +- modules/ROOT/pages/ogm/reference.adoc | 12 +- .../queries-aggregations/aggregations.adoc | 2 +- .../pages/queries-aggregations/index.adoc | 5 +- .../queries-aggregations/pagination.adoc | 196 ++++++++++++++++++ .../pagination/cursor-based.adoc | 91 -------- .../pagination/index.adoc | 9 - .../pagination/offset-based.adoc | 86 -------- .../ROOT/pages/security/authorization.adoc | 3 + modules/ROOT/pages/types/scalar.adoc | 2 +- modules/ROOT/pages/types/spatial.adoc | 4 +- 14 files changed, 225 insertions(+), 211 deletions(-) rename modules/ROOT/pages/{queries-aggregations => }/filtering.adoc (95%) create mode 100644 modules/ROOT/pages/queries-aggregations/pagination.adoc delete mode 100644 modules/ROOT/pages/queries-aggregations/pagination/cursor-based.adoc delete mode 100644 modules/ROOT/pages/queries-aggregations/pagination/index.adoc delete mode 100644 modules/ROOT/pages/queries-aggregations/pagination/offset-based.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index c60e3931..12f80c13 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -20,6 +20,7 @@ ** xref:types/interfaces.adoc[Interfaces] ** xref:types/unions.adoc[Union] ** xref:types/relationships.adoc[] +* xref:filtering.adoc[] * xref:neo4jgraphql-class.adoc[] * xref:directives/index.adoc[] ** xref:directives/database-mapping.adoc[] @@ -35,11 +36,8 @@ * xref:queries-aggregations/index.adoc[Queries and aggregations] ** xref:queries-aggregations/queries.adoc[] ** xref:queries-aggregations/aggregations.adoc[] -** xref:queries-aggregations/filtering.adoc[] ** xref:queries-aggregations/sorting.adoc[] -** xref:queries-aggregations/pagination/index.adoc[] -*** xref:queries-aggregations/pagination/offset-based.adoc[] -*** xref:queries-aggregations/pagination/cursor-based.adoc[] +** xref:queries-aggregations/pagination.adoc[] * xref:mutations/index.adoc[] ** xref:mutations/create.adoc[] diff --git a/modules/ROOT/pages/queries-aggregations/filtering.adoc b/modules/ROOT/pages/filtering.adoc similarity index 95% rename from modules/ROOT/pages/queries-aggregations/filtering.adoc rename to modules/ROOT/pages/filtering.adoc index b01f9a1b..da60b220 100644 --- a/modules/ROOT/pages/queries-aggregations/filtering.adoc +++ b/modules/ROOT/pages/filtering.adoc @@ -1,11 +1,13 @@ [[filtering]] = Filtering -:page-aliases: filtering.adoc +:page-aliases: queries-aggregations/filtering.adoc :description: This page describes filtering operators. +You can apply filters when you query or aggregate data as well as use filtering rules for xref::/security/authorization.adoc#_filtering_rules[authorization]. + 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[]). +Operators can either be standalone operators (see xref:#_boolean_operators[]) or they are appended to field names (for example, xref:#_string_comparison[]). All operators can be combined using the Boolean operators `AND`, `OR`, and `NOT`. @@ -42,7 +44,7 @@ query { } ---- -`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]. +`name_CONTAINS` and `name_ENDS_WITH` are xref:#_string_comparison[String comparisons] while `movies_SOME` is a xref:#_relationship_filtering[relationship filter]. === Equality operators @@ -60,7 +62,7 @@ query { } ---- -For non-equality, you must use the xref:/queries-aggregations/filtering.adoc#_boolean_operators[`NOT`] logical operator. +For non-equality, you must use the xref:#_boolean_operators[`NOT`] logical operator. .Filtering all users which are not named John [source, graphql, indent=0] @@ -101,11 +103,11 @@ 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. +See xref:filtering.adoc#_spatial_type_filtering[] 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. +Both the `Point` and the `CartesianPoint` types use xref:#_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. diff --git a/modules/ROOT/pages/index.adoc b/modules/ROOT/pages/index.adoc index 60c0e96b..f3c7e559 100644 --- a/modules/ROOT/pages/index.adoc +++ b/modules/ROOT/pages/index.adoc @@ -27,9 +27,9 @@ For every query and mutation that is executed against this generated schema, the - xref::/types/index.adoc[Types], including temporal and spatial. - Support for both node and relationship properties. - Extensibility through the xref::/directives/custom-logic.adoc#_cypher[`@cypher` directive] and/or xref::/directives/custom-logic.adoc#_customresolver[Custom Resolvers]. -- Extensive xref::queries-aggregations/filtering.adoc[Filtering] and xref::queries-aggregations/sorting.adoc[Sorting] options. +- Extensive xref::filtering.adoc[Filtering] and xref::queries-aggregations/sorting.adoc[Sorting] options. - Options for xref::/directives/database-mapping.adoc[Database mapping] and value xref::/directives/autogeneration.adoc[Autogeneration]. -- xref::/queries-aggregations/pagination/index.adoc[Pagination] options. +- xref::/queries-aggregations/pagination.adoc[Pagination] options. - xref::/security/index.adoc[Security options] and additional xref::schema-configuration/index.adoc[Schema Configuration]. - An xref::ogm/index.adoc[OGM] (Object Graph Mapper) for programmatic interaction with your GraphQL API. - A xref::getting-started/toolbox.adoc[Toolbox] (UI) to experiment with your Neo4j GraphQL API on Neo4j Desktop. diff --git a/modules/ROOT/pages/neo4jgraphql-class.adoc b/modules/ROOT/pages/neo4jgraphql-class.adoc index 6c5afa20..87ca2525 100644 --- a/modules/ROOT/pages/neo4jgraphql-class.adoc +++ b/modules/ROOT/pages/neo4jgraphql-class.adoc @@ -53,7 +53,7 @@ export type Neo4jFeaturesSettings = { Use `Neo4jFiltersSettings` to enable numeric String comparisons and regular expression filters for Strings and IDs. They are disabled by default. -See xref:queries-aggregations/filtering.adoc#_string_comparison[String comparison] and xref:queries-aggregations/filtering.adoc#_regex_matching[RegEx matching]. +See xref:filtering.adoc#_string_comparison[String comparison] and xref:filtering.adoc#_regex_matching[RegEx matching]. === `populatedBy` settings diff --git a/modules/ROOT/pages/ogm/reference.adoc b/modules/ROOT/pages/ogm/reference.adoc index 4a2bd2e2..d9e2ca04 100644 --- a/modules/ROOT/pages/ogm/reference.adoc +++ b/modules/ROOT/pages/ogm/reference.adoc @@ -200,7 +200,7 @@ This method can be used to aggregate nodes, and maps to the underlying schema xr |`where` |`GraphQLWhereArg` -|A JavaScript object representation of the GraphQL `where` input type used for xref::queries-aggregations/filtering.adoc[Filtering]. +|A JavaScript object representation of the GraphQL `where` input type used for xref::filtering.adoc[Filtering]. |=== ==== Example @@ -310,7 +310,7 @@ It returns a `Promise` which resolves to a `DeleteInfo` object: |`where` |`GraphQLWhereArg` -|A JavaScript object representation of the GraphQL `where` input type used for xref::queries-aggregations/filtering.adoc[Filtering]. +|A JavaScript object representation of the GraphQL `where` input type used for xref::filtering.adoc[Filtering]. |`delete` |`string` or `DocumentNode` or `SelectionSetNode` @@ -350,11 +350,11 @@ It returns a `Promise` which resolves to an array of objects matching the type o |`where` |`GraphQLWhereArg` -|A JavaScript object representation of the GraphQL `where` input type used for xref::queries-aggregations/filtering.adoc[Filtering]. +|A JavaScript object representation of the GraphQL `where` input type used for xref::filtering.adoc[Filtering]. |`options` |`GraphQLOptionsArg` -|A JavaScript object representation of the GraphQL `options` input type used for xref::queries-aggregations/sorting.adoc[Sorting] and xref::/queries-aggregations/pagination/index.adoc[Pagination]. +|A JavaScript object representation of the GraphQL `options` input type used for xref::queries-aggregations/sorting.adoc[Sorting] and xref::/queries-aggregations/pagination.adoc[Pagination]. |`selectionSet` |`string` or `DocumentNode` or `SelectionSetNode` @@ -408,7 +408,7 @@ It returns a `Promise` that resolves to the equivalent of the mutation response |`where` |`GraphQLWhereArg` -|A JavaScript object representation of the GraphQL `where` input type used for xref::queries-aggregations/filtering.adoc[Filtering]. +|A JavaScript object representation of the GraphQL `where` input type used for xref::filtering.adoc[Filtering]. |`update` |`any` @@ -428,7 +428,7 @@ It returns a `Promise` that resolves to the equivalent of the mutation response |`options` |`GraphQLOptionsArg` -|A JavaScript object representation of the GraphQL `options` input type used for xref::queries-aggregations/sorting.adoc[Sorting] and xref::/queries-aggregations/pagination/index.adoc[Pagination]. +|A JavaScript object representation of the GraphQL `options` input type used for xref::queries-aggregations/sorting.adoc[Sorting] and xref::/queries-aggregations/pagination.adoc[Pagination]. |`selectionSet` |`string` or `DocumentNode` or `SelectionSetNode` diff --git a/modules/ROOT/pages/queries-aggregations/aggregations.adoc b/modules/ROOT/pages/queries-aggregations/aggregations.adoc index 7d5ce2ee..a781c143 100644 --- a/modules/ROOT/pages/queries-aggregations/aggregations.adoc +++ b/modules/ROOT/pages/queries-aggregations/aggregations.adoc @@ -97,7 +97,7 @@ query { [NOTE] ==== -The argument `where` can also be used in aggregation queries for xref::queries-aggregations/filtering.adoc[filtering] data. +The argument `where` can also be used in aggregation queries for xref::filtering.adoc[filtering] data. ==== == Aggregate related nodes diff --git a/modules/ROOT/pages/queries-aggregations/index.adoc b/modules/ROOT/pages/queries-aggregations/index.adoc index e7cd86bf..c8533667 100644 --- a/modules/ROOT/pages/queries-aggregations/index.adoc +++ b/modules/ROOT/pages/queries-aggregations/index.adoc @@ -10,6 +10,7 @@ This section addresses the following topics: * xref:queries-aggregations/queries.adoc[Queries] - Read or fetch values. * xref:queries-aggregations/aggregations.adoc[Aggregations] - Combine lists of types from different sources into a single list. -* xref:queries-aggregations/filtering.adoc[Filtering] - Filter query results to find objects. * xref:queries-aggregations/sorting.adoc[Sorting] - Sort query results by individual fields. -* xref:queries-aggregations/pagination/index.adoc[Pagination] - Navigate result pages. +* xref:queries-aggregations/pagination.adoc[Pagination] - Navigate result pages. + +Also, refer to xref:filtering.adoc[Filtering] for details on how to filter query results to find objects. \ No newline at end of file diff --git a/modules/ROOT/pages/queries-aggregations/pagination.adoc b/modules/ROOT/pages/queries-aggregations/pagination.adoc new file mode 100644 index 00000000..30a7e247 --- /dev/null +++ b/modules/ROOT/pages/queries-aggregations/pagination.adoc @@ -0,0 +1,196 @@ +[[pagination]] += Pagination +:page-aliases: pagination/index.adoc, pagination/offset-based.adoc, pagination/cursor-based.adoc, queries-aggregation/pagination/index.adoc, queries-aggregation/pagination/offset-based.adoc, queries-aggregation/pagination/cursor-based.adoc + + +The Neo4j GraphQL Library offers two mechanisms for pagination: + +* Offset-based pagination - Pagination based on offsets, often associated with navigation via pages. +* Cursor-based pagination - Pagination based on cursors, often associated with infinitely-scrolling applications. + + +[[pagination-offset-based]] +== Offset-based pagination + +Offset-based pagination, often associated with navigation via pages, can be achieved through the use of the `offset` and `limit` options available when querying for data. + +Using the following type definition: + +[source, graphql, indent=0] +---- +type User { + name: String! +} +---- + +Fetch the first page of 10 by executing: + +[source, graphql, indent=0] +---- +query { + users(options: { + limit: 10 + }) { + name + } +} +---- + +And then on subsequent calls, introduce the `offset` argument and increment it by 10 on each call. + +Fetch the second page with: + +[source, graphql, indent=0] +---- +query { + users(options: { + offset: 10 + limit: 10 + }) { + name + } +} +---- + +Fetch the third page with: + +[source, graphql, indent=0] +---- +query { + users(options: { + offset: 20 + limit: 10 + }) { + name + } +} +---- + +And so on. + + +=== Total number of pages + +You can fetch the total number of records for a certain type using its count query, and then divide that number by your entries per page in order to calculate the total number of pages. +This determines what the last page is, and whether there is a next page. + +See xref::queries-aggregations/aggregations.adoc#_aggregate_related_nodes[count aggregation queries] for details on how to execute these queries. + + +=== Paginating relationship fields + +Say that in addition to the `User` type above, there is also a `Post` type which a `User` has many of. +You can also fetch a `User` and then paginate through their posts: + +[source, graphql, indent=0] +---- +query { + users(where: { + name: "Billy" + }) { + name + posts(options: { + offset: 20 + limit: 10 + }) { + content + } + } +} +---- + + +[[pagination-cursor-based]] +== Cursor-based pagination + +On relationship fields, you are able to take advantage of cursor-based pagination, which is often associated with infinitely-scrolling applications. + +Using the following type definition: + +[source, graphql, indent=0] +---- +type User { + name: String! + posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) +} + +type Post { + content: String! +} +---- + +If you wanted to fetch the posts of user "John Smith" 10 at a time, you would first fetch 10: + +[source, graphql, indent=0] +---- +query { + users(where: { name: "John Smith" }) { + name + postsConnection(first: 10) { + edges { + node { + content + } + } + pageInfo { + endCursor + hasNextPage + } + } + } +} +---- + +In the return value, if `hasNextPage` is `true`, you would pass `endCursor` into the next query of 10. +You can do this with a variable, for example, `$after` in the following query: + +[source, graphql, indent=0] +---- +query Users($after: String) { + users(where: { name: "John Smith" }) { + name + postsConnection(first: 10, after: $after) { + edges { + node { + content + } + } + pageInfo { + endCursor + hasNextPage + } + } + } +} +---- + +You may continue until `hasNextPage` is `false` in the return - this is when you have reached the end of the data. + + +=== `totalCount` + +The Connection fields also offer a `totalCount` field which can be used to calculate page numbers. +This is useful if you want to paginate by cursor but use page numbers in your application. + +Add the `totalCount` field which returns the total number of results matching the filter used, for example: + +[source, graphql, indent=0] +---- +query Users($after: String) { + users(where: { name: "John Smith" }) { + name + postsConnection(first: 10) { + edges { + node { + content + } + } + pageInfo { + endCursor + hasNextPage + } + totalCount + } + } +} +---- diff --git a/modules/ROOT/pages/queries-aggregations/pagination/cursor-based.adoc b/modules/ROOT/pages/queries-aggregations/pagination/cursor-based.adoc deleted file mode 100644 index db85f7a4..00000000 --- a/modules/ROOT/pages/queries-aggregations/pagination/cursor-based.adoc +++ /dev/null @@ -1,91 +0,0 @@ -[[pagination-cursor-based]] -= Cursor-based pagination -:page-aliases: pagination/cursor-based.adoc - - -On relationship fields, you are able to take advantage of cursor-based pagination, which is often associated with infinitely-scrolling applications. - -Using the following type definition: - -[source, graphql, indent=0] ----- -type User { - name: String! - posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) -} - -type Post { - content: String! -} ----- - -If you wanted to fetch the posts of user "John Smith" 10 at a time, you would first fetch 10: - -[source, graphql, indent=0] ----- -query { - users(where: { name: "John Smith" }) { - name - postsConnection(first: 10) { - edges { - node { - content - } - } - pageInfo { - endCursor - hasNextPage - } - } - } -} ----- - -In the return value, if `hasNextPage` is `true`, you would pass `endCursor` into the next query of 10. You might do this using a variable as in the following example: - -[source, graphql, indent=0] ----- -query Users($after: String) { - users(where: { name: "John Smith" }) { - name - postsConnection(first: 10, after: $after) { - edges { - node { - content - } - } - pageInfo { - endCursor - hasNextPage - } - } - } -} ----- - -You would continue doing this until `hasNextPage` if `false` - this is when you have reached the end of the data. - -== `totalCount` - -The Connection fields also offer a `totalCount` field which can be used to calculate page numbers, which is useful if you want to page by cursors but use page number in your application. Using the example above, you would simply add the `totalCount` field which will return the total number of results matching the filter used, which in this example would just be all posts: - -[source, graphql, indent=0] ----- -query Users($after: String) { - users(where: { name: "John Smith" }) { - name - postsConnection(first: 10) { - edges { - node { - content - } - } - pageInfo { - endCursor - hasNextPage - } - totalCount - } - } -} ----- diff --git a/modules/ROOT/pages/queries-aggregations/pagination/index.adoc b/modules/ROOT/pages/queries-aggregations/pagination/index.adoc deleted file mode 100644 index 6d0bedc6..00000000 --- a/modules/ROOT/pages/queries-aggregations/pagination/index.adoc +++ /dev/null @@ -1,9 +0,0 @@ -[[pagination]] -= Pagination -:page-aliases: pagination/index.adoc - - -The Neo4j GraphQL Library offers two mechanisms for pagination: - -- xref::/queries-aggregations/pagination/offset-based.adoc[Offset-based pagination] - Pagination based on offsets, often associated with navigation via pages. -- xref::/queries-aggregations/pagination/cursor-based.adoc[Cursor-based pagination] - Pagination based on cursors, often associated with infinitely-scrolling applications. diff --git a/modules/ROOT/pages/queries-aggregations/pagination/offset-based.adoc b/modules/ROOT/pages/queries-aggregations/pagination/offset-based.adoc deleted file mode 100644 index f73d5581..00000000 --- a/modules/ROOT/pages/queries-aggregations/pagination/offset-based.adoc +++ /dev/null @@ -1,86 +0,0 @@ -[[pagination-offset-based]] -= Offset-based pagination -:page-aliases: pagination/offset-based.adoc - -Offset-based pagination, often associated with navigation via pages, can be achieved through the use of the `offset` and `limit` options available when querying for data. - -Using the following type definition: - -[source, graphql, indent=0] ----- -type User { - name: String! -} ----- - -You would fetch the first "page" of 10 by executing: - -[source, graphql, indent=0] ----- -query { - users(options: { - limit: 10 - }) { - name - } -} ----- - -And then on subsequent calls, introduce the `offset` argument and increment it by 10 on each call. - -*Page 2:* - -[source, graphql, indent=0] ----- -query { - users(options: { - offset: 10 - limit: 10 - }) { - name - } -} ----- - -*Page 3:* - -[source, graphql, indent=0] ----- -query { - users(options: { - offset: 20 - limit: 10 - }) { - name - } -} ----- - -And so on, so forth. - -== Total number of pages - -You can fetch the total number of records for a certain type using its count query, and then divide that number by your entries per page in order to calculate the total number of pages. This will allow to to determine what the last page is, and whether there is a next page. - -See xref::queries-aggregations/queries.adoc[Count] queries for details on how to execute these queries. - -== Paginating relationship fields - -Say that in addition to the `User` type above, there is also a `Post` type which a `User` has many of. You can also fetch a `User` and then paginate through their posts: - -[source, graphql, indent=0] ----- -query { - users(where: { - name: "Billy" - }) { - name - posts(options: { - offset: 20 - limit: 10 - }) { - content - } - } -} ----- diff --git a/modules/ROOT/pages/security/authorization.adoc b/modules/ROOT/pages/security/authorization.adoc index 3c6a2d67..c647260e 100644 --- a/modules/ROOT/pages/security/authorization.adoc +++ b/modules/ROOT/pages/security/authorization.adoc @@ -21,6 +21,9 @@ Instead, use xref::/security/subscriptions-authorization.adoc[`@subscriptionsAut Filtering rules filter out data which users do not have access to, without throwing any errors. These rules are translated into filtering predicates, which are evaluated against matched data in the database. +Filters for such rules work just like filters for queries and aggregations. +Refer to xref:filtering.adoc[]. + Filtering rules protect data as well as obfuscate the information on the _existence_ of that data to unauthorized users. For instance, here is how to filter out `Post` nodes which don't belong to the current `User`: diff --git a/modules/ROOT/pages/types/scalar.adoc b/modules/ROOT/pages/types/scalar.adoc index 438edb11..2e04dbbf 100644 --- a/modules/ROOT/pages/types/scalar.adoc +++ b/modules/ROOT/pages/types/scalar.adoc @@ -24,7 +24,7 @@ type Person { .2+| `BigInt` | Supports up to 64 bit integers, serialized as strings in variables and in data responses. -Shares the same xref::queries-aggregations/filtering.adoc#_numerical_operators[Numerical operators] as the other numeric types. +Shares the same xref::filtering.adoc#_numerical_operators[Numerical operators] as the other numeric types. a| [source, graphql, indent=0] ---- diff --git a/modules/ROOT/pages/types/spatial.adoc b/modules/ROOT/pages/types/spatial.adoc index e9df8501..ca7a2b51 100644 --- a/modules/ROOT/pages/types/spatial.adoc +++ b/modules/ROOT/pages/types/spatial.adoc @@ -25,7 +25,7 @@ type TypeWithPoint { The `Point` type is automatically added to your schema, in addition to the input and output types that you need to query and manipulate spatial types through your API. -See xref::queries-aggregations/filtering.adoc#_filtering_spatial_types[Filtering spatial types] for filter options. +See xref::filtering.adoc#_filtering_spatial_types[Filtering spatial types] for filter options. ==== Type definition @@ -100,7 +100,7 @@ type TypeWithCartesianPoint { The `CartesianPoint` type is automatically added to your schema, in addition to the input and output types that you need to query and manipulate spatial types through your API. -See xref::queries-aggregations/filtering.adoc#_filtering_spatial_types[Filtering spatial types] for filter options. +See xref::filtering.adoc#_filtering_spatial_types[Filtering spatial types] for filter options. ==== Type definition