Custom, Cypher Based Procedures and Functions
By default, the custom Cypher statements are enabled.
We can disable it by setting the following property in apoc.conf
:
apoc.custom.procedures.enabled=false
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 |