diff --git a/.changeset/eighty-ads-develop.md b/.changeset/eighty-ads-develop.md new file mode 100644 index 0000000000..c5d6172b6f --- /dev/null +++ b/.changeset/eighty-ads-develop.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Specifying Cypher query options to be used is now `cypherQueryOptions` instead of just `queryOptions`, and each option accepts a simple string rather than an enum. diff --git a/docs/modules/ROOT/pages/migration/v4-migration/index.adoc b/docs/modules/ROOT/pages/migration/v4-migration/index.adoc index 2705b17334..3c50b59b47 100644 --- a/docs/modules/ROOT/pages/migration/v4-migration/index.adoc +++ b/docs/modules/ROOT/pages/migration/v4-migration/index.adoc @@ -778,3 +778,54 @@ type Actor @query(aggregate: true) { } ---- +=== Cypher query options + +If you had a need to pass in Cypher query options for query tuning, this interface has been changed. + +The config option `queryOptions` has now become `cypherQueryOptions`, and it now accepts simple strings instead of enums. + +The following is an example before the change: + +[source, javascript, indent=0] +---- +const { Neo4jGraphQL, CypherRuntime } = require("@neo4j/graphql"); +const { ApolloServer } = require("apollo-server"); + +const typeDefs = ` + type Movie { + title: String! + } +`; + +const neoSchema = new Neo4jGraphQL({ + typeDefs, + config: { + queryOptions: { + runtime: CypherRuntime.INTERPRETED, + }, + }, +}); +---- + +This is what is required after the change: + +[source, javascript, indent=0] +---- +const { Neo4jGraphQL } = require("@neo4j/graphql"); +const { ApolloServer } = require("apollo-server"); + +const typeDefs = ` + type Movie { + title: String! + } +`; + +const neoSchema = new Neo4jGraphQL({ + typeDefs, + config: { + cypherQueryOptions: { + runtime: "interpreted", + }, + }, +}); +---- diff --git a/docs/modules/ROOT/pages/troubleshooting.adoc b/docs/modules/ROOT/pages/troubleshooting.adoc index 98d3a971ea..cb6bccd744 100644 --- a/docs/modules/ROOT/pages/troubleshooting.adoc +++ b/docs/modules/ROOT/pages/troubleshooting.adoc @@ -46,7 +46,7 @@ For example, in order to set the Cypher runtime to "interpreted": [source, javascript, indent=0] ---- -const { Neo4jGraphQL, CypherRuntime } = require("@neo4j/graphql"); +const { Neo4jGraphQL } = require("@neo4j/graphql"); const neo4j = require("neo4j-driver"); const { ApolloServer } = require("apollo-server"); @@ -65,8 +65,8 @@ const neoSchema = new Neo4jGraphQL({ typeDefs, driver, config: { - queryOptions: { - runtime: CypherRuntime.INTERPRETED, + cypherQueryOptions: { + runtime: "interpreted", }, }, }); @@ -83,6 +83,47 @@ neoSchema.getSchema().then((schema) => { }); ---- +These options can also be set in the context function, which will take precedence: + +[source, javascript, indent=0] +---- +const { Neo4jGraphQL } = require("@neo4j/graphql"); +const neo4j = require("neo4j-driver"); +const { ApolloServer } = require("apollo-server"); + +const typeDefs = ` + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "bolt://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neoSchema = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +neoSchema.getSchema().then((schema) => { + const server = new ApolloServer({ + schema, + context: ({ req }) => ({ + req, + cypherQueryOptions: { + runtime: "interpreted", + }, + }), + }); + + server.listen().then(({ url }) => { + console.log(`Server ready at ${url}`); + }); +}); +---- + [[troubleshooting-faqs]] == FAQs diff --git a/packages/graphql/src/classes/Executor.ts b/packages/graphql/src/classes/Executor.ts index d42ff51b46..0ef79d7704 100644 --- a/packages/graphql/src/classes/Executor.ts +++ b/packages/graphql/src/classes/Executor.ts @@ -79,7 +79,7 @@ export type ExecutionContext = Driver | Session | Transaction; export type ExecutorConstructorParam = { executionContext: ExecutionContext; - queryOptions?: CypherQueryOptions; + cypherQueryOptions?: CypherQueryOptions; database?: string; bookmarks?: string | string[]; measureTime?: boolean; @@ -94,15 +94,15 @@ export class Executor { public lastBookmark: string | null; - private queryOptions: CypherQueryOptions | undefined; + private cypherQueryOptions: CypherQueryOptions | undefined; private database: string | undefined; private bookmarks: string | string[] | undefined; - constructor({ executionContext, queryOptions, database, bookmarks }: ExecutorConstructorParam) { + constructor({ executionContext, cypherQueryOptions, database, bookmarks }: ExecutorConstructorParam) { this.executionContext = executionContext; this.lastBookmark = null; - this.queryOptions = queryOptions; + this.cypherQueryOptions = cypherQueryOptions; this.database = database; this.bookmarks = bookmarks; } @@ -157,12 +157,12 @@ export class Executor { } private generateQuery(query: string): string { - if (this.queryOptions && Object.keys(this.queryOptions).length) { - const queryOptions = `CYPHER ${Object.entries(this.queryOptions) + if (this.cypherQueryOptions && Object.keys(this.cypherQueryOptions).length) { + const cypherQueryOptions = `CYPHER ${Object.entries(this.cypherQueryOptions) .map(([key, value]) => `${key}=${value}`) .join(" ")}`; - return `${queryOptions}\n${query}`; + return `${cypherQueryOptions}\n${query}`; } return query; diff --git a/packages/graphql/src/classes/Neo4jGraphQL.ts b/packages/graphql/src/classes/Neo4jGraphQL.ts index edfa46f313..8fd01c5c47 100644 --- a/packages/graphql/src/classes/Neo4jGraphQL.ts +++ b/packages/graphql/src/classes/Neo4jGraphQL.ts @@ -62,7 +62,7 @@ export interface Neo4jGraphQLConfig { driverConfig?: DriverConfig; enableDebug?: boolean; startupValidation?: StartupValidationConfig; - queryOptions?: CypherQueryOptions; + cypherQueryOptions?: CypherQueryOptions; } export type ValidationConfig = { diff --git a/packages/graphql/src/index.ts b/packages/graphql/src/index.ts index 4a18aefb15..f6490c6206 100644 --- a/packages/graphql/src/index.ts +++ b/packages/graphql/src/index.ts @@ -29,14 +29,6 @@ export { export * as directives from "./graphql/directives"; export * as scalars from "./graphql/scalars"; export { - CypherConnectComponentsPlanner, - CypherExpressionEngine, - CypherInterpretedPipesFallback, - CypherOperatorEngine, - CypherPlanner, - CypherReplanning, - CypherRuntime, - CypherUpdateStrategy, DeleteInfo, DriverConfig, EventMeta, diff --git a/packages/graphql/src/schema/resolvers/wrapper.ts b/packages/graphql/src/schema/resolvers/wrapper.ts index 941eeca32a..4531a0e6ed 100644 --- a/packages/graphql/src/schema/resolvers/wrapper.ts +++ b/packages/graphql/src/schema/resolvers/wrapper.ts @@ -147,9 +147,7 @@ export const wrapResolver = executionContext: context.executionContext, }; - if (config.queryOptions) { - executorConstructorParam.queryOptions = config.queryOptions; - } + executorConstructorParam.cypherQueryOptions = context.cypherQueryOptions || config.cypherQueryOptions; if (context.driverConfig?.database) { executorConstructorParam.database = context.driverConfig?.database; diff --git a/packages/graphql/src/types/index.ts b/packages/graphql/src/types/index.ts index 76157acec8..f8d511f813 100644 --- a/packages/graphql/src/types/index.ts +++ b/packages/graphql/src/types/index.ts @@ -299,65 +299,18 @@ export type TimeStampOperations = "CREATE" | "UPDATE"; export type CallbackOperations = "CREATE" | "UPDATE"; -export enum CypherRuntime { - INTERPRETED = "interpreted", - SLOTTED = "slotted", - PIPELINED = "pipelined", -} - -export enum CypherPlanner { - COST = "cost", - IDP = "idp", - DP = "dp", -} - -export enum CypherConnectComponentsPlanner { - GREEDY = "greedy", - IDP = "idp", -} - -export enum CypherUpdateStrategy { - DEFAULT = "default", - EAGER = "eager", -} - -export enum CypherExpressionEngine { - DEFAULT = "default", - INTERPRETED = "interpreted", - COMPILED = "compiled", -} - -export enum CypherOperatorEngine { - DEFAULT = "default", - INTERPRETED = "interpreted", - COMPILED = "compiled", -} - -export enum CypherInterpretedPipesFallback { - DEFAULT = "default", - DISABLED = "disabled", - WHITELISTED_PLANS_ONLY = "whitelisted_plans_only", - ALL = "all", -} - -export enum CypherReplanning { - DEFAULT = "default", - FORCE = "force", - SKIP = "skip", -} - /* Object keys and enum values map to values at https://neo4j.com/docs/cypher-manual/current/query-tuning/query-options/#cypher-query-options */ export interface CypherQueryOptions { - runtime?: CypherRuntime; - planner?: CypherPlanner; - connectComponentsPlanner?: CypherConnectComponentsPlanner; - updateStrategy?: CypherUpdateStrategy; - expressionEngine?: CypherExpressionEngine; - operatorEngine?: CypherOperatorEngine; - interpretedPipesFallback?: CypherInterpretedPipesFallback; - replan?: CypherReplanning; + runtime?: "interpreted" | "slotted" | "pipelined"; + planner?: "cost" | "idp" | "dp"; + connectComponentsPlanner?: "greedy" | "idp"; + updateStrategy?: "default" | "eager"; + expressionEngine?: "default" | "interpreted" | "compiled"; + operatorEngine?: "default" | "interpreted" | "compiled"; + interpretedPipesFallback?: "default" | "disabled" | "whitelisted_plans_only" | "all"; + replan?: "default" | "force" | "skip"; } /** The startup validation checks to run */ diff --git a/packages/graphql/src/types/neo4j-graphql-context.ts b/packages/graphql/src/types/neo4j-graphql-context.ts index 96991cf1d7..6b8022c8a1 100644 --- a/packages/graphql/src/types/neo4j-graphql-context.ts +++ b/packages/graphql/src/types/neo4j-graphql-context.ts @@ -22,6 +22,11 @@ import type { CypherQueryOptions, DriverConfig, RequestLike } from "."; import type { JWTPayload } from "jose"; export interface Neo4jGraphQLContext { + /** + * Configures which {@link https://neo4j.com/docs/cypher-manual/current/query-tuning/query-options/ | Cypher query options} + * when executing the translated query. + */ + cypherQueryOptions?: CypherQueryOptions; /** * @deprecated Use the {@link executionContext} property instead. */ @@ -48,11 +53,6 @@ export interface Neo4jGraphQLContext { * ``` */ jwt?: JWTPayload; - /** - * Configures which {@link https://neo4j.com/docs/cypher-manual/current/query-tuning/query-options/ | Cypher query options} - * when executing the translated query. - */ - queryOptions?: CypherQueryOptions; /** * The bearer token to be decoded/verified for use in authentication and authorization. * Normally found in the Authorization HTTP header. Can be provided with or without authentication scheme. diff --git a/packages/graphql/src/utils/execute.test.ts b/packages/graphql/src/utils/execute.test.ts index d3ad78257c..9ecc371540 100644 --- a/packages/graphql/src/utils/execute.test.ts +++ b/packages/graphql/src/utils/execute.test.ts @@ -19,16 +19,6 @@ import type { Driver } from "neo4j-driver"; import type { Neo4jGraphQL } from "../classes"; -import { - CypherConnectComponentsPlanner, - CypherExpressionEngine, - CypherInterpretedPipesFallback, - CypherOperatorEngine, - CypherPlanner, - CypherReplanning, - CypherRuntime, - CypherUpdateStrategy, -} from "../types"; import execute from "./execute"; import { trimmer } from "."; import { ContextBuilder } from "../../tests/utils/builders/context-builder"; @@ -176,7 +166,7 @@ describe("execute", () => { executionContext: driver, database, bookmarks, - queryOptions: {}, + cypherQueryOptions: {}, }), info: undefined, }).instance(), @@ -255,15 +245,15 @@ describe("execute", () => { executionContext: driver, database, bookmarks, - queryOptions: { - runtime: CypherRuntime.INTERPRETED, - planner: CypherPlanner.COST, - connectComponentsPlanner: CypherConnectComponentsPlanner.GREEDY, - updateStrategy: CypherUpdateStrategy.DEFAULT, - expressionEngine: CypherExpressionEngine.COMPILED, - operatorEngine: CypherOperatorEngine.COMPILED, - interpretedPipesFallback: CypherInterpretedPipesFallback.ALL, - replan: CypherReplanning.DEFAULT, + cypherQueryOptions: { + runtime: "interpreted", + planner: "cost", + connectComponentsPlanner: "greedy", + updateStrategy: "default", + expressionEngine: "compiled", + operatorEngine: "compiled", + interpretedPipesFallback: "all", + replan: "default", }, }), info: undefined, diff --git a/packages/graphql/tests/integration/config-options/query-options.int.test.ts b/packages/graphql/tests/integration/config-options/query-options.int.test.ts index ce5004ae8b..472887dd5b 100644 --- a/packages/graphql/tests/integration/config-options/query-options.int.test.ts +++ b/packages/graphql/tests/integration/config-options/query-options.int.test.ts @@ -22,7 +22,6 @@ import { graphql } from "graphql"; import { generate } from "randomstring"; import Neo4j from "../neo4j"; import { Neo4jGraphQL } from "../../../src/classes"; -import { CypherRuntime } from "../../../src"; describe("query options", () => { let driver: Driver; @@ -56,7 +55,7 @@ describe("query options", () => { const neoSchema = new Neo4jGraphQL({ typeDefs, driver, - config: { queryOptions: { runtime: CypherRuntime.INTERPRETED } }, + config: { cypherQueryOptions: { runtime: "interpreted" } }, }); const id = generate({