diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 090cfa01..361750a4 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -3,11 +3,36 @@ ** xref:getting-started/index.adoc[] *** xref:getting-started/toolbox.adoc[] +** Type definitions +*** xref:type-definitions/types/index.adoc[] +**** xref:type-definitions/types/scalar.adoc[Scalar] +**** xref:type-definitions/types/temporal.adoc[Temporal] +**** xref:type-definitions/types/spatial.adoc[Spatial] +**** xref:type-definitions/types/interfaces.adoc[Interface] +**** xref:type-definitions/types/unions.adoc[Union] +**** xref:type-definitions/types/relationships.adoc[] +*** xref:type-definitions/directives/index.adoc[] +**** xref:type-definitions/directives/basics.adoc[] +**** xref:type-definitions/directives/autogeneration.adoc[] +**** xref:type-definitions/directives/custom-directives.adoc[] +**** xref:type-definitions/directives/cypher.adoc[] +**** xref:type-definitions/directives/default-values.adoc[] +**** xref:type-definitions/directives/database-mapping.adoc[] +**** xref:type-definitions/directives/indexes-and-constraints.adoc[] + +** xref:schema-configuration/index.adoc[Schema configuration] +*** xref:schema-configuration/type-configuration.adoc[] +*** xref:schema-configuration/global-configuration.adoc[] +*** xref:schema-configuration/field-configuration.adoc[] + ** 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:mutations/index.adoc[] *** xref:mutations/create.adoc[] @@ -23,14 +48,11 @@ ** xref:custom-resolvers.adoc[] -** xref:pagination/index.adoc[] -*** xref:pagination/offset-based.adoc[] -*** xref:pagination/cursor-based.adoc[] - ** xref:authentication-and-authorization/index.adoc[] *** xref:authentication-and-authorization/configuration.adoc[] *** xref:authentication-and-authorization/authentication.adoc[] *** xref:authentication-and-authorization/authorization.adoc[] +*** xref:authentication-and-authorization/impersonation-and-user-switching.adoc[] ** xref:introspector.adoc[Introspector] @@ -50,31 +72,7 @@ *** xref:ogm/type-generation.adoc[] *** xref:ogm/reference.adoc[] -** Reference -*** xref:reference/api-reference/index.adoc[] -**** xref:reference/api-reference/neo4jgraphql.adoc[] -**** xref:reference/api-reference/ogm.adoc[] -*** xref:reference/type-definitions/index.adoc[] -**** xref:reference/type-definitions/indexes-and-constraints.adoc[] -**** xref:reference/type-definitions/interfaces.adoc[] -**** xref:reference/type-definitions/relationships.adoc[] -**** xref:reference/type-definitions/types.adoc[] -**** xref:reference/type-definitions/unions.adoc[] - -*** xref:reference/directives/index.adoc[] -**** xref:reference/directives/schema-configuration/index.adoc[] -***** xref:reference/directives/schema-configuration/type-configuration.adoc[] -***** xref:reference/directives/schema-configuration/global-configuration.adoc[] -***** xref:reference/directives/schema-configuration/field-configuration.adoc[] - -**** xref:reference/directives/autogeneration.adoc[] -**** xref:reference/directives/basics.adoc[] -**** xref:reference/directives/custom-directives.adoc[] -**** xref:reference/directives/cypher.adoc[] -**** xref:reference/directives/default-values.adoc[] -**** xref:reference/directives/database-mapping.adoc[] - -*** xref:reference/driver-configuration.adoc[] +*** xref:driver-configuration.adoc[] ** Frameworks and integrations *** xref:integrations/apollo-federation.adoc[] diff --git a/modules/ROOT/pages/authentication-and-authorization/impersonation-and-user-switching.adoc b/modules/ROOT/pages/authentication-and-authorization/impersonation-and-user-switching.adoc new file mode 100644 index 00000000..5ec4da36 --- /dev/null +++ b/modules/ROOT/pages/authentication-and-authorization/impersonation-and-user-switching.adoc @@ -0,0 +1,198 @@ += Impersonation and user switching + +Impersonation and user switching are features of the Neo4j database and driver which allow for query execution in a different context to the initial connection. + +== Impersonation + +Impersonation still authenticates with the database as the original configured user, but runs the query in the context of an impersonated user. +When impersonating a user, the query is run within the complete security context of the impersonated user and not the authenticated user (home database, permissions, etc.). + +An example of how to impersonate a different user per request can be found below. In this example, the user to impersonate is taken from a HTTP header `User`: + +.TypeScript +[%collapsible] +==== +[source, typescript, indent=0] +---- +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { Neo4jGraphQL, Neo4jGraphQLContext } from "@neo4j/graphql"; +import neo4j from "neo4j-driver"; + +const typeDefs = `#graphql + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "neo4j://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neo4jgraphql = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +const schema = await neo4jgraphql.getSchema(); + +const server = new ApolloServer({ + schema, +}); + +const { url } = await startStandaloneServer(server, { + // Your async context function should async and return an object + context: async ({ req }) => ({ + sessionConfig: { + impersonatedUser: req.headers.user, + }, + }), +}); + +console.log(`πŸš€ Server ready at: ${url}`); +---- +==== + +.JavaScript +[%collapsible] +==== +[source, javascript, indent=0] +---- +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { Neo4jGraphQL } from "@neo4j/graphql"; +import neo4j from "neo4j-driver"; + +const typeDefs = `#graphql + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "neo4j://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neo4jgraphql = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +const schema = await neo4jgraphql.getSchema(); + +const server = new ApolloServer({ + schema, +}); + +const { url } = await startStandaloneServer(server, { + // Your async context function should async and return an object + context: async ({ req }) => ({ + sessionConfig: { + impersonatedUser: req.headers.user, + }, + }), +}); + +console.log(`πŸš€ Server ready at: ${url}`); +---- +==== + +== User switching + +User switching completely switches the user authenticating with the database for the given session, without the performance cost of instantiating an entire new driver instance. + +An example of configuring user switching on a per request basis can be found in the example below. Note that the username and password are provided in HTTP headers `User` and `Password`, but this would not be recommended for production use: + +.TypeScript +[%collapsible] +==== +[source, typescript, indent=0] +---- +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { Neo4jGraphQL, Neo4jGraphQLContext } from "@neo4j/graphql"; +import neo4j from "neo4j-driver"; + +const typeDefs = `#graphql + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "neo4j://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neo4jgraphql = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +const schema = await neo4jgraphql.getSchema(); + +const server = new ApolloServer({ + schema, +}); + +const { url } = await startStandaloneServer(server, { + // Your async context function should async and return an object + context: async ({ req }) => ({ + sessionConfig: { + auth: neo4j.auth.basic(req.headers.user, req.headers.password), + }, + }), +}); + +console.log(`πŸš€ Server ready at: ${url}`); +---- +==== + +.JavaScript +[%collapsible] +==== +[source, javascript, indent=0] +---- +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { Neo4jGraphQL } from "@neo4j/graphql"; +import neo4j from "neo4j-driver"; + +const typeDefs = `#graphql + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "neo4j://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neo4jgraphql = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +const schema = await neo4jgraphql.getSchema(); + +const server = new ApolloServer({ + schema, +}); + +const { url } = await startStandaloneServer(server, { + // Your async context function should async and return an object + context: async ({ req }) => ({ + sessionConfig: { + auth: neo4j.auth.basic(req.headers.user, req.headers.password), + }, + }), +}); + +console.log(`πŸš€ Server ready at: ${url}`); +---- +==== + + diff --git a/modules/ROOT/pages/reference/driver-configuration.adoc b/modules/ROOT/pages/driver-configuration.adoc similarity index 100% rename from modules/ROOT/pages/reference/driver-configuration.adoc rename to modules/ROOT/pages/driver-configuration.adoc diff --git a/modules/ROOT/pages/index.adoc b/modules/ROOT/pages/index.adoc index bdef8a5e..4ee3561d 100644 --- a/modules/ROOT/pages/index.adoc +++ b/modules/ROOT/pages/index.adoc @@ -23,13 +23,13 @@ It can generate an entire executable schema with all of the additional types nee For every query and mutation that is executed against this generated schema, the Neo4j GraphQL Library generates a single Cypher query which is executed against the database. This eliminates the https://www.google.com/search?q=graphql+n%2B1[N+1 Problem], which can make GraphQL implementations slow and inefficient. - Automatic generation of xref::queries-aggregations/queries.adoc[Queries] and xref::mutations/index.adoc[Mutations] for CRUD interactions. -- xref::reference/type-definitions/types.adoc[Types], including temporal and spatial. +- xref::/type-definitions/types/index.adoc[Types], including temporal and spatial. - Support for both node and relationship properties. -- Extensibility through the xref::reference/directives/cypher.adoc[`@cypher` directive] and/or xref::custom-resolvers.adoc[Custom Resolvers]. +- Extensibility through the xref::/type-definitions/directives/cypher.adoc[`@cypher` directive] and/or xref::custom-resolvers.adoc[Custom Resolvers]. - Extensive xref::queries-aggregations/filtering.adoc[Filtering] and xref::queries-aggregations/sorting.adoc[Sorting] options. -- Options for value xref::reference/directives/autogeneration.adoc[Autogeneration] and xref::reference/directives/default-values.adoc[Default Values]. -- xref::pagination/index.adoc[Pagination] options. -- xref::authentication-and-authorization/index.adoc[Authentication and authorization options] and additional xref::reference/directives/schema-configuration/index.adoc[Schema Configuration]. +- Options for value xref::/type-definitions/directives/autogeneration.adoc[Autogeneration] and xref::/type-definitions/directives/default-values.adoc[Default Values]. +- xref::/queries-aggregations/pagination/index.adoc[Pagination] options. +- xref::authentication-and-authorization/index.adoc[Authentication and authorization 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/migration/index.adoc b/modules/ROOT/pages/migration/index.adoc index 3c3e9e6f..4be46dc5 100644 --- a/modules/ROOT/pages/migration/index.adoc +++ b/modules/ROOT/pages/migration/index.adoc @@ -97,7 +97,7 @@ type NameOfTypeToExclude @exclude { name: String } ---- -For more information regarding the above used `@exclude` directive, see xref::reference/directives/schema-configuration/type-configuration.adoc#_exclude_deprecated[`@exclude`] +For more information regarding the above used `@exclude` directive, see xref::/schema-configuration/type-configuration.adoc#_exclude_deprecated[`@exclude`] === Database Configuration @@ -153,7 +153,7 @@ server.listen().then(({ url }) => { }); ---- -Database bookmarks are also supported. See xref::reference/driver-configuration.adoc[Driver Configuration] for more information. +Database bookmarks are also supported. See xref::driver-configuration.adoc[Driver Configuration] for more information. [[migration-guide-type-definitions]] == Type Definitions @@ -173,7 +173,7 @@ Migrating this directive is trivial: For example, `@relation(name: "ACTED_IN", direction: OUT)` becomes `@relationship(type: "ACTED_IN", direction: OUT)`. -See xref::reference/type-definitions/relationships.adoc[Relationships] for more information on relationships in `@neo4j/graphql`. +See xref::/type-definitions/types/relationships.adoc[Relationships] for more information on relationships in `@neo4j/graphql`. ==== Relationship Properties @@ -226,13 +226,13 @@ And note the following changes to the two node types: === `@cypher` -No change. See xref::reference/directives/cypher.adoc[`@cypher` directive] for more details on this directive in `@neo4j/graphql`. +No change. See xref::/type-definitions/directives/cypher.adoc[`@cypher` directive] for more details on this directive in `@neo4j/graphql`. ==== `@neo4j_ignore` `@neo4j/graphql` offers two directives for skipping autogeneration for specified types/fields: -* xref::reference/directives/schema-configuration/type-configuration.adoc#_exclude_deprecated[`@exclude`]: Skip generation of specified Query/Mutation fields for an object type +* xref::/schema-configuration/type-configuration.adoc#_exclude_deprecated[`@exclude`]: Skip generation of specified Query/Mutation fields for an object type * xref::custom-resolvers.adoc#custom-resolver-directive[`@customResolver`]: Ignore a field, which will need custom logic for resolution ==== `@isAuthenticated`, `@hasRole` and `@hasScope` @@ -245,7 +245,7 @@ Not supported at this time. ==== `@id` -There is an equivalent directive in the new library, but it does not work using database constraints as per the old library. See xref::reference/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`]. +There is an equivalent directive in the new library, but it does not work using database constraints as per the old library. See xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`]. ==== `@unique`, `@index` and `@search` @@ -255,7 +255,7 @@ These all relate to database indexes and constraints, which are not currently su ==== Scalar Types -Supported as you would expect, with additional xref::reference/type-definitions/types.adoc#type-definitions-types-bigint[`BigInt`] support for 64 bit integers. +Supported as you would expect, with additional xref::/type-definitions/types/scalar.adoc[`BigInt`] support for 64 bit integers. ==== Temporal Types (`DateTime`, `Date`) @@ -285,14 +285,14 @@ Has become: Due to the move to ISO 8601 strings, input types are no longer necessary for temporal instances, so `_Neo4jDateTimeInput` has become `DateTime` and `_Neo4jDateInput` has become `Date` for input. -See xref::reference/type-definitions/types.adoc#type-definitions-types-temporal[Temporal Types]. +See xref::/type-definitions/types/temporal.adoc[Temporal Types]. ==== Spatial Types The single type in `neo4j-graphql-js`, `Point`, has been split out into two types: -* xref::reference/type-definitions/types.adoc#type-definitions-types-point[`Point`] -* xref::reference/type-definitions/types.adoc#type-definitions-types-cartesian-point[`CartesianPoint`] +* xref::/type-definitions/types/spatial.adoc#_point[`Point`] +* xref::/type-definitions/types/spatial.adoc#_cartesianpoint[`CartesianPoint`] Correspondingly, `_Neo4jPointInput` has also been split out into two input types: @@ -303,11 +303,11 @@ Using them in Queries and Mutations should feel remarkably similar. ==== Interface Types -Supported, queryable using inline fragments as per `neo4j-graphql-js`, but can also be created using Nested Mutations. See xref::reference/type-definitions/interfaces.adoc[Interfaces]. +Supported, queryable using inline fragments as per `neo4j-graphql-js`, but can also be created using Nested Mutations. See xref::/type-definitions/types/interfaces.adoc[Interfaces]. ==== Union Types -Supported, queryable using inline fragments as per `neo4j-graphql-js`, but can also be created using Nested Mutations. See xref::reference/type-definitions/unions.adoc#type-definitions-unions[Unions]. +Supported, queryable using inline fragments as per `neo4j-graphql-js`, but can also be created using Nested Mutations. See xref::/type-definitions/types/unions.adoc#type-definitions-unions[Unions]. === Fields diff --git a/modules/ROOT/pages/migration/v3-migration.adoc b/modules/ROOT/pages/migration/v3-migration.adoc index c97eaa73..5050896d 100644 --- a/modules/ROOT/pages/migration/v3-migration.adoc +++ b/modules/ROOT/pages/migration/v3-migration.adoc @@ -201,12 +201,12 @@ To improve consistency, some automatically generated plurals (e.g. `createActors your types use conventions such as `snake_case`. Because of this, you may find generated queries and mutations may have different names. If you encounter this problem, -please update your clients to use the new query names or use the `plural` option in the xref::reference/directives/database-mapping.adoc#type-definitions-node[@node directive] +please update your clients to use the new query names or use the `plural` option in the xref::/type-definitions/directives/database-mapping.adoc#type-definitions-node[@node directive] to force a custom plural value. == Custom Directives Defining and applying custom directives has changed significantly, if you are using or plan to use custom directives, make -sure to check the up-to-date documentation on xref::reference/directives/custom-directives.adoc[custom directives]. +sure to check the up-to-date documentation on xref::/type-definitions/directives/custom-directives.adoc[custom directives]. == Types changes Some automatically generated types have changed to improve consistency. diff --git a/modules/ROOT/pages/migration/v4-migration/index.adoc b/modules/ROOT/pages/migration/v4-migration/index.adoc index eefc18e0..98c868b1 100644 --- a/modules/ROOT/pages/migration/v4-migration/index.adoc +++ b/modules/ROOT/pages/migration/v4-migration/index.adoc @@ -992,7 +992,7 @@ const neoSchema = new Neo4jGraphQL({ === Opt-in Aggregation Aggregation operations are no longer generated by default. -They can be enabled case by case using the directives xref::reference/directives/schema-configuration/type-configuration.adoc#_query[`@query`] and xref::reference/directives/schema-configuration/field-configuration.adoc#_relationship[`@relationship`]. +They can be enabled case by case using the directives xref::/schema-configuration/type-configuration.adoc#_query[`@query`] and xref::/schema-configuration/field-configuration.adoc#_relationship[`@relationship`]. You can enable the operation fields `actorsAggregate` and `actedInAggregate` like this: diff --git a/modules/ROOT/pages/mutations/create.adoc b/modules/ROOT/pages/mutations/create.adoc index 5fc4abff..8e5c0caf 100644 --- a/modules/ROOT/pages/mutations/create.adoc +++ b/modules/ROOT/pages/mutations/create.adoc @@ -42,7 +42,7 @@ This allows you to create not only the type in question, but to recurse down and [NOTE] ==== -The `id` field is absent from both `create` input types as the xref::reference/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] directive has been used. +The `id` field is absent from both `create` input types as the xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] directive has been used. ==== == Single `create` diff --git a/modules/ROOT/pages/mutations/update.adoc b/modules/ROOT/pages/mutations/update.adoc index 064c90c9..146c2ca4 100644 --- a/modules/ROOT/pages/mutations/update.adoc +++ b/modules/ROOT/pages/mutations/update.adoc @@ -53,7 +53,7 @@ type Mutation { [NOTE] ==== -The `id` field cannot be updated as the xref::reference/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] directive has been used. +The `id` field cannot be updated as the xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] directive has been used. ==== == Single `update` @@ -412,7 +412,7 @@ mutation { == Mathematical operators Mathematical operators can be used to update numerical fields based on their original values in a single DB transaction. -For that, specific operators are available on different numerical types: `Int`, `Float`, xref::reference/type-definitions/types.adoc#type-definitions-types-bigint[`BigInt`]. +For that, specific operators are available on different numerical types: `Int`, `Float`, xref::/type-definitions/types/scalar.adoc[`BigInt`]. They are supported within these entities: * Nodes diff --git a/modules/ROOT/pages/ogm/reference.adoc b/modules/ROOT/pages/ogm/reference.adoc index fb0493e3..a8aa0979 100644 --- a/modules/ROOT/pages/ogm/reference.adoc +++ b/modules/ROOT/pages/ogm/reference.adoc @@ -390,7 +390,7 @@ const users = await User.find({ where: { name: "Jane Smith" }}); |`options` + + Type: `GraphQLOptionsArg` -|A JavaScript object representation of the GraphQL `options` input type used for xref::queries-aggregations/sorting.adoc[Sorting] and xref::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/index.adoc[Pagination]. |`selectionSet` + + @@ -467,7 +467,7 @@ const { users } = await User.update({ |`options` + + Type: `GraphQLOptionsArg` -|A JavaScript object representation of the GraphQL `options` input type used for xref::queries-aggregations/sorting.adoc[Sorting] and xref::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/index.adoc[Pagination]. |`selectionSet` + + diff --git a/modules/ROOT/pages/pagination/index.adoc b/modules/ROOT/pages/pagination/index.adoc deleted file mode 100644 index 30c86bf1..00000000 --- a/modules/ROOT/pages/pagination/index.adoc +++ /dev/null @@ -1,7 +0,0 @@ -[[pagination]] -= Pagination - -The Neo4j GraphQL Library offers two mechanisms for pagination: - -- xref::pagination/offset-based.adoc[Offset-based pagination] - Pagination based on offsets, often associated with navigation via pages. -- xref::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/filtering.adoc b/modules/ROOT/pages/queries-aggregations/filtering.adoc index dc3e1e07..7efaa971 100644 --- a/modules/ROOT/pages/queries-aggregations/filtering.adoc +++ b/modules/ROOT/pages/queries-aggregations/filtering.adoc @@ -26,7 +26,7 @@ For the `Boolean` type, equality operators are the only ones available. == Numerical operators -These are the operators available for numeric (`Int`, `Float`, xref::reference/type-definitions/types.adoc#type-definitions-types-bigint[`BigInt`]), xref::reference/type-definitions/types.adoc#type-definitions-types-temporal[temporal] and xref::reference/type-definitions/types.adoc#type-definitions-types-spatial[spatial] types: +These are the operators available for numeric (`Int`, `Float`, xref::/type-definitions/types/scalar.adoc[`BigInt`]), xref::/type-definitions/types/temporal.adoc[temporal] and xref::/type-definitions/types/spatial.adoc[spatial] types: * `_LT` * `_LTE` @@ -48,7 +48,7 @@ query { ---- Spatial types use numerical filtering differently and they also have additional options. -See xref::/reference/type-definitions/types.adoc#_filtering[Spatial types filtering] for more information. +See xref::/type-definitions/types/spatial.adoc#_filtering[Spatial types filtering] for more information. These same operators are disabled by default in the case of String comparisons. To enable, explicitly add them in the features options: diff --git a/modules/ROOT/pages/queries-aggregations/index.adoc b/modules/ROOT/pages/queries-aggregations/index.adoc index af5d8d6b..337b1a6d 100644 --- a/modules/ROOT/pages/queries-aggregations/index.adoc +++ b/modules/ROOT/pages/queries-aggregations/index.adoc @@ -2,7 +2,7 @@ :description: This section describes queries and aggregations. = Queries and aggregations -Each node defined in xref::/reference/type-definitions/index.adoc[type definitions] has two query fields generated for it: one for *querying* data and another one for *aggregating* it. +Each node defined in type definitions has two query fields generated for it: one for *querying* data and another one for *aggregating* it. Each of these fields, by their part, accepts two arguments used for *filtering*, *sorting*, and *pagination*. This section addresses the following topics: diff --git a/modules/ROOT/pages/pagination/cursor-based.adoc b/modules/ROOT/pages/queries-aggregations/pagination/cursor-based.adoc similarity index 100% rename from modules/ROOT/pages/pagination/cursor-based.adoc rename to modules/ROOT/pages/queries-aggregations/pagination/cursor-based.adoc diff --git a/modules/ROOT/pages/queries-aggregations/pagination/index.adoc b/modules/ROOT/pages/queries-aggregations/pagination/index.adoc new file mode 100644 index 00000000..9377e39f --- /dev/null +++ b/modules/ROOT/pages/queries-aggregations/pagination/index.adoc @@ -0,0 +1,7 @@ +[[pagination]] += Pagination + +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/pagination/offset-based.adoc b/modules/ROOT/pages/queries-aggregations/pagination/offset-based.adoc similarity index 100% rename from modules/ROOT/pages/pagination/offset-based.adoc rename to modules/ROOT/pages/queries-aggregations/pagination/offset-based.adoc diff --git a/modules/ROOT/pages/queries-aggregations/queries.adoc b/modules/ROOT/pages/queries-aggregations/queries.adoc index ba46261a..0f0c2845 100644 --- a/modules/ROOT/pages/queries-aggregations/queries.adoc +++ b/modules/ROOT/pages/queries-aggregations/queries.adoc @@ -31,7 +31,7 @@ query { == Undirected queries -All xref:reference/type-definitions/relationships.adoc[relationships] are created with a direction from one node to another. +All xref::/type-definitions/types/relationships.adoc[relationships] are created with a direction from one node to another. By default, all queries follow the direction defined in the relationship. However, in some cases it is necessary to query for all related nodes, regardless of the direction of the relationship. This can be achieved with the argument `directed: false`. @@ -68,4 +68,4 @@ query Query { ---- Keep in mind that *undirected relationships are only supported in queries*. -The xref:reference/type-definitions/relationships.adoc#_querydirection[type definitions] for a relationship may define a different behavior, so the `directed` option may not be available in some cases. \ No newline at end of file +The xref::/type-definitions/types/relationships.adoc#_querydirection[type definitions] for a relationship may define a different behavior, so the `directed` option may not be available in some cases. \ No newline at end of file diff --git a/modules/ROOT/pages/reference/directives/basics.adoc b/modules/ROOT/pages/reference/directives/basics.adoc deleted file mode 100644 index 3a93f50f..00000000 --- a/modules/ROOT/pages/reference/directives/basics.adoc +++ /dev/null @@ -1,62 +0,0 @@ -[[type-definitions-basics]] -= Basics - -Each type in your GraphQL type definitions can be mapped to an entity in your Neo4j database. - -== Nodes - -The most basic mapping is of GraphQL types to Neo4j nodes, where the GraphQL type name maps to the Neo4j node label. - -For example, to represent a node with label "Movie" and a single property "title" of type string: - -[source, graphql, indent=0] ----- -type Movie { - title: String -} ----- - -== Relationships - -Relationships are represented by marking particular fields with a directive. This directive, `@relationship`, defines the relationship type in the database, as well as which direction that relationship goes in. - -Add a second node type, "Actor", and connect the two together: - -[source, graphql, indent=0] ----- -type Movie { - title: String - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) -} - -type Actor { - name: String - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) -} ----- - -Note there is a directive on each "end" of the relationship in this case, but it is not essential. - -=== Relationship properties - -In order to add relationship properties to a relationship, you need to add a new type to your type definitions, but this time it will be of type `interface`. This interface must be decorated with the `@relationshipProperties` directive. -For example, for your "ACTED_IN" relationship, add a property "roles": - -[source, graphql, indent=0] ----- -type Movie { - title: String - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") -} - -type Actor { - name: String - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") -} - -interface ActedIn @relationshipProperties { - roles: [String] -} ----- - -Note that in addition to this new interface type, there is an added a key `properties` in the existing `@relationship` directives. diff --git a/modules/ROOT/pages/reference/directives/cypher.adoc b/modules/ROOT/pages/reference/directives/cypher.adoc index 9a44e0bd..458e3aad 100644 --- a/modules/ROOT/pages/reference/directives/cypher.adoc +++ b/modules/ROOT/pages/reference/directives/cypher.adoc @@ -27,7 +27,7 @@ Global variables are available for use within the Cypher statement. The value `this` is a reference to the currently resolved node, and it can be used to traverse the graph. -This can be seen in the usage example xref::reference/directives/cypher.adoc#type-definitions-cypher-object-usage[On an object type field] below. +This can be seen in the usage example xref::/type-definitions/directives/cypher.adoc#type-definitions-cypher-object-usage[On an object type field] below. === `auth` @@ -225,4 +225,4 @@ type Mutation { columnName: "a" ) } ----- +---- \ No newline at end of file diff --git a/modules/ROOT/pages/reference/directives/default-values.adoc b/modules/ROOT/pages/reference/directives/default-values.adoc index 1202ee12..97c72d29 100644 --- a/modules/ROOT/pages/reference/directives/default-values.adoc +++ b/modules/ROOT/pages/reference/directives/default-values.adoc @@ -93,4 +93,4 @@ directive @limit( The directive has two arguments: * `default` - If no `limit` argument is passed to the query, the default limit will be used. The query may still pass a higher or lower `limit`. -* `max` - Defines the maximum limit to be passed to the query. If a higher value is passed, this will be used instead. If no `default` value is set, `max` will be used for queries without limit. +* `max` - Defines the maximum limit to be passed to the query. If a higher value is passed, this will be used instead. If no `default` value is set, `max` will be used for queries without limit. \ No newline at end of file diff --git a/modules/ROOT/pages/reference/directives/index.adoc b/modules/ROOT/pages/reference/directives/index.adoc index e7262e69..d35bba0c 100644 --- a/modules/ROOT/pages/reference/directives/index.adoc +++ b/modules/ROOT/pages/reference/directives/index.adoc @@ -5,13 +5,13 @@ The `@alias` directive will map a GraphQL schema field to a Neo4j property on a node or relationship. -Reference: xref::reference/directives/database-mapping.adoc#type-definitions-alias[`@alias`] +Reference: xref::/type-definitions/directives/database-mapping.adoc#type-definitions-alias[`@alias`] == `@coalesce` The `@coalesce` directive exposes a mechanism for querying against non-existent, `null` values on a node. -Reference: xref::reference/directives/default-values.adoc#type-definitions-default-values-coalesce[`@coalesce`] +Reference: xref::/type-definitions/directives/default-values.adoc#type-definitions-default-values-coalesce[`@coalesce`] [[custom-resolver-directive]] == `@customResolver` @@ -25,59 +25,59 @@ Reference: xref::custom-resolvers.adoc#custom-resolver-directive[`@customResolve The `@cypher` directive overrides field resolution (including `Query` and `Mutation` fields), instead resolving with the specified Cypher. -Reference: xref::reference/directives/cypher.adoc[`@cypher` directive] +Reference: xref::/type-definitions/directives/cypher.adoc[`@cypher` directive] == `@default` The `@default` directive allows for the setting of a default value for a field on object creation. -Reference: xref::reference/directives/default-values.adoc#type-definitions-default-values-default[`@default`] +Reference: xref::/type-definitions/directives/default-values.adoc#type-definitions-default-values-default[`@default`] == `@exclude` label:deprecated[] This directive is deprecated. -Use the xref:reference/directives/schema-configuration/type-configuration.adoc#_query[`@query`], xref:reference/directives/schema-configuration/type-configuration.adoc#_mutation[`@mutation`] and the xref:reference/directives/schema-configuration/type-configuration.adoc#_subscription[`@subscription`] directives instead. +Use the xref:schema-configuration/type-configuration.adoc#_query[`@query`], xref:schema-configuration/type-configuration.adoc#_mutation[`@mutation`] and the xref:schema-configuration/type-configuration.adoc#_subscription[`@subscription`] directives instead. The `@exclude` directive is used on object types to instruct them to be skipped during Query, Mutation and Subscription generation. -Reference: xref::reference/directives/schema-configuration/type-configuration.adoc#_exclude_deprecated[`@exclude`] +Reference: xref::schema-configuration/type-configuration.adoc#_exclude_deprecated[`@exclude`] == `@filterable` The `@filterable` directive defines the filters generated for a field. -Reference: xref:reference/directives/schema-configuration/field-configuration.adoc#_filterable[`@filterable`] +Reference: xref:schema-configuration/field-configuration.adoc#_filterable[`@filterable`] == `@fulltext` The `@fulltext` directive indicates that there should be a Fulltext index inserted into the database for the specified Node and its properties. -Reference: xref::reference/type-definitions/indexes-and-constraints.adoc#type-definitions-indexes-fulltext[Fulltext indexes] +Reference: xref::/type-definitions/directives/indexes-and-constraints.adoc#type-definitions-indexes-fulltext[Fulltext indexes] == `@id` The `@id` directive marks a field as the unique ID for an object type, and allows for autogeneration of IDs. -Reference: xref::reference/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] +Reference: xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] == `@limit` The `@limit` is to be used on nodes, and when applied will inject values into Cypher `LIMIT` clauses. -Reference: xref::reference/directives/default-values.adoc#type-definitions-default-values-limit[`@limit`] +Reference: xref::/type-definitions/directives/default-values.adoc#type-definitions-default-values-limit[`@limit`] == `@mutation` This directive is used to limit the availability of Mutation operations in the library. -Reference: xref:reference/directives/schema-configuration/type-configuration.adoc#_mutation[`@mutation`] +Reference: xref:schema-configuration/type-configuration.adoc#_mutation[`@mutation`] == `@node` The `@node` directive is used to specify the configuration of a GraphQL object type which represents a Neo4j node. -Reference: xref::reference/directives/database-mapping.adoc#type-definitions-node[`@node`] +Reference: xref::/type-definitions/directives/database-mapping.adoc#type-definitions-node[`@node`] [[plural-directive]] == `@plural` @@ -85,7 +85,7 @@ Reference: xref::reference/directives/database-mapping.adoc#type-definitions-nod The `@plural` directive redefines how to compose the plural of the type for the generated operations. This is particularly useful for types that are not correctly pluralized or are non-English words. -Reference: xref::reference/directives/database-mapping.adoc#type-definitions-plural[`@plural`] +Reference: xref::/type-definitions/directives/database-mapping.adoc#type-definitions-plural[`@plural`] [[populated-by-directive]] == `@populatedBy` @@ -93,7 +93,7 @@ Reference: xref::reference/directives/database-mapping.adoc#type-definitions-plu The `@populatedBy` directive is used to specify a callback function that gets executed during GraphQL query parsing, to populate fields which have not been provided within the input. -Reference: xref::reference/directives/autogeneration.adoc#type-definitions-autogeneration-populated-by[`@populatedBy`] +Reference: xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-populated-by[`@populatedBy`] == `@private` @@ -105,13 +105,13 @@ Reference: xref::ogm/private.adoc[`@private` Directive] This directive is used to limit the availability of Query operations in the library. -Reference: xref:reference/directives/schema-configuration/type-configuration.adoc#_query[`@query`] +Reference: xref:schema-configuration/type-configuration.adoc#_query[`@query`] == `@relationship` The `@relationship` directive is used to configure relationships between object types. -Reference: xref::reference/type-definitions/relationships.adoc[Relationships], xref::reference/directives/schema-configuration/field-configuration.adoc#_relationship[`@relationship`] +Reference: xref::/type-definitions/types/relationships.adoc[Relationships], xref::schema-configuration/field-configuration.adoc#_relationship[`@relationship`] == `@relationshipProperties` @@ -133,28 +133,28 @@ The `@relayId` directive can be used on object type fields, to flag which field The `@selectable` directive sets the availability of fields on queries and aggregations. -Reference: xref:reference/directives/schema-configuration/field-configuration.adoc#_selectable[`@selectable`] +Reference: xref:schema-configuration/field-configuration.adoc#_selectable[`@selectable`] == `@settable` The `@settable` directive sets the availability of fields on the create and update inputs. -Reference: xref:reference/directives/schema-configuration/field-configuration.adoc#_settable[`@settable`] +Reference: xref:schema-configuration/field-configuration.adoc#_settable[`@settable`] == `@subscription` This directive is used to limit Subscription events available in the library. -Reference: xref:reference/directives/schema-configuration/type-configuration.adoc#_subscription[`@subscription`] +Reference: xref:schema-configuration/type-configuration.adoc#_subscription[`@subscription`] == `@timestamp` The `@timestamp` directive flags fields to be used to store timestamps on create/update events. -Reference: xref::reference/directives/autogeneration.adoc#type-definitions-autogeneration-timestamp[`@timestamp`] +Reference: xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-timestamp[`@timestamp`] == `@unique` The `@unique` directive indicates that there should be a uniqueness constraint in the database for the fields that it is applied to. -Reference: xref::reference/type-definitions/indexes-and-constraints.adoc#type-definitions-constraints-unique[Unique node property constraints] +Reference: xref::/type-definitions/directives/indexes-and-constraints.adoc#type-definitions-constraints-unique[Unique node property constraints] diff --git a/modules/ROOT/pages/reference/directives/schema-configuration/index.adoc b/modules/ROOT/pages/reference/directives/schema-configuration/index.adoc deleted file mode 100644 index a4236d2c..00000000 --- a/modules/ROOT/pages/reference/directives/schema-configuration/index.adoc +++ /dev/null @@ -1,39 +0,0 @@ -[[type-definitions-schema-configuration]] -= Schema Configuration - -Neo4j GraphQL Library supports several functionalities such as CRUD operations, aggregation, filtering, and others. -To make them work, a large amount of GraphQL types are generated. - -In some cases, it may be advisable to reduce the scope of the API produced. -This section provides information on how to limit access to unwanted operations and reduce the size of the schema which can improve the performance. - -For instance, starting from the type definitions: - -[source, graphql, indent=0] ----- -type Movie { - title: String - length: Int -} ----- - -If you want to prevent users from querying `movies` in the Neo4j Database, you need to make the following the changes: - -[source, graphql, indent=0] ----- -type Movie @query(read: false, aggregate: false) { - title: String - length: Int -} ----- - -With this change, the library does **not** generate the following operation fields from the `Query` type: - -* `movies` -* `moviesAggregate` -* `moviesConnection` - -[NOTE] -==== -Aggregations will no longer be generated by default in the 4.0.0 version of the library. See xref:migration/v4-migration/index.adoc#opt-in-aggregation[`Opt-in Aggregation`] for more information. -==== diff --git a/modules/ROOT/pages/reference/type-definitions/index.adoc b/modules/ROOT/pages/reference/type-definitions/index.adoc deleted file mode 100644 index e1366fb2..00000000 --- a/modules/ROOT/pages/reference/type-definitions/index.adoc +++ /dev/null @@ -1,18 +0,0 @@ -[[type-definitions]] -= Type Definitions - -- xref::reference/directives/basics.adoc[Basics] - Learn how to define your nodes and relationships using GraphQL type definitions. -- xref::reference/type-definitions/types.adoc[Types] - Learn about the various data types available in the Neo4j GraphQL Library. -- xref::reference/type-definitions/unions.adoc[Unions] - Learn about GraphQL unions and how they map to the Neo4j database. -- xref::reference/type-definitions/interfaces.adoc[Interfaces] - Learn about GraphQL interfaces and how they map to the Neo4j database. -- xref::reference/type-definitions/relationships.adoc[Relationships] - Learn more about defining relationships using the Neo4j GraphQL Library. -- xref::reference/directives/schema-configuration/index.adoc[Schema Configuration] - Learn about how to restrict access to certain types or fields. -- xref::reference/directives/autogeneration.adoc[Autogeneration] - Learn about certain types which you can enable autogeneration of values for. -- xref::reference/directives/cypher.adoc[`@cypher` directive] - Learn about how to add custom Cypher to your type definitions. -- xref::reference/directives/default-values.adoc[Default Values] - Learn about different ways of setting default values for particular fields. -- xref::reference/directives/database-mapping.adoc[Database Mapping] - Learn how to map the GraphQL Schema fields to custom Neo4j node and relationship properties. -- xref::reference/type-definitions/indexes-and-constraints.adoc[Indexes and Constraints] - Learn how to use schema directives to add indexes and constraints to your Neo4j database. -- xref::introspector.adoc[Infer GraphQL Type Definitions] - If you have an existing database, you can learn how to automatically generate the type definition file from that. - - - diff --git a/modules/ROOT/pages/reference/type-definitions/interfaces.adoc b/modules/ROOT/pages/reference/type-definitions/interfaces.adoc index 0ebfdc6f..6b6d01e6 100644 --- a/modules/ROOT/pages/reference/type-definitions/interfaces.adoc +++ b/modules/ROOT/pages/reference/type-definitions/interfaces.adoc @@ -1,75 +1,56 @@ -[[type-definitions-interfaces]] -= Interface Types +[[schema-configuration-type-configuration]] += Type Configuration -The Neo4j GraphQL Library supports the use of interfaces on relationship fields. This chapter will walk through their definition and usage. - -== Type definitions - -The following schema defines a `Actor` type, that has a relationship `ACTED_IN`, of type `[Production!]!`. `Production` is an interface type with `Movie` and `Series` implementations. Note in this example that relationship properties have also been used with the `@relationshipProperties` directive, so that interfaces representing relationship properties can be easily distinguished. +When representing a Neo4j Node, a GraphQL Object type produces multiple operation fields in the `Query`, `Mutation`, and `Subscription` types. +For example: [source, graphql, indent=0] ---- -interface Production { - title: String! - actors: [Actor!]! +type Movie { + title: String + length: Int } +---- -type Movie implements Production { - title: String! - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") - runtime: Int! -} +From these type definitions, the library generates the following operation fields: -type Series implements Production { - title: String! - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") - episodes: Int! -} +**Query**: -interface ActedIn @relationshipProperties { - role: String! -} + * `movies` + * `moviesAggregate` + * `moviesConnection` -type Actor { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") -} ----- +**Mutation**: -These type definitions will be used for the rest of the examples in this chapter. + * `createMovies` + * `deleteMovies` + * `updateMovies` -=== Directive inheritance +**Subscription**: -Any directives present on an interface or its fields will be "inherited" by any object types implementing it. For example, the type definitions above could be refactored to have the `@relationship` directive on the `actors` field in the `Production` interface instead of on each implementing type as it is currently: + * `movieCreated` + * `movieUpdated` + * `movieDeleted` +. -[source, graphql, indent=0] ----- -interface Production { - title: String! - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") -} +This section explains how to reduce the operation fields produced using the directives `@query`, `@mutation`, and `@subscription`. -type Movie implements Production { - title: String! - actors: [Actor!]! - runtime: Int! -} +== `@query` -type Series implements Production { - title: String! - actors: [Actor!]! - episodes: Int! -} +This directive is used to limit the availability of query operations in the library. -interface ActedIn @relationshipProperties { - role: String! -} +=== Definition -type Actor { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") -} +[source, graphql, indent=0] ---- +directive @query(read: Boolean! = true, aggregate: Boolean! = false) on OBJECT | SCHEMA +---- + +[NOTE] +==== +Aggregations will no longer be generated by default in the 4.0.0 version of the library. +See xref:migration/v4-migration/index.adoc#opt-in-aggregation[`Opt-in Aggregation`] for more information. +==== [[type-definitions-interfaced-types-querying]] == Querying an interface @@ -80,198 +61,74 @@ For example, the following will return all productions with title starting "The [source, graphql, indent=0] ---- -query GetProductionsStartingWithThe { - actors { - name - actedIn(where: { node: { title_STARTS_WITH: "The " } }) { - title - ... on Movie { - runtime - } - ... on Series { - episodes - } - } - } +enum MutationFields { + CREATE + UPDATE + DELETE } ----- - -Whilst the query below will only return the movies with title starting with "The " for each actor. -[source, graphql, indent=0] ----- -query GetMoviesStartingWithThe { - actors { - name - actedIn(where: { node: { _on: { Movie: { title_STARTS_WITH: "The " } } } }) { - title - ... on Movie { - runtime - } - } - } -} +directive @mutation(operations: [MutationFields!]! = [CREATE, UPDATE, DELETE]) on OBJECT | SCHEMA ---- -This is to prevent overfetching, and you can find an explanation of this xref::troubleshooting.adoc#appendix-preventing-overfetching[here]. +=== Usage -Alternatively, these implementation specific filters can be used to override filtering for a specific implementation. For example, if you wanted all productions with title starting with "The ", but movies with title starting with "A ", you could achieve this using the following: +==== Disable Create, Delete, and Update operations for _Movie_ [source, graphql, indent=0] ---- -query GetProductionsStartingWith { - actors { - name - actedIn(where: { node: { title_STARTS_WITH: "The ", _on: { Movie: { title_STARTS_WITH: "A " } } } }) { - title - ... on Movie { - runtime - } - ... on Series { - episodes - } - } - } +type Movie @mutation(operations: []) { + title: String + length: Int } ---- -== Creating using an interface field - -The below mutation creates an actor and some productions they've acted in: +==== Enable only Create operations for _Movie_ [source, graphql, indent=0] ---- -mutation CreateActorAndProductions { - createActors( - input: [ - { - name: "Chris Pratt" - actedIn: { - create: [ - { - edge: { - role: "Mario" - } - node: { - Movie: { - title: "Super Mario Bros" - runtime: 90 - } - } - } - { - edge: { - role: "Starlord" - } - node: { - Movie: { - title: "Guardians of the Galaxy" - runtime: 122 - } - } - } - { - edge: { - role: "Andy" - } - node: { - Series: { - title: "Parks and Recreation" - episodes: 126 - } - } - } - ] - } - } - ] - ) { - actors { - name - actedIn { - title - } - } - } +type Movie @mutation(operations: [CREATE]) { + title: String + length: Int } ---- -== Nested interface operations +== `@subscription` + +This directive is used to limit Subscription operations in the library. -Operations on interfaces are abstract until you instruct them not to be. Take the following example: +=== Definition [source, graphql, indent=0] ---- -mutation CreateActorAndProductions { - updateActors( - where: { name: "Woody Harrelson" } - connect: { - actedIn: { - where: { node: { title: "Zombieland" } } - connect: { actors: { where: { node: { name: "Emma Stone" } } } } - } - } - ) { - actors { - name - actedIn { - title - } - } - } +enum SubscriptionFields { + CREATE + UPDATE + DELETE + CREATE_RELATIONSHIP + DELETE_RELATIONSHIP } ----- -The above mutation will: +directive @subscription(operations: [SubscriptionFields!]! = [CREATE, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP]) on OBJECT | SCHEMA +---- -1. Find any `Actor` nodes with the name "Woody Harrelson" -2. Connect those nodes to any `Production` (`Movie` or `Series`) nodes with the title "Zombieland" -3. Connect the connected `Production` nodes to any `Actor` nodes with the name "Emma Stone" +=== Usage -As you can see, this is abstract all the way down. If you wanted to only connect `Movie` nodes to `Actor` nodes with name "Emma Stone", you could instead do: +==== Disable subscriptions for _Movie_ [source, graphql, indent=0] ---- -mutation CreateActorAndProductions { - updateActors( - where: { name: "Woody Harrelson" } - connect: { - actedIn: { - where: { node: { title: "Zombieland" } } - connect: { _on: { Movie: { actors: { where: { node: { name: "Emma Stone" } } } } } } - } - } - ) { - actors { - name - actedIn { - title - } - } - } +type Movie @subscription(operations: []) { + title: String + length: Int } ---- -Likewise, you could move this up a level to make sure you only connect to `Movie` nodes with title "Zombieland": +==== Enable only _movieCreated_ subscription for _Movie_ [source, graphql, indent=0] ---- -mutation CreateActorAndProductions { - updateActors( - where: { name: "Woody Harrelson" } - connect: { - actedIn: { - where: { node: { _on: { Movie: { title: "Zombieland" } } } } - connect: { actors: { where: { node: { name: "Emma Stone" } } } } - } - } - ) { - actors { - name - actedIn { - title - } - } - } +type Movie @subscription(operations: [CREATE]) { + title: String + length: Int } ----- +---- \ No newline at end of file diff --git a/modules/ROOT/pages/reference/type-definitions/types.adoc b/modules/ROOT/pages/reference/type-definitions/types.adoc deleted file mode 100644 index ac15e0b4..00000000 --- a/modules/ROOT/pages/reference/type-definitions/types.adoc +++ /dev/null @@ -1,301 +0,0 @@ -[[type-definitions-types]] -= Types - -Neo4j GraphQL supports all of the default GraphQL https://graphql.org/learn/schema/#scalar-types[scalar types] as well as additional scalar and object types specific to the Neo4j database. - -== Int - -One of the default GraphQL scalar types. Supports up to 53-bit values - see xref::reference/type-definitions/types.adoc#type-definitions-types-bigint[`BigInt`] for 64-bit value support. - -== Float - -One of the default GraphQL scalar types. - -== String - -One of the default GraphQL scalar types. - -== Boolean - -One of the default GraphQL scalar types. - -== ID - -One of the default GraphQL scalar types. Stored as a string in the database and always returned as a string. - -[[type-definitions-types-bigint]] -== `BigInt` - -Supports up to 64 bit integers, serialized as strings in variables and in data responses. Shares the same xref::queries-aggregations/filtering.adoc#filtering-numerical-operators[Numerical operators] as the other numeric types. - -[source, graphql, indent=0] ----- -type File { - size: BigInt -} ----- - -Can be passed as a number (does not need quotes) when used directly in a query or mutation: - -[source, graphql, indent=0] ----- -query { - files(where: { size: 9223372036854775807 }) { - size - } -} ----- - -[[type-definitions-types-temporal]] -== Temporal Types - -=== `DateTime` - -ISO datetime string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[datetime] temporal type. - -[source, graphql, indent=0] ----- -type User { - createdAt: DateTime -} ----- - -=== `Date` - -"yyyy-mm-dd" date string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[date] temporal type. - -[source, graphql, indent=0] ----- -type Movie { - releaseDate: Date -} ----- - -=== `Duration` - -ISO 8601 duration string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#cypher-temporal-durations[duration] type. - -[source, graphql, indent=0] ----- -type Movie { - runningTime: Duration! -} ----- - -_Note:_ - -- Decimal values are not currently accepted on `[YMWD]` -- Comparisons are made according to the https://neo4j.com/developer/cypher/dates-datetimes-durations/#comparing-filtering-values[Cypher Developer Guide] - -=== `LocalDateTime` - -"YYYY-MM-DDTHH:MM:SS" datetime string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[LocalDateTime] temporal type. - -[source, graphql, indent=0] ----- -type Movie { - nextShowing: LocalDateTime -} ----- - -=== `Time` - -RFC3339 time string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[Time] temporal type. - -[source, graphql, indent=0] ----- -type Movie { - nextShowing: Time -} ----- - -=== `LocalTime` - -"HH:MM:SS[.sss+]" time string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[LocalTime] temporal type. - -[source, graphql, indent=0] ----- -type Movie { - nextShowing: LocalTime -} ----- - -[[type-definitions-types-spatial]] -== Spatial Types - -Neo4j GraphQL spatial types translate to spatial values stored using https://neo4j.com/docs/cypher-manual/current/values-and-types/spatial/[point] in the database. The use of either of these types in a GraphQL schema will automatically introduce the types needed to run queries and mutations relevant to these spatial types. - -[[type-definitions-types-point]] -=== `Point` - -The `Point` type is used to describe the two https://neo4j.com/docs/cypher-manual/current/values-and-types/spatial/#spatial-values-crs-geographic[Geographic coordinate reference systems] supported by Neo4j. - -In order to use it in your schema, you quite simply add a field with a type `Point` to any type or types in schema, like the following: - -[source, graphql, indent=0] ----- -type TypeWithPoint { - location: Point! -} ----- - -Once this has been done, the `Point` type will be automatically added to your schema, in addition to all of the input and output types you will need to query and manipulate spatial types through your API. - -The rest of the documentation under this heading is documenting all of those automatically generated types and how to use them. - -==== Type definition - -[source, graphql, indent=0] ----- -type Point { - latitude: Float! - longitude: Float! - height: Float -} ----- - -==== Queries and mutations - -Due to the fact that `Point` is an object type, it has an additional type for input in queries and mutations. However, this input type has the same shape as the object type: - -[source, graphql, indent=0] ----- -input PointInput { - latitude: Float! - longitude: Float! - height: Float -} ----- - -===== Query - -For example, you can query for a `User` with an exact location: - -[source, graphql, indent=0] ----- -query Users($longitude: Float!, $latitude: Float!) { - users(where: { location: { longitude: $longitude, latitude: $latitude } }) { - name - location { - longitude - latitude - } - } -} ----- - -===== Mutation - -An example of creating a `User` with a location is as follows: - -[source, graphql, indent=0] ----- -mutation CreateUsers($name: String!, $longitude: Float!, $latitude: Float!) { - createUsers(input: [{ name: $name, location: { longitude: $longitude, latitude: $latitude } }]) { - users { - name - location { - longitude - latitude - } - } - } -} ----- - -==== Filtering - -In addition to the xref::queries-aggregations/filtering.adoc#filtering-numerical-operators[Numerical operators], the `Point` type has an additional `_DISTANCE` filter. All of the filters take the following type as an argument: - -[source, graphql, indent=0] ----- -input PointDistance { - point: Point! - distance: Float! -} ----- - -In essence, each of the filters mean the following: - -* `_LT`: Checks that the specified `point` field is less than the `distance` away in meters from the `Point` being compared against. -* `_LTE`: Checks that the specified `point` field is less than or equal to the `distance` away in meters from the `Point` being compared against. -* `_DISTANCE`: Checks that the specified `point` field is the exact `distance` away in meters from the `Point` being compared against. -* `_GTE`: Checks that the specified `point` field is greater than the `distance` away in meters from the `Point` being compared against. -* `_GT`: Checks that the specified `point` field is greater than or equal to the `distance` away in meters from the `Point` being compared against. - -In practice, you can construct queries such as the following which will find 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-definitions-types-cartesian-point]] -=== `CartesianPoint` - -The `CartesianPoint` type is used to describe the two https://neo4j.com/docs/cypher-manual/current/values-and-types/spatial/#spatial-values-crs-cartesian[Cartesian coordinate reference systems] supported by Neo4j. - -In order to use it in your schema, you quite simply add a field with a type `CartesianPoint` to any type or types in schema, like the following: - -[source, graphql, indent=0] ----- -type TypeWithCartesianPoint { - location: CartesianPoint! -} ----- - -Once this has been done, the `CartesianPoint` type will be automatically added to your schema, in addition to all of the input and output types you will need to query and manipulate spatial types through your API. - -The rest of the documentation under this heading is documenting all of those automatically generated types and how to use them. - -==== Type definition - -[source, graphql, indent=0] ----- -type CartesianPoint { - x: Float! - y: Float! - z: Float -} ----- - -==== Queries and mutations - -Due to the fact that `CartesianPoint` is an object type, it has an additional type for input in queries and mutations. However, this input type has the same shape as the object type: - -[source, graphql, indent=0] ----- -input CartesianPointInput { - x: Float! - y: Float! - z: Float -} ----- - -==== Filtering - -In addition to the xref::queries-aggregations/filtering.adoc#filtering-numerical-operators[Numerical operators], the `CartesianPoint` type has an additional `_DISTANCE` filter. All of the filters take the following type as an argument: - -[source, graphql, indent=0] ----- -input CartesianPointDistance { - point: CartesianPoint! - distance: Float! -} ----- - -In essence, each of the filters mean the following: - -* `_LT`: Checks that the specified `point` field is less than the `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. -* `_LTE`: Checks that the specified `point` field is less than or equal to the `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. -* `_DISTANCE`: Checks that the specified `point` field is the exact `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. -* `_GTE`: Checks that the specified `point` field is greater than the `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. -* `_GT`: Checks that the specified `point` field is greater than or equal to the `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. diff --git a/modules/ROOT/pages/reference/directives/schema-configuration/field-configuration.adoc b/modules/ROOT/pages/schema-configuration/field-configuration.adoc similarity index 71% rename from modules/ROOT/pages/reference/directives/schema-configuration/field-configuration.adoc rename to modules/ROOT/pages/schema-configuration/field-configuration.adoc index ab4f52b7..ec652924 100644 --- a/modules/ROOT/pages/reference/directives/schema-configuration/field-configuration.adoc +++ b/modules/ROOT/pages/schema-configuration/field-configuration.adoc @@ -1,5 +1,6 @@ [[schema-configuration-field-configuration]] -= Field Configuration +:description: This page describes how to use the directives @selectable, @settable, @filterable and `@relationship` to control how fields are exposed. += Field configuration In case you need to remove fields from a GraphQL Object Type or a GraphQL Input Object Type, consider the following type definitions: @@ -17,7 +18,7 @@ type Actor { } ---- -It will generate the type `Actor`: +It generates the type `Actor`: [source, graphql, indent=0] ---- @@ -30,7 +31,7 @@ type Actor { } ---- -By using the directives `@selectable`, `@settable`, `@filterable` and `@relationship` it is possible to control how these fields are exposed. +By using the directives `@selectable`, `@settable`, `@filterable`, and `@relationship` it is possible to control how these fields are exposed. For instance, to hide the field `age` in the Selection Set, you can use the `@selectable` directive: [source, graphql, indent=0] @@ -47,7 +48,7 @@ type Actor { } ---- -Now the type `Actor` will look like this: +Now the type `Actor` looks like this: [source, graphql, indent=0] ---- @@ -61,12 +62,13 @@ type Actor { == `@relationship` -There are several nested operations available for every field created using the `@relationship` directive. These are `create`, `connect`, `disconnect`, `connectOrCreate`, `delete`. +There are several nested operations available for every field created using the `@relationship` directive. These are `create`, `connect`, `disconnect`, `connectOrCreate`, and `delete`. However, these operations are not always needed. The `@relationship` directive allows you to define which operations should be available for a relationship by using the argument `nestedOperations`. -Additionally, the `@relationship` directive causes fields to be added for nested aggregations. These can be disabled using the `aggregate` argument. +Additionally, the `@relationship` directive causes fields to be added for nested aggregations. +These can be disabled using the `aggregate` argument. === Definition @@ -99,7 +101,7 @@ See xref:migration/v4-migration/index.adoc#_relationship_changes[`@relationship === Usage -==== Configure aggregation +*Configure aggregation* From the previous type definitions, the type `Actor` produced is: @@ -113,7 +115,8 @@ type Actor { } ---- -As it's visible, the relationship field `actedIn` produces the operation field `actedInAggregate` which allows aggregations on that relationship. It's possible to configure this behavior by passing the argument aggregate on the `@relationship` directive: +Note that the relationship field `actedIn` produces the operation field `actedInAggregate`, which allows aggregations on that relationship. +It is possible to configure this behavior by passing the argument aggregate on the `@relationship` directive: [source, graphql, indent=0] ---- @@ -129,7 +132,7 @@ type Actor { } ---- -In this case, as we passed the argument `aggregate` as false, the type `Actor` produced is: +In this case, as the argument `aggregate` was passed as false, the type `Actor` produced is: [source, graphql, indent=0] ---- @@ -141,9 +144,10 @@ type Actor { } ---- -=== Configure nested operations +*Configure nested operations* -A large part of the schema produced by the library is needed to support nested operations. These are enabled by the directive `@relationship` as described in xref:reference/type-definitions/relationships.adoc#_inserting_data[@relationship Inserting Data]. +A large part of the schema produced by the library is needed to support nested operations. +These are enabled by the directive `@relationship` as described in xref::/type-definitions/types/relationships.adoc#_inserting_data[`@relationship` -> Inserting data]. The nested operations available are: @@ -159,9 +163,10 @@ enum NestedOperations { } ---- -By default, the `@relationship` enables all of them when defined. To enable only some of them, you have to pass the argument `nestedOperations` specifying the operations required. +By default, `@relationship` enables all of them when defined. +To enable only some of them, you have to pass the argument `nestedOperations` specifying the operations required. -**Disable nested create** +*Disable nested create* To disable the nested `CREATE` operation, change the initial type definitions to: @@ -179,11 +184,11 @@ type Actor { } ---- -As the `CREATE` operation is not present in the `nestedOperations` argument array, it will be no longer possible to create movies starting from the `Actor` type. +As the `CREATE` operation is not present in the `nestedOperations` argument array, it is no longer possible to create movies starting from the `Actor` type. -**Disable all nested operations** +*Disable all nested operations* -If instead, no nested operations are required, it's possible to disable all the nested operations by passing an empty array, as: +If instead, no nested operations are required, it is possible to disable all the nested operations by passing an empty array: [source, graphql, indent=0] ---- @@ -204,8 +209,8 @@ type Actor { This directive sets the availability of fields on queries and aggregations. It has two arguments: -* **onRead**: If disabled, this field will not be available on queries and subscriptions. -* **onAggregate**: If disabled, aggregations will not be available for this field. +* **onRead**: if disabled, this field is not available on queries and subscriptions. +* **onAggregate**: if disabled, aggregations is not available for this field. === Definition @@ -227,7 +232,7 @@ type Movie { } ---- -The type `Movie` in the resulting schema will look like this: +The type `Movie` in the resulting schema looks like this: [source, graphql, indent=0] ---- @@ -248,7 +253,7 @@ type MovieAggregateSelection { } ---- -In case we wanted to remove the `description` field from `MovieAggregateSelection`, what we needed to do is change the `onAggregate` value to `false`, as follow: +In case you want to remove the `description` field from `MovieAggregateSelection`, you need to change the `onAggregate` value to `false`: [source, graphql, indent=0] ---- @@ -258,7 +263,7 @@ type Movie { } ---- -=== `@selectable` with Relationships +*`@selectable` with relationships* This directive can be used along with relationship fields. @@ -274,9 +279,9 @@ type Actor { } ---- -This means that the `actedIn` field is queryable from the homonymous generated field `actedIn` and the field `actedInConnection`, to avoid that, it's required to use the directive `@selectable`. - -For instance: +This means that the `actedIn` field can be queried from the homonymous generated field `actedIn` and the field `actedInConnection`. +To avoid that, it is required to use the directive `@selectable`. +For instance, these type definitions: [source, graphql, indent=0] ---- @@ -293,7 +298,7 @@ type Actor { } ---- -It will generate the type type `Actor`: +Generate the type `Actor`: [source, graphql, indent=0] ---- @@ -303,18 +308,16 @@ type Actor { } ---- -[NOTE] -==== -Please note how the `actedInAggregate` is not affected by the argument `onAggregate`. To disable the generation of `actedInAggregate` see the `aggregate` argument of the directive xref::reference/directives/schema-configuration/field-configuration.adoc#_relationship[`@relationship`] -==== +Note how `actedInAggregate` is not affected by the argument `onAggregate`. +To disable the generation of `actedInAggregate`, see the `aggregate` argument of the directive xref::/schema-configuration/field-configuration.adoc#_relationship[`@relationship`]. == `@settable` This directive sets the availability of the input field on creation and update mutations. It has two arguments: -* **onCreate**: If disabled, this field will not be available on creation operations. -* **onUpdate**: If disabled, this field will not be available on update operations. +* **onCreate**: if disabled, this field is not available on creation operations. +* **onUpdate**: if disabled, this field is not available on update operations. === Definition @@ -324,7 +327,7 @@ It has two arguments: directive @settable(onCreate: Boolean! = true, onUpdate: Boolean! = true) on FIELD_DEFINITION ---- -==== Usage +=== Usage With this definition: @@ -342,7 +345,7 @@ type Actor { } ---- -The following input fields will be generated: +The following input fields are generated: [source, graphql, indent=0] ---- @@ -356,12 +359,12 @@ input MovieUpdateInput { } ---- -This means the description can be set on creation, but it will not be available on update operations. +This means the description can be set on creation, but it is not available on update operations. -=== `@settable` with Relationships +*`@settable` with relationships* This directive can be used along with relationship fields. -When an operation on a field is disabled this way, that relationship will not be available on top-level operations. +When an operation on a field is disabled this way, that relationship is no longer available on top-level operations. For example: [source, graphql, indent=0] @@ -379,7 +382,7 @@ type Actor { } ---- -The following input fields will be generated: +The following input fields are generated: [source, graphql, indent=0] ---- @@ -393,15 +396,15 @@ input ActorUpdateInput { } ---- -This means the `actedIn` can be updated on an update, but it will not be available on create operations. +This means `actedIn` can be updated on an update, but it is no longer available on `create`` operations. == `@filterable` This directive defines the filters generated for the field to which this directive is applied. It has two arguments: -* **byValue**: If disabled, this field will not generate value filters. -* **byAggregate**: If disabled, this field will not generate aggregation filters. +* **byValue**: if disabled, this field does not generate value filters. +* **byAggregate**: if disabled, this field does not generate aggregation filters. === Definition @@ -411,7 +414,7 @@ It has two arguments: directive @filterable(byValue: Boolean! = true, byAggregate: Boolean! = true) on FIELD_DEFINITION ---- -==== Usage +=== Usage With this definition: @@ -430,7 +433,7 @@ type Actor { } ---- -The following input fields will be generated: +The following input fields are generated: [source, graphql, indent=0] ---- @@ -478,10 +481,10 @@ input ActorActedInNodeAggregationWhereInput { As shown by the generated input fields, the `description` field is not available for filtering on both value and aggregation filters. -=== `@filterable` with Relationships +*`@filterable` with relationships* This directive can be used along with relationship fields. -When an operation on a field is disabled this way, that relationship will not be available on top-level operations. +When an operation on a field is disabled this way, that relationship is no longer available on top-level operations. For example: [source, graphql, indent=0] @@ -500,7 +503,7 @@ type Actor { } ---- -The following input fields will be generated: +The following input fields are generated: [source, graphql, indent=0] ---- @@ -537,44 +540,4 @@ input ActorActedInNodeAggregationWhereInput { } ---- -As shown by the above inputs fields, the `actors` field is not available for filtering on both value and aggregation filters. - -== `@readonly` label:deprecated[] - -With this directive, fields will only be featured in mutations for creating and in object types for querying. -It is not mutable after creation. - -[NOTE] -==== -This directive is deprecated. -Use the xref::reference/directives/schema-configuration/field-configuration.adoc#_settable[`@settable`] directive instead. -==== - - -=== Definition - -[source, graphql, indent=0] ----- -"""Instructs @neo4j/graphql to only include a field in generated input type for creating, and in the object type within which the directive is applied.""" -directive @readonly on FIELD_DEFINITION ----- - - -== `@writeonly` label:deprecated[] - -With this directive, fields will only be featured in input types and will not be available for querying the object type through a Query or through a mutation response. - -[NOTE] -==== -This directive is deprecated. -Use the xref::reference/directives/schema-configuration/field-configuration.adoc#_selectable[`@selectable`] directive instead. -==== - - -=== Definition - -[source, graphql, indent=0] ----- -"""Instructs @neo4j/graphql to only include a field in the generated input types for the object type within which the directive is applied, but exclude it from the object type itself.""" -directive @writeonly on FIELD_DEFINITION ----- +As shown by the previous inputs fields, the `actors` field is not available for filtering on both value and aggregation filters. \ No newline at end of file diff --git a/modules/ROOT/pages/reference/directives/schema-configuration/global-configuration.adoc b/modules/ROOT/pages/schema-configuration/global-configuration.adoc similarity index 69% rename from modules/ROOT/pages/reference/directives/schema-configuration/global-configuration.adoc rename to modules/ROOT/pages/schema-configuration/global-configuration.adoc index 06b041fe..79a8ef13 100644 --- a/modules/ROOT/pages/reference/directives/schema-configuration/global-configuration.adoc +++ b/modules/ROOT/pages/schema-configuration/global-configuration.adoc @@ -1,9 +1,11 @@ [[schema-configuration-global-configuration]] -= Global Configuration +:description: This page describes how to globally disable specific types of operations. += Global configuration Through the schema configuration, it is possible to globally disable specific types of operation. -To set up operations individually, refer to xref:reference/directives/schema-configuration/type-configuration.adoc[Type Configuration]. -For instance, if you want to disable all top-level aggregation operations at once, Neo4j GraphQL Library offers this option through schema extensions with `@query`: +To set up operations individually, refer to xref:/schema-configuration/type-configuration.adoc[Type Configuration]. + +For instance, if you want *to disable all top-level aggregation operations at once*, the Neo4j GraphQL Library offers this option through schema extensions with `@query`: [source, graphql, indent=0] ---- @@ -32,8 +34,8 @@ extend schema @query(read: true, aggregate: false) **Invalid schema usage** The same schema configuration directive cannot be applied to both Schema and Object. - Take the following type definitions as example: + [source, graphql, indent=0] ---- type Movie { @@ -48,4 +50,4 @@ type Actor @query(read: false, aggregate: true) { extend schema @query(read: true, aggregate: false) ---- -Such configuration will prompt the error `"@query directive already defined at the schema location"`. +Such configuration prompts the error `"@query directive already defined at the schema location"`. diff --git a/modules/ROOT/pages/schema-configuration/index.adoc b/modules/ROOT/pages/schema-configuration/index.adoc new file mode 100644 index 00000000..b271b1a9 --- /dev/null +++ b/modules/ROOT/pages/schema-configuration/index.adoc @@ -0,0 +1,14 @@ +[[type-definitions-schema-configuration]] +:description: This section describes configurations that can be set to a schema in Neo4j GraphQL. += Schema configuration + +Neo4j GraphQL Library supports several functionalities such as CRUD operations, aggregation, filtering, and others. +To make them work, a large amount of GraphQL types are generated. +However, in some cases, it may be advisable to reduce the scope of the API produced. + +This section provides information on how to limit access to unwanted operations and reduce the size of the schema which can improve the performance: + +- xref::schema-configuration/index.adoc[Schema configuration] - How to restrict access to certain types or fields. +- xref::schema-configuration/field-configuration.adoc[Field configuration] - How to remove fields from a GraphQL Object Type or a GraphQL Input Object Type. +- xref::schema-configuration/global-configuration.adoc[Global configuration] - How to globally disable specific types of operation. +- xref::schema-configuration/type-configuration.adoc[Type configuration] - How to set up `Query`, `Mutation`, and `Subscription` types. \ No newline at end of file diff --git a/modules/ROOT/pages/schema-configuration/type-configuration.adoc b/modules/ROOT/pages/schema-configuration/type-configuration.adoc new file mode 100644 index 00000000..46eb8077 --- /dev/null +++ b/modules/ROOT/pages/schema-configuration/type-configuration.adoc @@ -0,0 +1,150 @@ +[[schema-configuration-type-configuration]] +:description: This page describes how to reduce the operation fields produced using the directives @query, @mutation, and @subscription. += Type configuration + +When representing a Neo4j node, a GraphQL Object Type produces multiple operation fields in the `Query`, `Mutation`, and `Subscription` types. +For example: + +[source, graphql, indent=0] +---- +type Movie { + title: String + length: Int +} +---- + +From these type definitions, the library generates the following operation fields: + +**Query**: + + * `movies` + * `moviesAggregate` + * `moviesConnection` + +**Mutation**: + + * `createMovies` + * `deleteMovies` + * `updateMovies` + +**Subscription**: + + * `movieCreated` + * `movieUpdated` + * `movieDeleted` +. + +This page describes how to reduce the operation fields produced using the directives `@query`, `@mutation`, and `@subscription`. + +== `@query` + +This directive is used to limit the availability of query operations in the library. + +=== Definition + +[source, graphql, indent=0] +---- +directive @query(read: Boolean! = true, aggregate: Boolean! = false) on OBJECT | SCHEMA +---- + +[NOTE] +==== +Aggregations are no longer generated by default in the 4.0.0 version of the library. +See xref:migration/v4-migration/index.adoc#opt-in-aggregation[Opt-in aggregation] for more information. +==== + +=== Usage + +.Disable _movies_ and _moviesConnection_ operations +[source, graphql, indent=0] +---- +type Movie @query(read: false, aggregate: true) { + title: String + length: Int +} +---- + +.Disable _moviesAggregate_ operations +[source, graphql, indent=0] +---- +type Movie @query(read: true, aggregate: false) { + title: String + length: Int +} +---- + +== `@mutation` + +This directive is used to limit the availability of mutation operations in the library. + +=== Definition + +[source, graphql, indent=0] +---- +enum MutationFields { + CREATE + UPDATE + DELETE +} + +directive @mutation(operations: [MutationFields!]! = [CREATE, UPDATE, DELETE]) on OBJECT | SCHEMA +---- + +=== Usage + +.Disable Create, Delete, and Update operations for _Movie_ +[source, graphql, indent=0] +---- +type Movie @mutation(operations: []) { + title: String + length: Int +} +---- + +.Enable only Create operations for _Movie_ +[source, graphql, indent=0] +---- +type Movie @mutation(operations: [CREATE]) { + title: String + length: Int +} +---- + +== `@subscription` + +This directive is used to limit subscription operations in the library. + +=== Definition + +[source, graphql, indent=0] +---- +enum SubscriptionFields { + CREATE + UPDATE + DELETE + CREATE_RELATIONSHIP + DELETE_RELATIONSHIP +} + +directive @subscription(operations: [SubscriptionFields!]! = [CREATE, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP]) on OBJECT | SCHEMA +---- + +=== Usage + +.Disable subscriptions for _Movie_ +[source, graphql, indent=0] +---- +type Movie @subscription(operations: []) { + title: String + length: Int +} +---- + +.Enable only _movieCreated_ subscription for _Movie_ +[source, graphql, indent=0] +---- +type Movie @subscription(operations: [CREATE]) { + title: String + length: Int +} +---- diff --git a/modules/ROOT/pages/subscriptions/filtering.adoc b/modules/ROOT/pages/subscriptions/filtering.adoc index 7330ed19..9fcdbb3b 100644 --- a/modules/ROOT/pages/subscriptions/filtering.adoc +++ b/modules/ROOT/pages/subscriptions/filtering.adoc @@ -20,14 +20,14 @@ All types can be tested for either equality or non-equality. For the `Boolean` t [[filtering-numerical-operators]] === Numerical operators -The following comparison operators are available for numeric types (`Int`, `Float`, xref::reference/type-definitions/types.adoc#type-definitions-types-bigint[`BigInt`]) +The following comparison operators are available for numeric types (`Int`, `Float`, xref::type-definitions/types/scalar.adoc[`BigInt`]) * `_LT` * `_LTE` * `_GTE` * `_GT` -NOTE: Filtering on xref::reference/type-definitions/types.adoc#type-definitions-types-temporal[Temporal Types] and xref::reference/type-definitions/types.adoc#type-definitions-types-spatial[Spatial Types]: is not yet supported. +NOTE: Filtering on xref::/type-definitions/types/temporal.adoc[Temporal Types] and xref::/type-definitions/types/spatial.adoc[Spatial Types]: is not yet supported. === String comparison diff --git a/modules/ROOT/pages/troubleshooting.adoc b/modules/ROOT/pages/troubleshooting.adoc index a62dcab4..cf38707d 100644 --- a/modules/ROOT/pages/troubleshooting.adoc +++ b/modules/ROOT/pages/troubleshooting.adoc @@ -134,7 +134,7 @@ RETURN m If you use a causal cluster or an Aura Professional instance, there is a chance that the created data is not yet present on the server which gets connected to on the next GraphQL query. -You can ensure that the data is available to query by passing a bookmark into your request - see xref::reference/driver-configuration.adoc#driver-configuration-bookmarks[Specifying Neo4j Bookmarks] for more information. +You can ensure that the data is available to query by passing a bookmark into your request - see xref::driver-configuration.adoc#driver-configuration-bookmarks[Specifying Neo4j Bookmarks] for more information. === What is `_emptyInput` in my update and create inputs? diff --git a/modules/ROOT/pages/reference/directives/autogeneration.adoc b/modules/ROOT/pages/type-definitions/directives/autogeneration.adoc similarity index 67% rename from modules/ROOT/pages/reference/directives/autogeneration.adoc rename to modules/ROOT/pages/type-definitions/directives/autogeneration.adoc index a6ddfef8..0cd80b1c 100644 --- a/modules/ROOT/pages/reference/directives/autogeneration.adoc +++ b/modules/ROOT/pages/type-definitions/directives/autogeneration.adoc @@ -1,16 +1,19 @@ [[type-definitions-autogeneration]] +:description: This page describes directives used for autogeneration. = Autogeneration +This page describes directives used for autogeneration: + [[type-definitions-autogeneration-id]] == `@id` -This directive marks a field as the unique identifier for an object type, and by default; enables autogeneration of IDs for the field and implies that a unique node property constraint should exist for the property. +This directive marks a field as the unique identifier for an object type. +By default, that enables autogeneration of IDs for the field and implies that a unique node property constraint should exist for the property. The format of each generated ID is a UUID generated by https://neo4j.com/docs/cypher-manual/current/functions/scalar/#functions-randomuuid[randomUUID() function]. - If autogeneration for an ID field is enabled, the field will not be present in input types for mutations. -See xref::reference/type-definitions/indexes-and-constraints.adoc#type-definitions-constraints-unique[Unique node property constraints] for details on how to assert the existence of the necessary database constraints for relevant fields. +See xref::/type-definitions/directives/indexes-and-constraints.adoc#type-definitions-constraints-unique[Unique node property constraints] for details on how to assert the existence of the necessary database constraints for relevant fields. === Definition @@ -25,7 +28,7 @@ directive @id( === Usage -The following two type definitions are equivalent in that they both specify an ID which will benefit from autogeneration: +The following two type definitions are equivalent in that they both specify an ID which benefits from autogeneration: [source, graphql, indent=0] ---- @@ -66,11 +69,13 @@ type User { [[type-definitions-autogeneration-timestamp]] == `@timestamp` -=== Timestamps - -This directive marks a field as a timestamp field, which will be used to store timestamps of when particular events happen through the GraphQL API. +This directive marks a field as a timestamp field, which can be used to store timestamps of when particular events happen through the GraphQL API. -> These events are triggered and stored at the GraphQL API layer. Events happening in your database through other routes will not trigger updates of these timestamps. +[NOTE] +==== +These events are triggered and stored at the GraphQL API layer. +Events happening in your database through other routes do not trigger updates of these timestamps. +==== === Definition @@ -90,7 +95,7 @@ directive @timestamp( === Usage -The following type definition has two individual fields to store the timestamps of create and update events. +The following type definition has two individual fields to store the timestamps of create and update events: [source, graphql, indent=0] ---- @@ -100,7 +105,7 @@ type User { } ---- -The following two equivalent type definitions have a single field storing the event timestamp of the last create or update: +The following two equivalent type definitions have a single field storing the event timestamp of the last `create` or `update`: [source, graphql, indent=0] ---- @@ -115,13 +120,14 @@ type User { lastModified: DateTime! @timestamp(operations: [CREATE, UPDATE]) } ---- + [[type-definitions-autogeneration-populated-by]] == `@populatedBy` -The `@populatedBy` directive is used to specify a callback function that gets executed during GraphQL query parsing, +This directive is used to specify a callback function that gets executed during GraphQL query parsing, to populate fields which have not been provided within the input. -For non-required values, callbacks may return `undefined` (meaning that nothing will be changed or added to the property) or `null` (meaning that the property will be removed). +For non-required values, callbacks may return `undefined` (meaning that nothing is changed or added to the property) or `null` (meaning that the property will be removed). === Definition @@ -143,7 +149,7 @@ directive @populatedBy( === Usage -Type Definitions: +Type definitions: [source, graphql, indent=0] ---- @@ -153,9 +159,7 @@ type Product { } ---- -Schema Construction: - -> Note that the callback is asynchronous! +Schema construction (note that the callback is asynchronous): [source, javascript, indent=0] ---- @@ -176,11 +180,12 @@ new Neo4jGraphQL({ }) ---- -==== Using GraphQL context values +== Context values -The GraphQL context for the request is available as the third argument in a callback. This maps to the argument pattern for GraphQL resolvers. +The GraphQL context for the request is available as the third argument in a callback. +This maps to the argument pattern for GraphQL resolvers. -For example, if you wanted a field `modifiedBy`: +For example, if you want a field `modifiedBy`: [source, graphql, indent=0] ---- @@ -211,7 +216,7 @@ new Neo4jGraphQL({ }) ---- -NOTE: The second positional argument, in this case `_args`, has a type of `Record`, and as such will always be an empty object. +Note that the second positional argument, in this case `_args`, has a type of `Record`, and as such it will always be an empty object. === Definition @@ -233,7 +238,7 @@ directive @callback( === Usage -Type Definitions: +Type definitions: [source, graphql, indent=0] ---- @@ -243,9 +248,7 @@ type Product { } ---- -Schema Construction: - -> Note that the callback is asynchronous! +Schema construction (note that the callback is asynchronous): [source, javascript, indent=0] ---- @@ -264,41 +267,4 @@ new Neo4jGraphQL({ } } }) ----- - -==== Using GraphQL context values - -The GraphQL context for the request is available as the third argument in a callback. This maps to the argument pattern for GraphQL resolvers. - -For example, if you wanted a field `modifiedBy`: - -[source, graphql, indent=0] ----- -type Record { - content: String! - modifiedBy: @callback(operations: [CREATE, UPDATE], name: "modifiedBy") -} ----- - -If the username is located in `context.username`, you could define a callback such as: - -[source, javascript, indent=0] ----- -const modifiedByCallback = async (_parent, _args, context) => { - return context.username; -} - -new Neo4jGraphQL({ - typeDefs, - driver, - features: { - populatedBy: { - callbacks: { - modifiedBy: modifiedByCallback - } - } - } -}) ----- - -NOTE: The second positional argument, in this case `_args`, has a type of `Record`, and as such will always be an empty object. +---- \ No newline at end of file diff --git a/modules/ROOT/pages/type-definitions/directives/basics.adoc b/modules/ROOT/pages/type-definitions/directives/basics.adoc new file mode 100644 index 00000000..51381a44 --- /dev/null +++ b/modules/ROOT/pages/type-definitions/directives/basics.adoc @@ -0,0 +1,67 @@ +[[type-definitions-basics]] +:description: This page describes basic notions about how to use directives with the Neo4j GraphQL Library. += Basics + +Each type in your GraphQL type definitions can be mapped to an entity in your Neo4j database, such as nodes, relationships, and relationship properties. +This page describes how that can be done. + +== Nodes + +The most basic mapping, it uses GraphQL type names to map to the Neo4j node label. +For example, to represent a node with label "Movie" and a single property "title" of type string: + +[source, graphql, indent=0] +---- +type Movie { + title: String +} +---- + +== Relationships + +Relationships are represented by marking particular fields with a directive -- in this case, `@relationship`. +It defines the relationship type in the database, as well as which direction that relationship goes in. + +To add a second node type, "Actor", and connect the two together, you should do the following: + +[source, graphql, indent=0] +---- +type Movie { + title: String + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) +} + +type Actor { + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) +} +---- + +Note that, in this case, there is a directive on each "end" of the relationship, but it is not essential. + +=== Relationship properties + +In order to add properties to a relationship, you need to add a new type to your type definitions. +This time, however, it should be an interface which must be decorated with the `@relationshipProperties` directive. + +For example, for the "ACTED_IN" relationship, add a property "roles": + +[source, graphql, indent=0] +---- +type Movie { + title: String + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") +} + +type Actor { + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") +} + +interface ActedIn @relationshipProperties { + roles: [String] +} +---- + +Note that in addition to this interface type, there is an added a key `properties` in the existing `@relationship` directives. +For more information, see xref::/type-definitions/types/relationships.adoc[Type definitions -> Relationships]. diff --git a/modules/ROOT/pages/reference/directives/custom-directives.adoc b/modules/ROOT/pages/type-definitions/directives/custom-directives.adoc similarity index 73% rename from modules/ROOT/pages/reference/directives/custom-directives.adoc rename to modules/ROOT/pages/type-definitions/directives/custom-directives.adoc index 6427741b..a221fab6 100644 --- a/modules/ROOT/pages/reference/directives/custom-directives.adoc +++ b/modules/ROOT/pages/type-definitions/directives/custom-directives.adoc @@ -1,18 +1,11 @@ [[type-definitions-custom-directives]] -= Custom Directives +:description: This page describes how to use custom directives in the Neo4j GraphQL Library. += Custom directives -As of `@graphql-tools` version 8, the mechanism for defining and applying -custom directives has changed significantly, and this is reflected in the -Neo4j GraphQL Library. +As of https://www.graphql-tools.com/docs/schema-directives[`@graphql-tools`] version 8, the mechanism for defining and applying custom directives has changed significantly, and this is reflected in the Neo4j GraphQL Library. -It comes highly recommended to read and follow the -https://www.graphql-tools.com/docs/schema-directives[`@graphql-tools` documentation on schema directives] -for comprehensive documentation on this feature. - -== Example - -This example will work towards the implementation of a field directive to -uppercase field values, with the following definition: +To understand the changes, consider the following example. +It works with the implementation of a field directive to uppercase field values, using the following definition: [source, graphql, indent=0] ---- @@ -48,16 +41,14 @@ function upperDirective(directiveName: string) { } ---- -On calling the function, the type definition and the transformer will be -returned for the directive: +On calling the function, the type definition and the transformer returns for the directive: [source, typescript, indent=0] ---- const { upperDirectiveTypeDefs, upperDirectiveTransformer } = upperDirective("uppercase"); ---- -On construction of a `Neo4jGraphQL` instance, the directive definition can be -passed into the `typeDefs` array alongside the rest of the type definitions: +On construction of a `Neo4jGraphQL` instance, the directive definition can be passed into the `typeDefs` array alongside the rest of the type definitions: [source, typescript, indent=0] ---- @@ -74,13 +65,11 @@ const neoSchema = new Neo4jGraphQL({ }); ---- -Finally, the Neo4j GraphQL schema must be transformed using the transformer -returned from the directive function: +Finally, the Neo4j GraphQL schema must be transformed using the transformer returned from the directive function: [source, typescript, indent=0] ---- const schema = upperDirectiveTransformer(await neoSchema.getSchema()); ---- -This `schema` object is an instance of a `GraphQLSchema` which can be used in -any GraphQL tools, such as in Apollo Server. +Note that this `schema` object is an instance of a `GraphQLSchema` which can be used in any GraphQL tools, such as in Apollo Server. diff --git a/modules/ROOT/pages/type-definitions/directives/cypher.adoc b/modules/ROOT/pages/type-definitions/directives/cypher.adoc new file mode 100644 index 00000000..67cafe7a --- /dev/null +++ b/modules/ROOT/pages/type-definitions/directives/cypher.adoc @@ -0,0 +1,236 @@ +[[type-definitions-cypher]] +:description: This page describes how to use the @cypher directive in the Neo4j GraphQL Library. += `@cypher` + +The `@cypher` directive binds a GraphQL field to the results of a Cypher query. +This directive can be used both for properties in a type or as top level queries. + +== Global variables + +Global variables are available for use within the Cypher statement and can be applied to the `@cypher` directive. + +[cols="1,2,2"] +|=== +| Variable | Description | Example + +| `this` +| This value is a reference to the currently resolved node, and it can be used to traverse the graph. +a| +[source, graphql, indent=0] +---- +{ + Movie { + title + actors: ACTED_IN @this { + role + actor { + name + } + } + directors: DIRECTED @this { + director { + name + } + } + } +} +---- + +| `auth` +a| This value is represented by the following TypeScript interface definition: +[source, typescript, indent=0] +---- +interface Auth { + isAuthenticated: boolean; + roles?: string[]; + jwt: any; +} +---- +a| You can use the JWT in the request to return the value of the currently logged in User: +[source, graphql, indent=0] +---- +type User { + id: String +} + +type Query { + me: User @cypher( + statement: """ + MATCH (user:User {id: $jwt.sub}) + RETURN user + """, + columnName: "user" + ) +} +---- + +| `cypherParams` +| Use it to inject values into the Cypher query from the GraphQL context function. +a| Inject into context: +[source, typescript, indent=0] +---- +const server = new ApolloServer({ + typeDefs, +}); + +await startStandaloneServer(server, { + context: async ({ req }) => ({ cypherParams: { userId: "user-id-01" } }), +}); +---- + +Use in Cypher query: + +[source, graphql, indent=0] +---- +type Query { + userPosts: [Post] @cypher(statement: """ + MATCH (:User {id: $userId})-[:POSTED]->(p:Post) + RETURN p + """, columnName: "p") +} +---- +|=== + + +== Return values + +The return value of Cypher statements must always be of the same type to which the directive is applied. + +The variable must also be aliased with a name that is the same as the one passed to `columnName`. +This can be the name of a node, relationship query or an alias in the `RETURN` statement of the Cypher statement. + +=== Scalar values + +Cypher statements must return a value which matches the scalar type to which the directive was applied. +For example: + +[source, graphql, indent=0] +---- +type Query { + randomNumber: Int @cypher(statement: "RETURN rand() as result", columnName: "result") +} +---- + +=== Object types + +When returning an object type, all fields of the type must be available in the Cypher return value. +This can be achieved by either returning the entire object from the Cypher query, or returning a map of the fields which are required for the object type. +Both approaches are demonstrated below: + +[source, graphql, indent=0] +---- +type User { + id +} + +type Query { + users: [User] + @cypher( + statement: """ + MATCH (u:User) + RETURN u + """, + columnName: "u" + ) +} +---- + +[source, graphql, indent=0] +---- +type User { + id +} + +type Query { + users: [User] @cypher(statement: """ + MATCH (u:User) + RETURN { + id: u.id + } as result + """, columnName: "result") +} +---- + +The downside of the latter approach is that you need to adjust the return object as you change your object type definition. + +== Usage examples + +The `@cypher` directive can be used in different contexts, such as the ones described in this section. + +[[type-definitions-cypher-object-usage]] +=== On an object type field + +In the example below, a field `similarMovies` is bound to the `Movie` type, to find other movies with an overlap of actors: + +[source, graphql, indent=0] +---- +type Actor { + actorId: ID! + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) +} + +type Movie { + movieId: ID! + title: String + description: String + year: Int + actors(limit: Int = 10): [Actor!]! + @relationship(type: "ACTED_IN", direction: IN) + similarMovies(limit: Int = 10): [Movie] + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(:Actor)-[:ACTED_IN]->(rec:Movie) + WITH rec, COUNT(*) AS score ORDER BY score DESC + RETURN rec LIMIT $limit + """, + columnName: "rec" + ) +} +---- + +=== On a query type field + +The following example demonstrates a query to return all of the actors in the database: + +[source, graphql, indent=0] +---- +type Actor { + actorId: ID! + name: String +} + +type Query { + allActors: [Actor] + @cypher( + statement: """ + MATCH (a:Actor) + RETURN a + """, + columnName: "a" + ) +} +---- + +=== On a mutation type field + +The following example demonstrates a mutation using a Cypher query to insert a single actor with the specified name argument: + +[source, graphql, indent=0] +---- +type Actor { + actorId: ID! + name: String +} + +type Mutation { + createActor(name: String!): Actor + @cypher( + statement: """ + CREATE (a:Actor {name: $name}) + RETURN a + """, + columnName: "a" + ) +} +---- \ No newline at end of file diff --git a/modules/ROOT/pages/reference/directives/database-mapping.adoc b/modules/ROOT/pages/type-definitions/directives/database-mapping.adoc similarity index 55% rename from modules/ROOT/pages/reference/directives/database-mapping.adoc rename to modules/ROOT/pages/type-definitions/directives/database-mapping.adoc index 69a0db8e..78bef11b 100644 --- a/modules/ROOT/pages/reference/directives/database-mapping.adoc +++ b/modules/ROOT/pages/type-definitions/directives/database-mapping.adoc @@ -1,22 +1,16 @@ [[type-definitions-database-mapping]] -= Database Mapping +:description: This page describes how to use directives for database mapping. += Database mapping + +This page describes how to use directives for database mapping. [[type-definitions-alias]] == `@alias` This directive maps a GraphQL field to a Neo4j property on a node or relationship. +It can be used on any fields that are not `@cypher` or `@relationship` fields. -This can be used on any fields that are not `@cypher` or `@relationship` fields. - -=== Definition - -[source, graphql, indent=0] ----- -"""Indicates that the field is to be mapped to the underlying Neo4j under a different property name.""" -directive @alias(property: String!) on FIELD_DEFINITION ----- - -=== Usage +For example: [source, graphql, indent=0] ---- @@ -43,15 +37,14 @@ interface UserLivesInProperties @relationshipProperties { } ---- -==== Properties are automatically escaped - -The property in aliases are automatically escaped (wrapped with backticks ``), so there's no need to add escape characters around them. +Note that the property in aliases are automatically escaped (wrapped with backticks ``), so there is no need to add escape characters around them. [[type-definitions-plural]] == `@plural` -The `@plural` directive redefines how to compose the plural of the type for the generated operations. +This directive redefines how to compose the plural of the type for the generated operations. This is particularly useful for types that are not correctly pluralized or are non-English words. +Take this type definition as an example: [source, graphql, indent=0] ---- @@ -71,29 +64,19 @@ This way, instead of the wrongly generated `teches`, the type is properly writte } ---- -The same is applied to other operations such as `createTechs`. Note that database labels will not change. +The same is applied to other operations such as `createTechs`. +However, keep in mind that database labels are not changed with this directive. [[type-definitions-node]] == `@node` -The `@node` directive is used to specify the configuration of a GraphQL object type which represents a Neo4j node. +This directive is used to specify the configuration of a GraphQL object type which represents a Neo4j node. +It can be appended with the following optional parameters: -=== Definition +[discrete] +=== `labels` -[source, graphql, indent=0] ----- -"""Informs @neo4j/graphql of node metadata""" -directive @node( - """Map the GraphQL type to match Neo4j node labels""" - labels: [String] -) on OBJECT ----- - -=== Usage -`@node` can be used with the following optional parameters. - -==== `labels` -The parameter `labels` defines the list of label to be used in Neo4j instead of the GraphQL type name: +This parameter defines the list of label to be used in Neo4j instead of the GraphQL type name: [source, graphql, indent=0] ---- @@ -113,7 +96,7 @@ This way, the following query: } ---- -Generates the cypher query: +Generates the Cypher query: [source, cypher, indent=0] ---- @@ -141,7 +124,7 @@ This way, the following query: } ---- -Generates the cypher query: +Generates the Cypher query: [source, cypher, indent=0] ---- @@ -149,9 +132,13 @@ MATCH (this:Dog:K9) RETURN this { .name } as this ---- -Please note that defining `labels` means you take control of the database labels of the node. Indexes and constraints in Neo4j only support a single label, for which the first element of the `labels` argument will be used. +[NOTE] +==== +Defining `labels` means you take control of the database labels of the node. +Indexes and constraints in Neo4j only support a single label, for which the first element of the `labels` argument is used. +==== -The following example results in a unique constraint be asserted for the label `K9` and the property `name`: +The following example results in a unique constraint to be asserted for the label `K9` and the property `name`: [source, graphql, indent=0] ---- @@ -160,8 +147,11 @@ type Dog @node(labels: ["K9", "Dog"]) { } ---- -===== Using `$jwt` and `$context` -In some cases, we may want to generate dynamic labels depending on the user requesting. In these cases, we can use the variable `$jwt` to define a custom label define in the JWT: +[discrete] +=== Using `$jwt` and `$context` + +In some cases, you may want to generate dynamic labels depending on the user requesting. +For that, you can use the variable `$jwt` to define a custom label in the JWT: [source, graphql, indent=0] ---- @@ -170,7 +160,7 @@ type User @node(labels: ["$jwt.username"]) { } ---- -The following query will yield a different cypher query depending on the user JWT: +The following query yielda a different Cypher query depending on the user JWT: [source, graphql, indent=0] ---- @@ -181,7 +171,7 @@ The following query will yield a different cypher query depending on the user JW } ---- -Assuming an user with the value `"username": "arthur"` in JWT, the Cypher query will look like: +Assuming there is a user with the value `"username": "arthur"` in JWT, the Cypher query looks like: [source, cypher, indent=0] ---- diff --git a/modules/ROOT/pages/type-definitions/directives/default-values.adoc b/modules/ROOT/pages/type-definitions/directives/default-values.adoc new file mode 100644 index 00000000..91b6b072 --- /dev/null +++ b/modules/ROOT/pages/type-definitions/directives/default-values.adoc @@ -0,0 +1,117 @@ +[[type-definitions-default-values]] +:description: This page describes how to use default values in the Neo4j GraphQL Library. += Default values + +This page describes how to use default values in the Neo4j GraphQL Library. + +[[type-definitions-default-values-default]] +== `@default` + +When generating the input type for the `create`` mutation, the value specified in this directive is used as the default value for this field. + +=== Definition + +[source, graphql, indent=0] +---- +"""Int | Float | String | Boolean | ID | DateTime | Enum""" +scalar Scalar + +"""Instructs @neo4j/graphql to set the specified value as the default value in the CreateInput type for the object type in which this directive is used.""" +directive @default( + """The default value to use. Must be a scalar type and must match the type of the field with which this directive decorates.""" + value: Scalar!, +) on FIELD_DEFINITION +---- + +=== Usage + +`@default` may be used with enums. +When setting the default value for an enum field, it must be one of the enumerated enum values: + +[source, graphql, indent=0] +---- +enum Location { + HERE + THERE + EVERYWHERE +} + +type SomeType { + firstLocation: Location! @default(value: HERE) # valid usage + secondLocation: Location! @default(value: ELSEWHERE) # invalid usage, will throw an error +} +---- + +[[type-definitions-default-values-coalesce]] +== `@coalesce` + +When translating from GraphQL to Cypher, any instances of fields to which this directive is applied will be wrapped in a `coalesce()` function in the WHERE clause (see https://neo4j.com/developer/kb/understanding-non-existent-properties-and-null-values/#_use_coalesce_to_use_a_default_for_a_null_value). + +This directive helps querying against non-existent properties in a database. +However, it is encouraged to populate these properties with meaningful values if this is becoming the norm. The `@coalesce`directive is a primitive implementation of the function which only takes a static default value as opposed to using another property in a node or a Cypher expression. + +=== Definition + +[source, graphql, indent=0] +---- +"""Int | Float | String | Boolean | ID | DateTime | Enum""" +scalar ScalarOrEnum + +"""Instructs @neo4j/graphql to wrap the property in a coalesce() function during queries, using the single value specified.""" +directive @coalesce( + """The value to use in the coalesce() function. Must be a scalar type and must match the type of the field with which this directive decorates.""" + value: Scalar!, +) on FIELD_DEFINITION +---- + +=== Usage + +`@coalesce` may be used with enums. +When setting the default value for an enum field, it must be one of the enumerated enum values: + +[source, graphql, indent=0] +---- +enum Status { + ACTIVE + INACTIVE +} +type Movie { + status: Status @coalesce(value: ACTIVE) +} +---- + +[[type-definitions-default-values-limit]] +== `@limit` + +Available on nodes, this directive injects values into a query such as the `limit`. + +=== Definition + +[source, graphql, indent=0] +---- +"""The `@limit` is to be used on nodes, where applied will inject values into a query such as the `limit`.""" +directive @limit( + default: Int + max: Int +) on OBJECT +---- + +=== Usage + +The directive has two arguments: + +* `default` - if no `limit` argument is passed to the query, the default limit is used. +The query may still pass a higher or lower `limit`. +* `max` - defines the maximum limit to be passed to the query. +If a higher value is passed, it is used instead. +If no `default` value is set, `max` is used for queries without limit. + +[source, graphql, indent=0] +---- +{ + Movie @limit(amount: 5) { + title + year + } +} +---- diff --git a/modules/ROOT/pages/type-definitions/directives/index.adoc b/modules/ROOT/pages/type-definitions/directives/index.adoc new file mode 100644 index 00000000..e0de5e14 --- /dev/null +++ b/modules/ROOT/pages/type-definitions/directives/index.adoc @@ -0,0 +1,86 @@ +[[directives]] +:description: This page lists all directives available in the Neo4j GraphQL Library. += Directives + +The Neo4j GraphQL Library provides the following directives to be used whilst defining types: + +[cols="1,3"] +|=== +| Directive | Description + +| xref::/type-definitions/directives/database-mapping.adoc#type-definitions-alias[`@alias`] +| Maps a GraphQL schema field to a Neo4j property on a node or relationship. + +| xref::/type-definitions/directives/default-values.adoc#type-definitions-default-values-coalesce[`@coalesce`] +| Exposes a mechanism for querying against non-existent, `null` values on a node. + +| xref::custom-resolvers.adoc#custom-resolver-directive[`@customResolver`] +| Specifies that a field is resolved by a custom resolver, and allows the specification +of any required fields that is passed as arguments to the custom resolver. + +| xref::/type-definitions/directives/cypher.adoc[`@cypher`] +| Overrides field resolution (including `Query` and `Mutation` fields), instead resolving with the specified Cypher. + +| xref::/type-definitions/directives/default-values.adoc#type-definitions-default-values-default[`@default`] +| Allows for the setting of a default value for a field on object creation. + +| xref:/schema-configuration/field-configuration.adoc#_filterable[`@filterable`] +| Defines the filters generated for a field. + +| xref::/type-definitions/directives/indexes-and-constraints.adoc#type-definitions-indexes-fulltext[`@fulltext`] +| Indicates that there should be a fulltext index inserted into the database for the specified Node and its properties. + +| xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] +| Marks a field as the unique ID for an object type, and allows for autogeneration of IDs. + +| xref::/type-definitions/directives/default-values.adoc#type-definitions-default-values-limit[`@limit`] +| Used on nodes to inject values into Cypher `LIMIT` clauses. + +| xref:/schema-configuration/type-configuration.adoc#_mutation[`@mutation`] +| Limits the availability of Mutation operations in the library. + +| xref::/type-definitions/directives/database-mapping.adoc#type-definitions-node[`@node`] +| Specifies the configuration of a GraphQL object type which represents a Neo4j node. + +| xref::/type-definitions/directives/database-mapping.adoc#type-definitions-plural[`@plural`] +| Redefines how to compose the plural of the type for the generated operations. +This is particularly useful for types that are not correctly pluralized or are non-English words. + +| xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-populated-by[`@populatedBy`] +| Specifies a callback function that gets executed during GraphQL query parsing, +to populate fields which have not been provided within the input. + +| xref::ogm/private.adoc[`@private`] +| Protects fields which should only be available through the xref::ogm/index.adoc[OGM]. + +| xref:/schema-configuration/type-configuration.adoc#_query[`@query`] +| Limits the availability of Query operations in the library. + +| xref::/schema-configuration/field-configuration.adoc#_relationship[`@relationship`] +| Configure xref::/type-definitions/types/relationships.adoc[relationships] between object types. + +| *`@relationshipProperties`* +a| Required to help you distinguish between interfaces which are used for relationship properties, and otherwise. +Can only be used on interfaces, as per its definition: +[source, graphql, indent=0] +---- +"""Required to differentiate between interfaces for relationship properties, and otherwise.""" +directive @relationshipProperties on INTERFACE +---- + +| xref:/schema-configuration/field-configuration.adoc#_selectable[`@selectable`] +| Sets the availability of fields on queries and aggregations. + +| xref:/schema-configuration/field-configuration.adoc#_settable[`@settable`] +| Sets the availability of fields on the create and update inputs. + +| xref:/schema-configuration/type-configuration.adoc#_subscription[`@subscription`] +| Limits subscription operations in the library. + +| xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-timestamp[`@timestamp`] +| Flags fields to be used to store timestamps on create/update events. + +| xref::/type-definitions/directives/indexes-and-constraints.adoc#type-definitions-constraints-unique[`@unique`] +| Indicates that there should be a uniqueness constraint in the database for the fields that it is applied to. + +|=== \ No newline at end of file diff --git a/modules/ROOT/pages/reference/type-definitions/indexes-and-constraints.adoc b/modules/ROOT/pages/type-definitions/directives/indexes-and-constraints.adoc similarity index 59% rename from modules/ROOT/pages/reference/type-definitions/indexes-and-constraints.adoc rename to modules/ROOT/pages/type-definitions/directives/indexes-and-constraints.adoc index b435374f..754d75c3 100644 --- a/modules/ROOT/pages/reference/type-definitions/indexes-and-constraints.adoc +++ b/modules/ROOT/pages/type-definitions/directives/indexes-and-constraints.adoc @@ -1,7 +1,9 @@ [[type-definitions-indexes-and-constraints]] -= Indexes and Constraints +:description: This page describes how to use indexes and constraints in the Neo4j GraphQL Library. += Indexes and constraints + +This page describes how to use indexes and constraints in the Neo4j GraphQL Library. -[[type-definitions-constraints-unique]] == Unique node property constraints Unique node property constraints map to `@unique` directives used in your type definitions, which has the following definition: @@ -15,15 +17,16 @@ directive @unique( ) on FIELD_DEFINITION ---- -Additionally, the usage of the xref::reference/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] directive by default implies that there should be a unique node property constraint in the database for that property. +Additionally, the usage of the xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`] directive by default implies that there should be a unique node property constraint in the database for that property. -Using this directive does not automatically ensure the existence of these constraints, and you will need to run a function on server startup. See the section xref::reference/type-definitions/indexes-and-constraints.adoc#type-definitions-indexes-and-constraints-asserting[Asserting constraints] below for details. +Using this directive does not automatically ensure the existence of these constraints, and you will need to run a function on server startup. +See the section xref::/type-definitions/directives/indexes-and-constraints.adoc#_asserting_constraints[Asserting constraints] for details. -=== `@unique` directive usage +=== Usage -`@unique` directives can only be used in GraphQL object types representing nodes, and will only be applied for the "main" label for the node. You can find some examples below. +`@unique` directives can only be used in GraphQL object types representing nodes, and they are only applicable for the "main" label for the node. -In the following example, a unique constraint will be asserted for the label `Colour` and the property `hexadecimal`: +In the following example, a unique constraint is asserted for the label `Colour` and the property `hexadecimal`: [source, graphql, indent=0] ---- @@ -32,7 +35,7 @@ type Colour { } ---- -In the next example, a unique constraint with name `unique_colour` will be asserted for the label `Colour` and the property `hexadecimal`: +In the next example, a unique constraint with name `unique_colour` is asserted for the label `Colour` and the property `hexadecimal`: [source, graphql, indent=0] ---- @@ -41,7 +44,7 @@ type Colour { } ---- -The `@node` directive is used to change the database label mapping in this next example, so a unique constraint will be asserted for the first label in the list, `Color`, and the property `hexadecimal`: +The `@node` directive is used to change the database label mapping in this next example, so a unique constraint is asserted for the first label in the list, `Color`, and the property `hexadecimal`: [source, graphql, indent=0] ---- @@ -51,7 +54,7 @@ type Colour @node(labels: ["Color"]) { ---- In the following example, all labels specified in the `labels` argument of the `@node` directive are also checked when asserting constraints. -If there is a unique constraint specified for the `hexadecimal` property of nodes with the `Hue` label, but not the `Color` label, no error will be thrown and no new constraints created when running `assertIndexesAndConstraints`. +If there is a unique constraint specified for the `hexadecimal` property of nodes with the `Hue` label, but not the `Color` label, no error is thrown and no new constraints are created when running `assertIndexesAndConstraints`. [source, graphql, indent=0] ---- @@ -60,10 +63,10 @@ type Colour @node(labels: ["Color", "Hue"]) { } ---- -[[type-definitions-indexes-fulltext]] == Fulltext indexes You can use the `@fulltext` directive to add a https://neo4j.com/docs/cypher-manual/current/indexes-for-full-text-search/[Full text index] inside Neo4j. +For example: [source, graphql, indent=0] ---- @@ -71,7 +74,6 @@ input FullTextInput { indexName: String queryName: String fields: [String]! - name: String # Deprecated and will be removed in version 4.0. of the library. Use indexName instead. } """ @@ -80,11 +82,14 @@ Informs @neo4j/graphql that there should be a fulltext index in the database, al directive @fulltext(indexes: [FullTextInput]!) on OBJECT ---- -Using this directive does not automatically ensure the existence of these indexes, and you will need to run a function on server startup. See the section xref::reference/type-definitions/indexes-and-constraints.adoc#type-definitions-indexes-and-constraints-asserting[Asserting constraints] below for details. +Using this directive does not automatically ensure the existence of these indexes. +You need to run a function on server startup. +See the section xref::/type-definitions/directives/indexes-and-constraints.adoc#_asserting_constraints[Asserting constraints] for details. -=== Specifying the `@fulltext` directive +=== Specifying -The directive can be used on nodes. Here we add a Fulltext index, called 'ProductName', for the name field, on the Product node. +The `@fulltext` directive can be used on nodes. +In this example, a `Fulltext` index called "ProductName", for the name `field`, on the `Product` node, is added: [source, graphql, indent=0] ---- @@ -94,17 +99,17 @@ type Product @fulltext(indexes: [{ indexName: "ProductName", fields: ["name"] }] } ---- -When you run xref::reference/type-definitions/indexes-and-constraints.adoc#type-definitions-indexes-and-constraints-asserting[Asserting constraints] this shall do the index creation like so: +When you run xref::/type-definitions/directives/indexes-and-constraints.adoc#_asserting_constraints[Asserting constraints], they create the index like so: [source, cypher, indent=0] ---- CALL db.index.fulltext.createNodeIndex("ProductName", ["Product"], ["name"]) ---- -=== Using the `@fulltext` directive +=== Usage -For every index specified, a new top level query will be generated by the library. For example, for the type definitions above, -the following query and types would be generated: +For every index specified, a new top level query is generated by the library. +For example, for the previous type definitions, the following query and types are generated: [source, graphql, indent=0] ---- @@ -138,7 +143,8 @@ input FloatWhere { } ---- -This query can then be used to perform a https://lucene.apache.org/[Lucene full-text query] to match and return products. Below is an example of this: +This query can then be used to perform a https://lucene.apache.org/[Lucene full-text query] to match and return products. +Here is an example of this: [source, graphql, indent=0] ---- @@ -152,7 +158,7 @@ query { } ---- -The above query would produce results in the following format: +This query produces results in the following format: [source, json, indent=0] ---- @@ -182,7 +188,7 @@ The above query would produce results in the following format: } ---- -Additionally, it is possible to define a custom query name as part of the `@fulltext` directive, using the `queryName` argument as shown below: +Additionally, it is possible to define a custom query name as part of the `@fulltext` directive by using the `queryName` argument: [source, graphql, indent=0] ---- @@ -192,7 +198,7 @@ type Product @fulltext(indexes: [{ queryName: "CustomProductFulltextQuery", inde } ---- -This would then produce the following top-level query: +This produces the following top-level query: [source, graphql, indent=0] ---- @@ -202,7 +208,7 @@ type Query { } ---- -This query can then be used as shown below: +This query can then be used like this: [source, graphql, indent=0] ---- @@ -216,35 +222,11 @@ query { } ---- -==== Deprecated usage - - -NOTE: Querying full-text indexes in the following ways has been deprecated and will be removed in version 4.0. - -Once you specify the index, you will now gain a 'Top Level' `fulltext` key on the following operations: - -1. read -2. count -3. aggregate - -Here we use the `fulltext` key, and the phrase is using https://lucene.apache.org/[Lucene’s full-text query language] to match and return Products: - -[source, graphql, indent=0] ----- -query { - products(fulltext: { ProductName: { phrase: "beer OR cerveza" } }) { - name - } -} ----- - -> Note that you can only query one Fulltext index at once and that the fulltext key is only available on 'Top Level' queries. - - -[[type-definitions-indexes-and-constraints-asserting]] == Asserting constraints -In order to ensure that the specified constraints exist in the database, you will need to run the function `assertIndexesAndConstraints`, the full details of which can be found in the xref::reference/api-reference/neo4jgraphql.adoc#api-reference-assertconstraints[API reference]. A simple example to create the necessary constraints might look like the following, assuming a valid driver instance in the variable `driver`. This will create two constraints, one for each field decorated with `@id`, `@unique` and apply the indexes specified in `@fulltext`: +In order to ensure that the specified constraints exist in the database, you need to run the function `assertIndexesAndConstraints` (see more details in xref::reference/api-reference/neo4jgraphql.adoc#api-reference-assertconstraints[API reference]). +A simple example to create the necessary constraints might look like the following, assuming a valid driver instance in the variable `driver`. +This creates two constraints, one for each field decorated with `@id` and `@unique`, and apply the indexes specified in `@fulltext`: [source, javascript, indent=0] ---- diff --git a/modules/ROOT/pages/type-definitions/types/index.adoc b/modules/ROOT/pages/type-definitions/types/index.adoc new file mode 100644 index 00000000..b674d16f --- /dev/null +++ b/modules/ROOT/pages/type-definitions/types/index.adoc @@ -0,0 +1,53 @@ +[[types]] +:description: This page lists all types available in the Neo4j GraphQL Library. += Types + +The Neo4j GraphQL Library provides the following types: + +[cols="1,3"] +|=== +| Type | Description + +| xref::/type-definitions/types/scalar.adoc[`Int`] +| The `Int` scalar type represents a signed 32-bit numeric non-fractional value. + +| xref::/type-definitions/types/scalar.adoc[`Float`] +| The `Float` scalar type represents a signed double-precision fractional value. + +| xref::/type-definitions/types/scalar.adoc[`String`] +| The `String` scalar type represents textual data, represented as UTF-8 character sequences. + +| xref::/type-definitions/types/scalar.adoc[`Boolean`] +| The `Boolean` scalar type represents `true` or `false`. + +| xref::/type-definitions/types/scalar.adoc[`ID`] +| The `ID` scalar type represents a unique identifier, often used to refetch an object. + +| xref::/type-definitions/types/scalar.adoc[`BigInt`] +| The `BigInt` scalar type represents a signed 64-bit numeric non-fractional value. + +| xref:/type-definitions/types/temporal.adoc[`Date`] +| The `Date` scalar type is a ISO 8601 date mapping to the Neo4j `DATE` type. + +| xref::/type-definitions/types/temporal.adoc[`Time`] +| The `Time` scalar type is a ISO 8601 time of day and timezone mapping to the Neo4j `ZONED TIME` type. + +| xref::/type-definitions/types/temporal.adoc[`LocalTime`] +| The `LocalTime` scalar type is a ISO 8601 time of day mapping to the Neo4j `LOCAL TIME` type. + +| xref::/type-definitions/types/temporal.adoc[`DateTime`] +| The `DateTime` scalar type is a ISO 8601 date, time of day and timezone mapping to the Neo4j `ZONED DATETIME` type. + +| xref:/type-definitions/types/temporal.adoc[`LocalDateTime`] +| The `LocalDateTime` scalar type is a ISO 8601 date and time of day mapping to the Neo4j `LOCAL DATETIME` type. + +| xref::/type-definitions/types/temporal.adoc[`Duration`] +| The `Duration` scalar type is a ISO 8601 duration mapping to the Neo4j `DURATION` type. + +| xref::/type-definitions/types/spatial.adoc#point[`Point`] +| The `Point` object type is a WGS 84 3D geographic point. + +| xref::/type-definitions/types/spatial.adoc#cartesian-point[`CartesianPoint`] +| The `CartesianPoint` object type is a Cartesian 3D point. + +|=== \ No newline at end of file diff --git a/modules/ROOT/pages/type-definitions/types/interfaces.adoc b/modules/ROOT/pages/type-definitions/types/interfaces.adoc new file mode 100644 index 00000000..299820e1 --- /dev/null +++ b/modules/ROOT/pages/type-definitions/types/interfaces.adoc @@ -0,0 +1,456 @@ +[[type-definitions-interfaces]] +:description: This page describes how to use and define interfaces on relationship fields. += Interface types + +This page describes how to use and define interfaces on relationship fields. + +== Creating an interface field + +The following schema defines a `Actor` type, that has a relationship `ACTED_IN`, of type `[Production!]!`. `Production` is an interface type with `Movie` and `Series` implementations. Note in this example that relationship properties have also been used with the `@relationshipProperties` directive, so that interfaces representing relationship properties can be easily distinguished. + +[source, graphql, indent=0] +---- +interface Production { + title: String! + actors: [Actor!]! +} + +type Movie implements Production { + title: String! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + runtime: Int! +} + +type Series implements Production { + title: String! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + episodes: Int! +} + +interface ActedIn @relationshipProperties { + role: String! +} + +type Actor { + name: String! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") +} +---- + +These type definitions will be used for the rest of the examples in this chapter. + +=== Directive inheritance + +Any directives present on an interface or its fields will be "inherited" by any object types implementing it. For example, the type definitions above could be refactored to have the `@relationship` directive on the `actors` field in the `Production` interface instead of on each implementing type as it is currently: + +[source, graphql, indent=0] +---- +interface Production { + title: String! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") +} + +type Movie implements Production { + title: String! + actors: [Actor!]! + runtime: Int! +} + +type Series implements Production { + title: String! + actors: [Actor!]! + episodes: Int! +} + +interface ActedIn @relationshipProperties { + role: String! +} + +type Actor { + name: String! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") +} +---- + +[[type-definitions-interfaced-types-querying]] +== Querying an interface + +Which implementations are returned by a query are dictated by the `where` filter applied. + +For example, the following will return all productions with title starting "The " for every actor: + +[source, graphql, indent=0] +---- +query GetProductionsStartingWithThe { + actors { + name + actedIn(where: { node: { title_STARTS_WITH: "The " } }) { + title + ... on Movie { + runtime + } + ... on Series { + episodes + } + } + } +} +---- + +Whilst the query below will only return the movies with title starting with "The " for each actor. + +[source, graphql, indent=0] +---- +query GetMoviesStartingWithThe { + actors { + name + actedIn(where: { node: { _on: { Movie: { title_STARTS_WITH: "The " } } } }) { + title + ... on Movie { + runtime + } + } + } +} +---- + +This is to prevent overfetching, and you can find an explanation of this xref::troubleshooting.adoc#appendix-preventing-overfetching[here]. + +Alternatively, these implementation specific filters can be used to override filtering for a specific implementation. For example, if you wanted all productions with title starting with "The ", but movies with title starting with "A ", you could achieve this using the following: + +[source, graphql, indent=0] +---- +query GetProductionsStartingWith { + actors { + name + actedIn(where: { node: { title_STARTS_WITH: "The ", _on: { Movie: { title_STARTS_WITH: "A " } } } }) { + title + ... on Movie { + runtime + } + ... on Series { + episodes + } + } + } +} +---- + +== Creating using an interface field + +The below mutation creates an actor and some productions they've acted in: + +[source, graphql, indent=0] +---- +mutation CreateActorAndProductions { + createActors( + input: [ + { + name: "Chris Pratt" + actedIn: { + create: [ + { + edge: { + role: "Mario" + } + node: { + Movie: { + title: "Super Mario Bros" + runtime: 90 + } + } + } + { + edge: { + role: "Starlord" + } + node: { + Movie: { + title: "Guardians of the Galaxy" + runtime: 122 + } + } + } + { + edge: { + role: "Andy" + } + node: { + Series: { + title: "Parks and Recreation" + episodes: 126 + } + } + } + ] + } + } + ] + ) { + actors { + name + actedIn { + title + } + } + } +} +---- + +== Nested interface operations + +Operations on interfaces are abstract until you instruct them not to be. +Take the following example: + +[source, graphql, indent=0] +---- +mutation CreateActorAndProductions { + updateActors( + where: { name: "Woody Harrelson" } + connect: { + actedIn: { + where: { node: { title: "Zombieland" } } + connect: { actors: { where: { node: { name: "Emma Stone" } } } } + } + } + ) { + actors { + name + actedIn { + title + } + } + } +} +---- + +The above mutation: + +. Finds any `Actor` nodes with the name "Woody Harrelson". +. Connects the "Woody Harrelson" node to a `Production` node with the title "Zombieland". +. Connects the connected `Production` node to any `Actor` nodes with the name "Emma Stone". + +This query, however, is fully abstract. +If you want to only connect `Movie` nodes to `Actor` nodes with name "Emma Stone", you could instead do: + +[source, graphql, indent=0] +---- +mutation CreateActorAndProductions { + updateActors( + where: { name: "Woody Harrelson" } + connect: { + actedIn: { + where: { node: { title: "Zombieland" } } + connect: { _on: { Movie: { actors: { where: { node: { name: "Emma Stone" } } } } } } + } + } + ) { + actors { + name + actedIn { + title + } + } + } +} +---- + +Alternatively, you can also make sure you only connect to `Movie` nodes with title "Zombieland": + +[source, graphql, indent=0] +---- +mutation CreateActorAndProductions { + updateActors( + where: { name: "Woody Harrelson" } + connect: { + actedIn: { + where: { node: { _on: { Movie: { title: "Zombieland" } } } } + connect: { actors: { where: { node: { name: "Emma Stone" } } } } + } + } + ) { + actors { + name + actedIn { + title + } + } + } +} +---- + +== Directive inheritance + +For the next example, consider the following schema. +It defines an `Actor` type, that has a relationship `ACTED_IN`, of type `[Production!]!`. +`Production` is an interface type with `Movie` and `Series` implementations. +In this example, relationship properties have also been used with the `@relationshipProperties` directive, so that interfaces representing relationship properties can be easily distinguished: + +[source, graphql, indent=0] +---- +interface Production { + title: String! + actors: [Actor!]! +} + +type Movie implements Production { + title: String! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + runtime: Int! +} + +type Series implements Production { + title: String! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + episodes: Int! +} + +interface ActedIn @relationshipProperties { + role: String! +} + +type Actor { + name: String! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") +} +---- + +Now, considering that any xref::/type-definitions/directives/index.adoc[directives] present on an interface or its fields are "inherited" by any object types implementing it, the example schema could be refactored to have the `@relationship` directive on the `actors` field in the `Production` interface instead of on each implementing type as it is currently. +That is how it would look like: + +[source, graphql, indent=0] +---- +interface Production { + title: String! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") +} + +type Movie implements Production { + title: String! + actors: [Actor!]! + runtime: Int! +} + +type Series implements Production { + title: String! + actors: [Actor!]! + episodes: Int! +} + +interface ActedIn @relationshipProperties { + role: String! +} + +type Actor { + name: String! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") +} +---- + +=== Overriding + +In addition to inheritance, directives can be overridden on a per-implementation basis. +Say you had an interface defining some `Content`, with some basic authorization rules, such as: + +[source, graphql, indent=0] +---- +interface Content + @auth(rules: [{ operations: [CREATE, UPDATE, DELETE], allow: { author: { username: "$jwt.sub" } } }]) { + title: String! + author: [Author!]! @relationship(type: "HAS_CONTENT", direction: IN) +} + +type User { + username: String! + content: [Content!]! @relationship(type: "HAS_CONTENT", direction: OUT) +} +---- + +You might implement this once for public content and once for private content which has additional rules in place: + +[source, graphql, indent=0] +---- +type PublicContent implements Content { + title: String! + author: [Author!]! +} + +type PrivateContent implements Content + @auth(rules: [{ operations: [CREATE, READ, UPDATE, DELETE], allow: { author: { username: "$jwt.sub" } } }]) { + title: String! + author: [Author!]! +} +---- + +The `PublicContent` type inherits the auth rules from the `Content` interface, while the `PrivateContent` type uses the auth rules specified there. + +In summary, there are three choices for the application of directives when using interfaces: + +* Directives specified on the interface and inherited by all implementing types when the directives for every type are the same. +* Directives specified on the interface and overridden by certain implementing types when directives are broadly the same with a few discrepancies. +* Directives specified on implementing types alone when there is very little commonality between types, or certain types need a directive and others don't. + +== Querying an interface + +In order to set which implementations are returned by a query, a filter `where` needs to be applied. +For example, the following query returns all productions (`movies` and `series`) with title starting "The " for every actor: + +[source, graphql, indent=0] +---- +query GetProductionsStartingWithThe { + actors { + name + actedIn(where: { node: { title_STARTS_WITH: "The " } }) { + title + ... on Movie { + runtime + } + ... on Series { + episodes + } + } + } +} +---- + +This query, on the other hand, only returns the movies with title starting with "The" for each actor: + +[source, graphql, indent=0] +---- +query GetMoviesStartingWithThe { + actors { + name + actedIn(where: { node: { _on: { Movie: { title_STARTS_WITH: "The " } } } }) { + title + ... on Movie { + runtime + } + } + } +} +---- + +This approach aims to prevent overfetching. +For more information, read the page xref::troubleshooting.adoc#appendix-preventing-overfetching[Troubleshooting -> Preventing overfetching]. + +Alternatively, these specific filters can also be used to override filtering for a specific implementation. +For example, if you want to fetch all `series` with title starting with "The " and `movies` with title starting with "A ", you can do it like that: + +[source, graphql, indent=0] +---- +query GetProductionsStartingWith { + actors { + name + actedIn(where: { node: { title_STARTS_WITH: "The ", _on: { Movie: { title_STARTS_WITH: "A " } } } }) { + title + ... on Movie { + runtime + } + ... on Series { + episodes + } + } + } +} +---- + diff --git a/modules/ROOT/pages/reference/type-definitions/relationships.adoc b/modules/ROOT/pages/type-definitions/types/relationships.adoc similarity index 55% rename from modules/ROOT/pages/reference/type-definitions/relationships.adoc rename to modules/ROOT/pages/type-definitions/types/relationships.adoc index a830a1a2..e28f9214 100644 --- a/modules/ROOT/pages/reference/type-definitions/relationships.adoc +++ b/modules/ROOT/pages/type-definitions/types/relationships.adoc @@ -1,17 +1,19 @@ [[type-definitions-relationships]] +:description: This page describes how to write type definitions for a simple connected model, inserting data through the schema, and then querying it. = Relationships -Without relationships, your type definitions are simply a collection of disconnected nodes, with little value. Adding relationships into your data model gives your data the context that it needs to run complex queries across wide sections of your graph. This section will run through writing some type definitions for a simple connected model, inserting some data through the schema, and then querying it. +Without relationships, your type definitions work rather as a collection of disconnected nodes, with little value. +Adding relationships into your data model gives your data the context that it needs to run complex queries across wide sections of your graph. -== Example graph +This page describes how to write type definitions for a simple connected model, inserting data through the schema, and then querying it. -The following graph will be used in this example, where a Person type has two different relationship types which can connect it to a Movie type. +== Type definitions -image::relationships.svg[title="Example graph"] +Take the following graph as an example in which a `Person` type has two different relationship types, which can connect it to a `Movie` type. -== Type definitions +image::relationships.svg[title="Example graph"] -First, to define the nodes, you should define the two distinct types in this model: +To create that graph using the Neo4j GraphQL Library, first you need to define the nodes and define the two distinct types in this model: [source, graphql, indent=0] ---- @@ -45,21 +47,23 @@ type Movie { } ---- -The following should be noted about the fields you just added: +Note that, in this query: -* A Person can act in or direct multiple movies, and a Movie can have multiple actors. However, it is exceedingly rare for a Movie to have more than one director, and you can model this cardinality in your type definitions, to ensure accuracy of your data. -* A Movie isn't really a Movie without a director, and this has been signified by marking the `director` field as non-nullable, meaning that a Movie must have a `DIRECTED` relationship coming into it. -* To figure out whether the `direction` argument of the `@relationship` directive should be `IN` or `OUT`, visualise your relationships like in the diagram above, and model out the direction of the arrows. -* The @relationship directive is a reference to Neo4j relationships, whereas in the schema, the phrase edge(s) is used to be consistent with the general API language used by Relay. +* A `Person` can _act in_ or _direct_ multiple movies, and a `Movie` can have multiple actors. +However, it is rare for a `Movie` to have more than one director, so you can model this cardinality in your type definitions, to ensure accuracy of your data. +* A `Movie` isn't really a `Movie` without a director, and this has been signified by marking the `director` field as non-nullable. +This means that a `Movie` *must* have a `DIRECTED` relationship coming into it to be valid. +* To figure out whether the `direction` argument of the `@relationship` directive should be `IN` or `OUT`, visualize your relationships like in the diagram above, then model out the direction of the arrows. +* The `@relationship` directive is a reference to Neo4j relationships, whereas in the schema, the phrase `edge(s)` is used to be consistent with the general API language used by Relay. -=== Relationship Properties +=== Relationship properties -Relationship properties can be added to the above type definitions in two steps: +You can add relationship properties to the example in two steps: -1. Add an interface definition, decorated with the `@relationshipProperties` directive, containing the desired relationship properties -2. Add a `properties` argument to both "sides" (or just one side, if you prefer) of the `@relationship` directive which points to the newly defined interface +. Add an interface definition decorated with the `@relationshipProperties` directive and containing the desired relationship properties. +. Add a `properties` argument to both "sides" (or just one side, if you prefer) of the `@relationship` directive which points to the newly defined interface. -For example, to distinguish which roles an actor played in a movie: +For example, suppose you want to distinguish which roles an actor played in a movie: [source, graphql, indent=0] ---- @@ -82,9 +86,11 @@ interface ActedIn @relationshipProperties { } ---- -=== QueryDirection -All relationships have a direction, however, when querying it is possible to perform xref::queries-aggregations/queries.adoc#_undirected_queries[undirected queries]. -When defining a relationship you can define the default behaviour when performing queries over this relationship with the argument `queryDirection`: +=== `queryDirection` + +All relationships have a direction. +However, when querying them, it is possible to perform xref::queries-aggregations/queries.adoc#_undirected_queries[undirected queries]. +To set the default behavior of a relationship when it is queried, you can use the argument `queryDirection`: [source, graphql, indent=0] ---- @@ -97,14 +103,17 @@ type Person { `queryDirection` can have the following values: -* `DEFAULT_DIRECTED` (default): All queries will be **directed** by default, but users may perform undirected queries. -* `DEFAULT_UNDIRECTED`: All queries will be **undirected** by default, but users may perform directed queries. -* `DIRECTED_ONLY`: Only directed queries can be perform on this relationship. -* `UNDIRECTED_ONLY`: Only undirected queries can be perform on this relationship. +* `DEFAULT_DIRECTED` (default): all queries are **directed** by default, but users may perform undirected queries. +* `DEFAULT_UNDIRECTED`: all queries are **undirected** by default, but users may perform directed queries. +* `DIRECTED_ONLY`: only directed queries can be performed on this relationship. +* `UNDIRECTED_ONLY`: only undirected queries can be performed on this relationship. == Inserting data -Nested mutations mean that there are many ways in which you can insert data into your database through the GraphQL schema. You can't create a Movie without adding a director, and you can do that by either creating the director first and then creating and connecting the movie, or you can create both the Movie and the director in the same mutation. With the latter approach: +Nested mutations mean that there are many ways in which you can insert data into your database through the GraphQL schema. +Consider the previously mentioned rule that a `Movie` node cannot be created without adding a director. +You can, however, create a director node first and then create and connect it to a `Movie`. +Another option is to create both `Movie` and `Director` in the same mutation, for example: [source, graphql, indent=0] ---- @@ -135,7 +144,7 @@ mutation CreateMovieAndDirector { } ---- -You then need to create the actor in this example, and connect them to the new Movie node, also specifying which roles they played: +You then need to create the actor in this example, and connect them to the new `Movie` node, also specifying which roles they played: [source, graphql, indent=0] ---- @@ -179,7 +188,8 @@ mutation CreateActor { Note the selection of the `actorsConnection` field in order to query the `roles` relationship property. -As you can see, these nested mutations are very powerful, and in the second mutation you ran, you were able to return the entire graph which was created in this example. In fact, these mutations can actually be compressed down into a single mutation which inserts all of the data needed: +Also observe that, in the second mutation, the entire graph was returned. +That is not necessary, since you can compress down these mutations into one single operation that inserts all of the data needed: [source, graphql, indent=0] ---- @@ -232,11 +242,11 @@ mutation CreateMovieDirectorAndActor { } ---- -Once you get your head around this, you'll be creating giant sub-graphs in one mutation in no time! +Acknowledging this helps you create bigger sub-graphs in one mutation at once and, therefore, more efficiently. == Fetching your data -Now that you have the Movie information in your database, you can query all of the information which you just inserted as follows: +Now that you have the `Movie` information in your database, you can query everything altogether as follows: [source, graphql, indent=0] ---- @@ -263,7 +273,8 @@ query { == Cardinality -The Neo4j GraphQL Library has type definition requirements for "many" relationships, so given this example: +The Neo4j GraphQL Library has type definition requirements for "many" relationship. +For example: [source, graphql, indent=0] ---- @@ -277,17 +288,5 @@ type Post { } ---- -The relationship at `User.posts` is considered a "many" relationship. Relationships such as the one above should always be of type `NonNullListType` and `NonNullNamedType`, meaning both the array and the type inside of it should have a `!`. - -== Relationship types are automatically escaped - -Relationship types are automatically escaped (wrapped with backticks ``), so there's no need to add escape characters around the relationship type names. For example, do this: - -[source, graphql, indent=0] ----- -type User { - name: String! - posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) -} ----- - +The relationship at `User.posts` is considered a "many" relationship, which means it should always be of type `NonNullListType` and `NonNullNamedType`. +In other words, both the array and the type inside of a "many" relationship should have a `!`. \ No newline at end of file diff --git a/modules/ROOT/pages/type-definitions/types/scalar.adoc b/modules/ROOT/pages/type-definitions/types/scalar.adoc new file mode 100644 index 00000000..67315112 --- /dev/null +++ b/modules/ROOT/pages/type-definitions/types/scalar.adoc @@ -0,0 +1,83 @@ +[[type-definitions-types]] +:description: This page lists the default types available in the Neo4j GraphQL Library. += Scalar types + +Neo4j GraphQL supports all of the built-in GraphQL https://graphql.org/learn/schema/#scalar-types[scalar types] as well as additional scalar and object types specific to the Neo4j database. + +== Scalar types + +[cols="1,2,2"] +|=== +| Type | Description | Example + +| `Int` +| Supports up to 53-bit values. +a| +[source, graphql, indent=0] +---- +type Person { + age: Int! +} +---- + +.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#filtering-numerical-operators[Numerical operators] as the other numeric types. +a| +[source, graphql, indent=0] +---- +type File { + size: BigInt +} +---- + +| Can be passed as a number (does not need quotes) when used directly in a query or mutation. +a| +[source, graphql, indent=0] +---- +query { + files(where: { size: 9223372036854775807 }) { + size + } +} +---- + +| `Float` +| Placeholder text to make the table prettier. +a| +[source, graphql, indent=0] +---- +type Product { + price: Float! +} +---- + +| `String` +| Stored as a string in the database and always returned as a string. +a| +[source, graphql, indent=0] +---- +type Product { + name: String! +} +---- + +| `Boolean` +| Placeholder text to make the table prettier. +a| +[source, graphql, indent=0] +---- +type Product { + inStock: Boolean! +} +---- + +| `ID` +| Placeholder text to make the table prettier. +[source, graphql, indent=0] +---- +type Product { + id: ID! +} +---- +|=== \ No newline at end of file diff --git a/modules/ROOT/pages/type-definitions/types/spatial.adoc b/modules/ROOT/pages/type-definitions/types/spatial.adoc new file mode 100644 index 00000000..6f0b6958 --- /dev/null +++ b/modules/ROOT/pages/type-definitions/types/spatial.adoc @@ -0,0 +1,185 @@ + +[[type-definitions-spatial-types]] +:description: This page lists the spatial types available in the Neo4j GraphQL Library. += Temporal types + +== Spatial Types + +Neo4j GraphQL spatial types translate to spatial values stored using https://neo4j.com/docs/cypher-manual/current/values-and-types/spatial/[`Point`] in the database. +The use of either of these types in a GraphQL schema automatically introduces the types needed to run queries and mutations relevant to these spatial types. + +[[point]] +=== `Point` + +The `Point` type is used to describe the two https://neo4j.com/docs/cypher-manual/current/values-and-types/spatial/#spatial-values-crs-geographic[Geographic coordinate reference systems] supported by Neo4j. + +In order to use it in your schema, you quite simply add a field with a type `Point` to any type or types in schema, like the following: + +[source, graphql, indent=0] +---- +type TypeWithPoint { + location: Point! +} +---- + +Once this has been done, the `Point` type is automatically added to your schema, in addition to all of the input and output types you need to query and manipulate spatial types through your API. + +These are the automatically generated types and how to use them: + +==== Type definition + +[source, graphql, indent=0] +---- +type Point { + latitude: Float! + longitude: Float! + height: Float +} +---- + +==== Queries and mutations + +Due to the fact that `Point` is an object type, it has an additional type for input in queries and mutations. +However, this input type has the same shape as the object type: + +[source, graphql, indent=0] +---- +input PointInput { + latitude: Float! + longitude: Float! + height: Float +} +---- + +For example, you can query for a `User` with an exact location: + +[source, graphql, indent=0] +---- +query Users($longitude: Float!, $latitude: Float!) { + users(where: { location: { longitude: $longitude, latitude: $latitude } }) { + name + location { + longitude + latitude + } + } +} +---- + +Or you can create a `User` with a location as follows: + +[source, graphql, indent=0] +---- +mutation CreateUsers($name: String!, $longitude: Float!, $latitude: Float!) { + createUsers(input: [{ name: $name, location: { longitude: $longitude, latitude: $latitude } }]) { + users { + name + location { + longitude + latitude + } + } + } +} +---- + +==== Filtering + +Besides the xref::queries-aggregations/filtering.adoc#filtering-numerical-operators[Numerical operators], the `Point` type has an additional `_DISTANCE` filter. +Here is a list of what each filter does: + +* `_LT`: checks that the specified `Point` field is less than the `distance` away in meters from the `Point` being compared against. +* `_LTE`: checks that the specified `Point` field is less than or equal to the `distance` away in meters from the `Point` being compared against. +* `_DISTANCE`: checks that the specified `Point` field is the exact `distance` away in meters from the `Point` being compared against. +* `_GTE`: checks that the specified `Point` field is greater than the `distance` away in meters from the `Point` being compared against. +* `_GT`: checks that the specified `Point` field is greater than or equal to the `distance` away in meters from the `Point` being compared against. + +All of the filters take the following type as an argument: + +[source, graphql, indent=0] +---- +input PointDistance { + point: Point! + distance: Float! +} +---- + +In practice, you can construct queries such as the following which can find 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 + } + } +} +---- + +[[cartesian-point]] +=== `CartesianPoint` + +The `CartesianPoint` type is used to describe the two https://neo4j.com/docs/cypher-manual/current/values-and-types/spatial/#spatial-values-crs-cartesian[Cartesian coordinate reference systems] supported by Neo4j. + +To use it in the schema, add a field with a type `CartesianPoint` to any type or types, such as in this example: + +[source, graphql, indent=0] +---- +type TypeWithCartesianPoint { + location: CartesianPoint! +} +---- + +Once this has been done, the `CartesianPoint` type is automatically added to your schema, in addition to all of the input and output types you will need to query and manipulate spatial types through your API. + +These are the automatically generated types and how to use them: + +==== Type definition + +[source, graphql, indent=0] +---- +type CartesianPoint { + x: Float! + y: Float! + z: Float +} +---- + +==== Queries and mutations + +Due to the fact that `CartesianPoint` is an object type, it has an additional type for input in queries and mutations. +However, this input type has the same shape as the object type: + +[source, graphql, indent=0] +---- +input CartesianPointInput { + x: Float! + y: Float! + z: Float +} +---- + +==== Filtering + +Besides the xref::queries-aggregations/filtering.adoc#filtering-numerical-operators[Numerical operators], the `CartesianPoint` type has an additional `_DISTANCE` filter. + +Here is a list of what each filter does: + +* `_LT`: checks that the specified `Point` field is less than the `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. +* `_LTE`: checks that the specified `Point` field is less than or equal to the `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. +* `_DISTANCE`: checks that the specified `Point` field is the exact `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. +* `_GTE`: checks that the specified `Point` field is greater than the `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. +* `_GT`: checks that the specified `Point` field is greater than or equal to the `distance` away from the `CartesianPoint` being compared against, in the units used to specify the points. + +All of the filters take the following type as an argument: + +[source, graphql, indent=0] +---- +input CartesianPointDistance { + point: CartesianPoint! + distance: Float! +} +---- \ No newline at end of file diff --git a/modules/ROOT/pages/type-definitions/types/temporal.adoc b/modules/ROOT/pages/type-definitions/types/temporal.adoc new file mode 100644 index 00000000..2d62387c --- /dev/null +++ b/modules/ROOT/pages/type-definitions/types/temporal.adoc @@ -0,0 +1,77 @@ +[[type-definitions-temporal-types]] +:description: This page lists the temporal types available in the Neo4j GraphQL Library. += Temporal types + +Neo4j GraphQL supports all of the default GraphQL https://graphql.org/learn/schema/#scalar-types[scalar types] as well as additional scalar and object types specific to the Neo4j database, such as the temporal types. + +== Temporal types + +[cols="1,2,2"] +|=== +| Type | Description | Example + +| `DateTime` +| ISO datetime string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[datetime] temporal type. +a| +[source, graphql, indent=0] +---- +type User { + createdAt: DateTime +} +---- + +| `Date` +| "YYYY-MM-DD" date string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[date] temporal type. +a| +[source, graphql, indent=0] +---- +type Movie { + releaseDate: Date +} +---- + +| `Duration` +a| ISO 8601 duration string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#cypher-temporal-durations[duration] type. +[NOTE] +==== +Decimal values are not currently accepted on `[YMWD]`. +Comparisons are made according to the https://neo4j.com/developer/cypher/dates-datetimes-durations/#comparing-filtering-values[Cypher Developer Guide]. +==== +a| +[source, graphql, indent=0] +---- +type Movie { + runningTime: Duration! +} +---- + +| `LocalDateTime` +| "YYYY-MM-DDTHH:MM:SS" datetime string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[LocalDateTime] temporal type. +a| +[source, graphql, indent=0] +---- +type Movie { + nextShowing: LocalDateTime +} +---- + +| `Time` +| RFC3339 time string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[Time] temporal type. +a| +[source, graphql, indent=0] +---- +type Movie { + nextShowing: Time +} +---- + +| `LocalTime` +| "HH:MM:SS[.sss+]" time string stored as a https://neo4j.com/docs/cypher-manual/current/values-and-types/temporal/#_temporal_value_types[LocalTime] temporal type. +a| +[source, graphql, indent=0] +---- +type Movie { + nextShowing: LocalTime +} +---- +|=== \ No newline at end of file diff --git a/modules/ROOT/pages/reference/type-definitions/unions.adoc b/modules/ROOT/pages/type-definitions/types/unions.adoc similarity index 67% rename from modules/ROOT/pages/reference/type-definitions/unions.adoc rename to modules/ROOT/pages/type-definitions/types/unions.adoc index 29c42662..06e8d219 100644 --- a/modules/ROOT/pages/reference/type-definitions/unions.adoc +++ b/modules/ROOT/pages/type-definitions/types/unions.adoc @@ -1,7 +1,12 @@ [[type-definitions-unions]] -= Union Types +:description: This page describes how to use unions on relatinship fields with the Neo4j GraphQL Library. += Union types -The Neo4j GraphQL Library supports the use of unions on relationship fields. For example, the following schema defines a `User` type, that has a relationship `HAS_CONTENT`, of type `[Content!]!`. `Content` is of type `union` representing either a `Blog` or a `Post`. +The Neo4j GraphQL Library supports the use of unions on relationship fields. + +As an example, consider the following schema. +It defines a `User` type that has a relationship `HAS_CONTENT`, of type `[Content!]!`. +`Content` is of type `union`, representing either a `Blog` or a `Post`: [source, graphql, indent=0] ---- @@ -22,50 +27,9 @@ type User { } ---- -Below you can find some examples of how queries and mutations work with this example. - -[[type-definitions-unions-querying]] -== Querying a union - -Which union members are returned by a Query are dictated by the `where` filter applied. - -For example, the following will return all user content, and you will specifically get the title of each blog. - -[source, graphql, indent=0] ----- -query GetUsersWithBlogs { - users { - name - content { - ... on Blog { - title - } - } - } -} ----- - -Whilst the query below will only return blogs. You could for instance use a filter to check that the title is not null to essentially return all blogs: - -[source, graphql, indent=0] ----- -query GetUsersWithAllContent { - users { - name - content(where: { Blog: { NOT: { title: null } }}) { - ... on Blog { - title - } - } - } -} ----- - -This is to prevent overfetching, and you can find an explanation of this xref::troubleshooting.adoc#appendix-preventing-overfetching[here]. - == Creating a union -The below mutation creates the user and their content: +To create the union featured in the example, you need to do this mutation: [source, graphql, indent=0] ---- @@ -103,3 +67,42 @@ mutation CreateUserAndContent { } } ---- + +== Querying a union + +Which union members are returned by a query are dictated by the `where` filter applied to the query. +The following example returns all user content, more specifically the title of each blog: + +[source, graphql, indent=0] +---- +query GetUsersWithBlogs { + users { + name + content { + ... on Blog { + title + } + } + } +} +---- + +While this particular query only returns blogs, you could for instance use a filter to check that the title is not null when the list of blogs is returned: + +[source, graphql, indent=0] +---- +query GetUsersWithAllContent { + users { + name + content(where: { Blog: { NOT: { title: null } }}) { + ... on Blog { + title + } + } + } +} +---- + +This also helps with preventing overfetching. +See more information in the xref::troubleshooting.adoc#appendix-preventing-overfetching[Troubleshooting] section. +