Custom, Cypher Based Procedures and Functions
All these procedures (except the list and show ones) are intended to be executed in the system database,
therefore they have to be used executed by opening a system database session. There are several ways of doing this:
- when using cypher-shell or Neo4j Browser, one can prefix their Cypher query with Moreover, they accept as first parameter the name of the database towards which we want to install/update/remove the automatic UUIDs. Through this implementation, we can use these procedures in a cluster environment, by leveraging the cluster routing mechanism. |
Installing, updating or removing a custom Cypher statement is an eventually consistent operation.
Therefore, they are not immediately added/updated/removed,
but they have a refresh rate handled by the Apoc configuration In case of a cluster environment,
the |
Qualified Name | Type | Release |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Overview
I wanted for a long time to be able to register Cypher statements as proper procedures and functions, so that they become callable in a standalone way.
You can achieve that with the apoc.custom.installProcedure
and apoc.custom.installFunction
procedure calls.
Those register a given Cypher statement, prefixed with the custom.*
namespace, overriding potentially existing ones, so you can redefine them as needed.
The first parameter of the apoc.custom.installProcedure
and apoc.custom.installFunction
procedures,
is the signature of the procedure/function you want to create.
This looks similar to the signature
results returned by the SHOW PROCEDURES YIELD signature
, SHOW FUNCTIONS YIELD signature
cypher commands, or by the CALL apoc.help('<fun_or_procedure_name>') YIELD signature
procedure, just without the ?
.
That is:
- for a procedure: nameProcedure(firstParam = defaultValue :: typeParam , secondParam = defaultValue :: typeParam, ….) :: (firstResult :: typeResult, secondResult :: typeResult, … )
- for a function: nameFunction(firstParam = defaultValue :: typeParam , secondParam = defaultValue :: typeParam, ….) :: typeResult
Note that, for both procedures and functions, the = defaultValue
are optionals.
The default values are parsed as JSON.
If you want to create a procedure/function with a default String parameter with whitespaces, quotes (for example: "my text ' with ' quote" ) or "null" (as a string),
you have to quote the result, e.g CALL apoc.custom.declareProcedure("procWithNullString(param='null'::STRING)::(output::STRING)", 'return $param as output')
|
The typeParam
and typeResult
in the signature parameter can be one of the following values:
-
FLOAT, DOUBLE, INT, INTEGER, INTEGER | FLOAT, NUMBER, LONG
-
TEXT, STRING
-
BOOL, BOOLEAN
-
POINT, GEO, GEOMETRY
-
DATE, DATETIME, LOCALDATETIME, TIME, LOCALTIME, DURATION
-
NODE, REL, RELATIONSHIP, EDGE, PATH
-
MAP
-
MAPRESULT (valid for declareFunction, does not wrap the result in a further map. see here: [map-vs-map-result])
-
LIST TYPE, LIST OF TYPE (where
TYPE
can be one of the previous values) -
ANY
In Neo4j 5.13, the NUMBER type was replaced by INTEGER | FLOAT , but we can still use it for backwards compatibility.
|
If you override procedures or functions you might need to call call db.clearQueryCaches() as lookups to internal id’s are kept in compiled query plans.
|
Starting from version 5.11, if we execute a So we must necessarily open a new transaction to execute the custom procedures / functions declared. |
The following examples assume that we are on the neo4j
database,
and we want to create the custom procedures/function in that database.
Custom Procedures with apoc.custom.installProcedure
Procedure, input and output names must have at least 2 characters. |
Here is a simple example:
CALL apoc.custom.installProcedure('answerInteger() :: (answer::INT)', 'RETURN 42 as answer')
This registers, after a time defined by the configuration apoc.custom.procedures.refresh
,
a procedure custom.answer
that you then can call.
CALL custom.answerInteger
answer |
---|
42 |
Or you can also write in this way:
CALL apoc.custom.installProcedure('answer() :: (row::MAP)', 'RETURN 42 as answer')
In this case the result is wrapped in a stream of maps called row
. Therefore, you can do:
CALL custom.answer()
YIELD row
RETURN row.answer
answer |
---|
42 |
We can create the procedure custom.powers
that returns a stream of the powers of the first parameter, up to and including the power provided by the second parameter:
CALL apoc.custom.installProcedure(
'powers(input::INT, power::INT) :: (answer::INT)',
'UNWIND range(0, $power) AS power
RETURN $input ^ power AS answer'
);
We can use this function, to return 4°, 4¹, 4², and 4³, as shown in the query below:
CALL custom.powers(4,3);
answer |
---|
1.0 |
4.0 |
16.0 |
64.0 |
Moreover, via the 3rd parameter, we can create the custom procedure in a different database than neo4j
.
For example, the following one creates a custom procedure in the foo
database:
CALL apoc.custom.installProcedure('foodb() :: (row::INT)', 'RETURN 42 as row', 'foo')
Furthermore, we can pass as the 4th parameter a string to specify the procedure mode (default "WRITE"). It can be: - "READ" - if the procedure will only perform read operations against the graph - "WRITE" - if it may perform both read and write operations against the graph - "SCHEMA" - if it will perform operations against the schema - "DBMS" - if it will perform system operations - i.e. not against the graph
Also, we can pass a description
parameter as the 5th parameter,
which will be returned by the call apoc.custom.list
and SHOW PROCEDURES
.
Custom Functions with apoc.custom.installFunction
Function, input and output names must have at least 2 characters. |
Here is a simple example:
CALL apoc.custom.installFunction('answerFun() :: INT', 'RETURN 42 as answer')
This registers, after a time defined by the configuration apoc.custom.procedures.refresh
,
the statement as procedure custom.answer
that you then can call.
RETURN custom.answerFun()
answer |
---|
42 |
Or you can also write in this way:
CALL apoc.custom.installFunction('answerFunMap() :: MAP', 'RETURN 42 as answer')
In this case the result is wrapped in a stream of maps called row
. Therefore, you can do:
WITH custom.answerFunMap() YIELD row
RETURN row.answer
answer |
---|
42 |
We can create the function custom.double
, that doubles the provided value, by running the following function:
CALL apoc.custom.installFunction(
'double(input::INT) :: INT',
'RETURN $input*2 as answer'
);
We can use this function, as shown in the query below:
RETURN custom.double(83) AS value;
value |
---|
166 |
Moreover, via the 3rd parameter, we can create the custom procedure in a different database than neo4j
.
For example, the following one creates a custom procedure in the foo
database:
CALL apoc.custom.installFunction('foodb() :: INT', 'RETURN 42', 'foo')
Furthermore, we can pass as a 4th parameter a boolean (with default false) which, if true, in case the function returns a list of a single element, it will return only the single element itself and not the list.
For example:
CALL apoc.custom.installFunction('forceSingleTrue(input::ANY) :: LIST OF INT',
'RETURN 1',
true
);
value |
---|
1 |
otherwise with false the result will be a singleton list:
CALL apoc.custom.installFunction('forceSingleFalse(input::ANY) :: LIST OF INT',
'RETURN 1',
false
);
value |
---|
[1] |
Also, we can pass a description
parameter as the 5th parameter,
which will be returned by the call apoc.custom.list
and SHOW FUNCTIONS
.
List of registered procedures/function with apoc.custom.list
The procedure apoc.custom.list
provide a list of all registered procedures/function via
apoc.custom.installProcedure
and apoc.custom.installFunction
Given this call:
CALL apoc.custom.list
The output will look like the following table:
type | name | description | mode | statement | inputs | outputs | forceSingle |
---|---|---|---|---|---|---|---|
"function" |
"answer" |
<null> |
<null> |
"RETURN $input as answer" |
[["input","integer | float"]] |
"long" |
false |
"procedure" |
"answer" |
"Procedure that answer to the Ultimate Question of Life, the Universe, and Everything" |
"read" |
"RETURN $input as answer" |
[["input","int","42"]] |
[["answer","integer | float"]] |
<null> |
Remove a procedure apoc.custom.dropProcedure
The procedure apoc.custom.dropProcedure
allows to delete the targeted custom procedure, from a specific database (with neo4j
as a default),
after a time defined by the configuration apoc.custom.procedures.refresh
.
Given this call:
CALL apoc.custom.dropProcedure(<name>, <databaseName>)
Fields:
argument | description |
---|---|
name |
the procedure name |
databaseName |
the database name (default: |
Remove a procedure apoc.custom.dropFunction
The procedure apoc.custom.dropFunction
allows to delete the targeted custom function, from a specific database (with neo4j
as a default),
after a time defined by the configuration apoc.custom.procedures.refresh
.
Given this call:
CALL apoc.custom.dropFunction(<name>)
Fields:
argument | description |
---|---|
name |
the function name |
databaseName |
the database name (default: |
Export metadata
To import custom procedures in another database (for example after a |