Skip to content

Commit

Permalink
Fixes #2895: update apoc.custom.* documentation (#2937)
Browse files Browse the repository at this point in the history
  • Loading branch information
vga91 committed Sep 21, 2022
1 parent c93c81d commit c6c9548
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,23 @@

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.declareProcedure` and `apoc.custom.dclareFunction` procedure calls.
You can achieve that with the `apoc.custom.declareProcedure` and `apoc.custom.declareFunction` procedure calls.
Those register a given Cypher statement, prefixed with the `custom.*` namespace, overriding potentially existing ones, so you can redefine them as needed.

Here is a simple example:

[source,cypher]
----
CALL apoc.custom.declareProcedure('answer() :: INT','RETURN 42 as answer')
----

This registers the statement as procedure `custom.answer` that you then can call.
As no information on parameter and return types is given, it just returns a stream of columns of maps called `row`.

[source,cypher]
----
CALL custom.answer() YIELD row
RETURN row.answer()
----

The same is possible as a function:

[source,cypher]
----
CALL apoc.custom.declareFunction('answer() :: (result::INT)','RETURN 42')
----

NOTE: If you override procedures or functions you might need to call `call dbms.clearQueryCaches()` as lookups to internal ids are kept in compiled query plans.

== Custom Procedures with `apoc.custom.declareProcedure`
The first parameter of the `apoc.custom.declareProcedure` and `apoc.custom.declareFunction` 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`

The given statement will be registered as a procedure, the results will be turned into a stream of records.

== Input parameters
[.procedures, opts=header]
|===
| Name | Type | Default
|signature|STRING?|null
|statement|STRING?|null
|mode|STRING?|read
|description|STRING?|
|===

The type names are what you would expect and see in outputs of `SHOW PROCEDURES` or `apoc.help` just without the `?`.
Note that, for both procedures and functions, the `= defaultValue` are optionals.
The default values are parsed as JSON.

.Type Names
The `typeParam` and `typeResult` in the signature parameter can be one of the following values:
* FLOAT, DOUBLE, INT, INTEGER, NUMBER, LONG
* TEXT, STRING
* BOOL, BOOLEAN
Expand All @@ -63,35 +34,17 @@ The default values are parsed as JSON.
* LIST TYPE, LIST OF TYPE (where `TYPE` can be one of the previous values)
* ANY

.Find neighbours of a node by name
[source,cypher]
----
CALL apoc.custom.declareProcedure('neighbours(name::STRING) :: NODE',
'MATCH (n:Person {name:$name})-->(nb) RETURN nb as neighbour','read',
'get neighbours of a person');

CALL custom.neighbours('Keanu Reeves') YIELD neighbour;
----
NOTE: 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.


== Custom Functions with `apoc.custom.declareFunction`
== Custom Procedures with `apoc.custom.declareProcedure`

Given statement will be registered as a statement, the results into a single value.
If the given output type is a list, results will be collected into a list, otherwise the first row will be used.
The statement needs to return a single column, otherwise an error is thrown.
include::partial$usage/apoc.custom.declareProcedure.adoc[]

== Input parameters
[.procedures, opts=header]
|===
| Name | Type | Default
|signature|STRING?|null
|statement|STRING?|null
|forceSingle|BOOLEAN?|false
|description|STRING?|
|===
== Custom Functions with `apoc.custom.declareFunction`

The type names are what you would expect and see in outputs of `SHOW PROCEDURES` or `apoc.help` just without the `?`.
The default values are parsed as JSON.
include::partial$usage/apoc.custom.declareFunction.adoc[]


== List of registered procedures/function with `apoc.custom.list`
Expand All @@ -106,7 +59,7 @@ Given the this call:
CALL apoc.custom.list
----

The the output will look like the following table:
The output will look like the following table:

[%autowidth,opts=header]
|===
Expand Down Expand Up @@ -175,4 +128,4 @@ changes to each cluster member
====
To import custom procedures in another database (for example after a `./neo4j-admin backup` and `/neo4j-admin restore`),
please see the xref::overview/apoc.systemdb/apoc.systemdb.export.metadata.adoc[apoc.systemdb.export.metadata] procedure.
====
====
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
Here is a simple example:

[source,cypher]
----
CALL apoc.custom.declareFunction('answerFun() :: INT', 'RETURN 42 as answer')
----

This registers the statement as procedure `custom.answer` that you then can call.
[source,cypher]
----
RETURN custom.answerFun()
----

.Results
[opts="header"]
|===
| answer
| 42
|===

Or you can also write in this way:

[source,cypher]
----
CALL apoc.custom.declareFunction('answerFunMap() :: MAP', 'RETURN 42 as answer')
----

In this case the result is wrapped in a stream of maps called `row`. Therefore, you can do:

[source,cypher]
----
WITH custom.answerFunMap() YIELD row
RETURN row.answer
----

.Results
[opts="header"]
|===
| answer
| 42
|===

We can create the function `custom.powers` that returns a stream of the powers of the first parameter, up to and including the power provided by the second parameter:

[source,cypher]
----
CALL apoc.custom.declareProcedure(
'powers(input::INT, power::INT) :: (answer::INT)',
'UNWIND range(0, $power) AS power
RETURN $input ^ power AS answer'
);
----

We can create the function `custom.double`, that doubles the provided value, by running the following function:

[source,cypher]
Expand All @@ -23,3 +76,43 @@ RETURN custom.double(83) AS value;
| value
| 166
|===

Furthermore, we can pass as a 3rd 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:

[source,cypher]
----
CALL apoc.custom.declareFunction('forceSingleTrue(input::ANY) :: LIST OF INT',
'RETURN 1',
true
);
----

.Results
[opts="header"]
|===
| value
| 1
|===

otherwise with false the result will be a singleton list:

[source,cypher]
----
CALL apoc.custom.declareFunction('forceSingleFalse(input::ANY) :: LIST OF INT',
'RETURN 1',
false
);
----

.Results
[opts="header"]
|===
| value
| [1]
|===

Moreover, we can pass a `description` parameter as the 4th parameter,
which will be returned by the `call apoc.custom.list` and `SHOW FUNCTIONS`.
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
Here is a simple example:

[source,cypher]
----
CALL apoc.custom.declareProcedure('answerInteger() :: (row::INT)', 'RETURN 42 as answer')
----

This registers the statement as procedure `custom.answer` that you then can call.
[source,cypher]
----
CALL custom.answerInteger
----

.Results
[opts="header"]
|===
| answer
| 42
|===

Or you can also write in this way:

[source,cypher]
----
CALL apoc.custom.declareProcedure('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:

[source,cypher]
----
CALL custom.answer() YIELD row
RETURN row.answer
----

.Results
[opts="header"]
|===
| answer
| 42
|===


We can create the function `custom.powers` that returns a stream of the powers of the first parameter, up to and including the power provided by the second parameter:

[source,cypher]
Expand Down Expand Up @@ -26,4 +69,15 @@ call custom.powers(4,3);
| 4.0
| 16.0
| 64.0
|===
|===


Furthermore, we can pass as the 3rd 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

Moreover, we can pass a `description` parameter as the 4th parameter,
which will be returned by the `call apoc.custom.list` and `SHOW PROCEDURES`.

0 comments on commit c6c9548

Please sign in to comment.