Skip to content

Commit

Permalink
Adds wrapper over CypherEnvironment for Cypher.Raw
Browse files Browse the repository at this point in the history
  • Loading branch information
angrykoala committed Sep 17, 2024
1 parent 9d39d11 commit adf599f
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-seahorses-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@neo4j/cypher-builder": major
---

Cypher.Raw no longer exposes Cypher.Environment variable. It provides a `CypherRawContext` instance with a `compile` method to compile nested elements in custom cypher instead
8 changes: 4 additions & 4 deletions docs/modules/ROOT/pages/how-to/customize-cypher.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ In more complex scenarios, you may need to access variables created with the Cyp
However, these values are not available before executing `.build`.
To achieve this, `Raw` supports a callback that is executed while the query is being built, and has access to the variables.

This callback receives a parameter `env` that can be used to manually compile Cypher Builder clauses and translate variable names.
This callback receives a parameter `context` that can be used to manually compile Cypher Builder clauses and translate variable names.
It returns the following values:

* `string`: Cypher string to be used for this element.
Expand All @@ -305,8 +305,8 @@ However, a custom `Raw` is being injected as part of the `WHERE` subclause:
const movie = new Cypher.Node();
const match = new Cypher.Match(movie, { labels: ["Movie"] })
.where(
new Cypher.Raw((env) => {
const movieStr = env.compile(movie);
new Cypher.Raw((context) => {
const movieStr = context.compile(movie);
const cypher = `${movieStr}.prop = $myParam`;
const params = {
Expand Down Expand Up @@ -340,5 +340,5 @@ And the following parameters:
----

The callback passed into `Raw` is producing the string `this0.prop = $myParam`.
To achieve this, it uses the utility method `utils.compileCypher` and passes the variable `movie` and the `env` parameter, which then returns the string `this0`.
To achieve this, it uses the utility method `utils.compileCypher` and passes the variable `movie` and the `context` parameter, which then returns the string `this0`.
Finally, the custom parameter `$myParam` is returned in the tuple `[cypher, params]`, ensuring that it is available when executing `match.build()`.
3 changes: 1 addition & 2 deletions src/Cypher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export { Foreach } from "./clauses/Foreach";
export { LoadCSV } from "./clauses/LoadCSV";
export { Match, OptionalMatch } from "./clauses/Match";
export { Merge } from "./clauses/Merge";
export { Raw } from "./clauses/Raw";
export { Raw, type RawCypherContext } from "./clauses/Raw";
export { Return } from "./clauses/Return";
export { Union } from "./clauses/Union";
export { Unwind } from "./clauses/Unwind";
Expand Down Expand Up @@ -160,7 +160,6 @@ export {
export { CypherProcedure as Procedure, VoidCypherProcedure as VoidProcedure } from "./procedures/CypherProcedure";

// Types
export type { CypherEnvironment as Environment } from "./Environment";
export type { BuildConfig, Clause } from "./clauses/Clause";
export type { Order } from "./clauses/sub-clauses/OrderBy";
export type { ProjectionColumn } from "./clauses/sub-clauses/Projection";
Expand Down
12 changes: 6 additions & 6 deletions src/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,18 @@ export class CypherEnvironment {
}, {});
}

public addNamedParamReference(name: string, param: Param): void {
if (!this.references.has(param)) {
this.addParam(name, param);
}
}

public addExtraParams(params: Record<string, Param>): void {
Object.entries(params).forEach(([key, param]) => {
this.addNamedParamReference(key, param);
});
}

public addNamedParamReference(name: string, param: Param): void {
if (!this.references.has(param)) {
this.addParam(name, param);
}
}

public getParamsSize(): number {
return this.params.length;
}
Expand Down
2 changes: 1 addition & 1 deletion src/clauses/Raw.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe("Raw Cypher", () => {
test("Create a custom query with Raw callback", () => {
const releasedParam = new Cypher.Param(1999);

const rawCypher = new Cypher.Raw((env: Cypher.Environment) => {
const rawCypher = new Cypher.Raw((env) => {
const releasedParamId = env.compile(releasedParam); // Gets the raw Cypher for the param

const customCypher = `MATCH(n) WHERE n.title=$title_param AND n.released=${releasedParamId}`;
Expand Down
18 changes: 16 additions & 2 deletions src/clauses/Raw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
*/

import type { CypherEnvironment } from "../Environment";
import type { CypherCompilable } from "../types";
import { toCypherParams } from "../utils/to-cypher-params";
import { Clause } from "./Clause";

type RawCypherCallback = (env: CypherEnvironment) => [string, Record<string, unknown>] | string | undefined;
type RawCypherCallback = (context: RawCypherContext) => [string, Record<string, unknown>] | string | undefined;

/** Allows for a raw string to be used as a clause
* @group Other
Expand All @@ -37,7 +38,7 @@ export class Raw extends Clause {
}

public getCypher(env: CypherEnvironment): string {
const cbResult = this.callback(env);
const cbResult = this.callback(new RawCypherContext(env));
if (!cbResult) return "";
let query: string;
let params = {};
Expand All @@ -56,3 +57,16 @@ export class Raw extends Clause {
return () => str;
}
}

export class RawCypherContext {
private env: CypherEnvironment;

constructor(env: CypherEnvironment) {
this.env = env;
}

/** Compiles a Cypher element in the current context */
public compile(element: CypherCompilable): string {
return this.env.compile(element);
}
}
9 changes: 5 additions & 4 deletions src/expressions/IsType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
* limitations under the License.
*/

import type { Environment, Expr } from "..";
import { CypherASTNode } from "../CypherASTNode";
import type { CypherEnvironment } from "../Environment";
import type { Expr } from "../types";
import { asArray } from "../utils/as-array";
import type { ValueOf } from "../utils/type-helpers";

Expand Down Expand Up @@ -105,7 +106,7 @@ class ListType {
return this;
}

public getCypher(env: Environment): string {
public getCypher(env: CypherEnvironment): string {
// Note that all types must be nullable or non nullable
const notNullStr = this._notNull ? " NOT NULL" : "";
const typesStr = this.types
Expand Down Expand Up @@ -138,7 +139,7 @@ export class IsType extends CypherASTNode {
return this;
}

public getCypher(env: Environment): string {
public getCypher(env: CypherEnvironment): string {
const exprCypher = env.compile(this.expr);
const isStr = this.not ? "IS NOT" : "IS";

Expand All @@ -158,7 +159,7 @@ export class IsType extends CypherASTNode {

type Type = ValueOf<typeof BaseTypes> | ListType;

function compileType(type: Type, env: Environment): string {
function compileType(type: Type, env: CypherEnvironment): string {
if (type instanceof ListType) {
return env.compile(type);
} else {
Expand Down
10 changes: 5 additions & 5 deletions src/expressions/labels/label-expressions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* limitations under the License.
*/

import type { Environment } from "../..";
import type { CypherEnvironment } from "../../Environment";
import type { CypherCompilable } from "../../types";
import { compileCypherIfExists } from "../../utils/compile-cypher-if-exists";
import { escapeLabel } from "../../utils/escape";
Expand All @@ -41,9 +41,9 @@ export abstract class LabelExpr implements CypherCompilable {
/**
* @internal
*/
public abstract getCypher(env: Environment): string;
public abstract getCypher(env: CypherEnvironment): string;

protected compileLabel(expr: Label, env: Environment) {
protected compileLabel(expr: Label, env: CypherEnvironment) {
if (typeof expr === "string") {
return escapeLabel(expr);
}
Expand All @@ -62,7 +62,7 @@ class BinaryLabelExpr extends LabelExpr {
/**
* @internal
*/
public getCypher(env: Environment): string {
public getCypher(env: CypherEnvironment): string {
const labelStrs = this.labels.map((l) => this.compileLabel(l, env));
if (labelStrs.length === 0) return "";

Expand All @@ -81,7 +81,7 @@ class NotLabelExpr extends LabelExpr {
/**
* @internal
*/
public getCypher(env: Environment): string {
public getCypher(env: CypherEnvironment): string {
const labelStrs = this.compileLabel(this.label, env);

return `${this.operator}${labelStrs}`;
Expand Down

0 comments on commit adf599f

Please sign in to comment.