Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simpler configuration of Cypher query options #3628

Merged
merged 2 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eighty-ads-develop.md
Original file line number Diff line number Diff line change
@@ -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.
51 changes: 51 additions & 0 deletions docs/modules/ROOT/pages/migration/v4-migration/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
});
----
47 changes: 44 additions & 3 deletions docs/modules/ROOT/pages/troubleshooting.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand All @@ -65,8 +65,8 @@ const neoSchema = new Neo4jGraphQL({
typeDefs,
driver,
config: {
queryOptions: {
runtime: CypherRuntime.INTERPRETED,
cypherQueryOptions: {
runtime: "interpreted",
},
},
});
Expand All @@ -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

Expand Down
14 changes: 7 additions & 7 deletions packages/graphql/src/classes/Executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/graphql/src/classes/Neo4jGraphQL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export interface Neo4jGraphQLConfig {
driverConfig?: DriverConfig;
enableDebug?: boolean;
startupValidation?: StartupValidationConfig;
queryOptions?: CypherQueryOptions;
cypherQueryOptions?: CypherQueryOptions;
}

export type ValidationConfig = {
Expand Down
8 changes: 0 additions & 8 deletions packages/graphql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 1 addition & 3 deletions packages/graphql/src/schema/resolvers/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
63 changes: 8 additions & 55 deletions packages/graphql/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
10 changes: 5 additions & 5 deletions packages/graphql/src/types/neo4j-graphql-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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.
Expand Down
30 changes: 10 additions & 20 deletions packages/graphql/src/utils/execute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -176,7 +166,7 @@ describe("execute", () => {
executionContext: driver,
database,
bookmarks,
queryOptions: {},
cypherQueryOptions: {},
}),
info: undefined,
}).instance(),
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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({
Expand Down