Customize Cypher

This page describes how to customize Cypher® queries using Cypher Builder. Such a scenario could occur if you:

  • Need to embed Cypher strings into existing Cypher queries.

  • Use Cypher Builder within a larger Cypher query.

  • Need to use features that are not supported in the current version of the Cypher Builder.

  • Use custom functions or procedures.

Embedding custom Cypher in a query may lead to Code Injection and other security issues.

Custom variable names

In most cases, Cypher Builder makes sure variable names are unique and do not collide. However, in case you need to explicitly set names, you can use the Named* variables:

new Cypher.NamedVariable("myVarName")

For more information, see Named variables.

Build Prefix

Though not recommended, you may need to mix multiple queries built with Cypher Builder into a single string. For example, this query:

const match1=new Cypher.Match(new Cypher.Pattern(new Cypher.Node(), { labels: ["Movie"] }))
const match2=new Cypher.Match(new Cypher.Pattern(new Cypher.Node(), { labels: ["Person"] }))

const cypher=`
${match1.build()}
${match2.build()}
`

Generates the following Cypher:

MATCH(this0:Movie)
MATCH(this1:Person)

In this query, the variable this0 is used for both MATCH statements, thus causing variable name collision. This happens because both queries (match1 and match2) are built separately.

If merging these queries before executing .build() (e.g. using Cypher.concat) is not a viable solution, a "prefix" string can be passed to .build() to avoid name collision:

const cypher=`
${match1.build("movie")}
${match2.build("person")}
`

In this case, the resulting Cypher looks like:

MATCH(movie_this0:Movie)
MATCH(person_this0:Person)

The prefix parameter in .build() prepends the provided string to every variable, except named variables.

Custom parameters

Parameters are only generated if they are used in the query. To add custom parameters, regardless of these being used or not, an object can be passed as a second parameter to .build:

const clause = new Cypher.Return(new Cypher.Param("Hello"))
clause.build("", {
    myParameter: "Hello World"
});

This generates the Cypher:

RETURN $param1

And the parameters:

{
    "param1": "Hello",
    "myParameter": "Hello World"
}

Custom parameter name

Similarly to variables, when defining a parameter this can be explicitly named by using the class NamedParam instead of Param.

For example, this query:

const movie = new Cypher.Node();
const matchQuery = new Cypher.Match(movie, { labels: ["Movie"]}).where(movie, { name: new Cypher.NamedParam("myParam") }).return(movie);

Generates the following query:

MATCH (this0:Movie)
WHERE this0.name = $myParam
RETURN this0

Note that $myParam does not return as a param by .build(). To generate the parameter, pass a value in the same way as normal parameters:

const movie = new Cypher.Node();
const matchQuery = new Cypher.Match({movie,   labels: ["Movie"] }).where(movie, { name: new Cypher.NamedParam("myParam", "Keanu Reeves") }).return(movie);

The resulting parameter is:

{
    "myParam": "Keanu Reeves"
}

Custom functions and procedures

Cypher Builder provides some built-in functions and procedures, but it also supports custom ones, for instance when using plugins or creating User-defined functions.

Functions

Arbitrary function calls can be built using the Cypher.Function class, for example:

new Cypher.Function("myFunc");

To learn more about creating custom functions go to here.

Procedures

In the case of arbitrary procedures, they can be defined with the class Cypher.Procedure:

const myProcedure = new Cypher.Procedure("my-procedure");

The generated Cypher automatically adds the CALL clause:

CALL my-procedure()

Parameters can then be passed as an argument to the constructor:

const myProcedure = new Cypher.Procedure("my-procedure", [new Cypher.Literal("Keanu"), new Cypher.Variable()])
CALL my-procedure("Keanu", var0)

Yield

Custom procedures may be followed by a YIELD statement with the .yield method:

const myProcedure = new Cypher.Procedure("my-procedure").yield("value");
CALL my-procedure() YIELD value

Unlike built-in procedures, however, this method doesn’t have TypeScript typings for the column names, so .yield accepts any string. More specific typings can be set in the Procedure class:

new Cypher.Procedure<"columnA" | "columnB">("my-procedure")

Trying to use .yield with anything different to "columnA" or "columnB" returns as a TypeScript error.

Void procedures

Some procedures cannot be used along with YIELD as they do not return any values. These can be defined with Cypher.VoidProcedure:

const myProcedure = new Cypher.VoidProcedure("my-proc");

This can be used as any other procedure, except that the .yield method is not available.

Reusing custom procedures

Custom procedures can be reused by wrapping them with a JavaScript function:

function myCustomProcedure(param1) {
    return new Cypher.Procedure("my-custom-procedure", [param1])
}

This function can then be used in the same fashion as built-in procedures:

myCustomProcedure(new Cypher.Variable()).yield("column")
CALL my-custom-procedure(var0) YIELD "column"

Raw

The class Cypher.Raw allows embedding a Cypher string within a larger query built with Cypher Builder. It acts as a wildcard that can be used anywhere.

For instance, this query:

const customReturn = new Cypher.Raw(`10 as myVal`);

const returnClause = new Cypher.Return(customReturn);

const { cypher, params } = returnClause.build();

Returns the following Cypher:

RETURN 10 as myVal

In this case, the RETURN clause is being generated by Cypher Builder, but the actual value 10 as myVal has been injected with Raw. This string can be anything, including other clauses or invalid Cypher, and can be generated dynamically:

const returnVar="myVal"
const customReturn = new Cypher.Raw(`10 as ${returnVar}`);

const returnClause = new Cypher.Return(customReturn);

Additionally, Raw can also be used in Cypher.concat to attach an arbitrary string to any Cypher Builder element.

Using a callback

In more complex scenarios, you may need to access variables created with the Cypher Builder in your custom Cypher string. 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. It returns the following values:

  • string: Cypher string to be used for this element.

  • [string, object]: a tuple with the first element being the Cypher string, and the second an object with the parameters to be injected in the query.

  • undefined: if undefined, Raw will be translated as an empty string.

In this example, a MATCH…​RETURN statement is being created with Cypher Builder in the usual way. 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);

            const cypher = `${movieStr}.prop = $myParam`;
            const params = {
                myParam: "Hello World",
            };

            return [cypher, params];
        })
    )
    .return(movie);

const { cypher, params } = match.build();

This returns the following Cypher:

MATCH (this0:`Movie`)
WHERE this0.prop = $myParam
RETURN this0

And the following parameters:

{
    "myParam": "Hello World"
}

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. Finally, the custom parameter $myParam is returned in the tuple [cypher, params], ensuring that it is available when executing match.build().