User-defined functions

User-defined functions are simpler forms of procedures that return a single value and are read-only. Although they are less powerful in capability, they are often easier to use and more efficient than procedures for many common tasks. For a comparison between user-defined procedures, functions, and aggregation functions see Neo4j customized code.

Call a user-defined function

User-defined functions are called in the same way as any other Cypher function. The function name must be fully qualified, so a function named join defined in the package org.neo4j.examples could be called using:

MATCH (p: Person) WHERE p.age = 36
RETURN org.neo4j.examples.join(collect(p.names))

Create a function

User-defined functions are created similarly to how procedures are created. But unlike procedures, they are annotated with @UserFunction and return a single value instead of a stream of values.

Particular things to note:

  • All functions are annotated with @UserFunction.

  • The function name must be namespaced and is not allowed in reserved namespaces.

  • If a function is registered with the same name as a built-in function in a deprecated namespace, the built-in function is shadowed.

See Values and types for details on values and types.

The correct way to signal an error from within a function is to throw RuntimeException.

package example;

import java.util.List;

import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.UserFunction;

public class Join
{
    @UserFunction
    @Description("example.join(['s1','s2',...], delimiter) - join the given strings with the given delimiter.")
    public String join(
            @Name("strings") List<String> strings,
            @Name(value = "delimiter", defaultValue = ",") String delimiter) {
        if (strings == null || delimiter == null) {
            return null;
        }
        return String.join(delimiter, strings);
    }
}

Integration tests

Tests for user-defined functions are created in the same way as those for procedures.

A template for testing a user-defined function that joins a list of strings.
package example;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.Session;
import org.neo4j.harness.Neo4j;
import org.neo4j.harness.Neo4jBuilders;

import static org.assertj.core.api.Assertions.assertThat;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class JoinTest {

    private Neo4j embeddedDatabaseServer;

    @BeforeAll
    void initializeNeo4j() {
        this.embeddedDatabaseServer = Neo4jBuilders.newInProcessBuilder()
                .withDisabledServer()
                .withFunction(Join.class)
                .build();
    }

    @AfterAll
    void closeNeo4j() {
        this.embeddedDatabaseServer.close();
    }

    @Test
    void joinsStrings() {
        // This is in a try-block, to make sure we close the driver after the test
        try(Driver driver = GraphDatabase.driver(embeddedDatabaseServer.boltURI());
            Session session = driver.session()) {

            // When
            String result = session.run( "RETURN example.join(['Hello', 'World']) AS result").single().get("result").asString();

            // Then
            assertThat( result).isEqualTo(( "Hello,World" ));
        }
    }
}

Reserved and deprecated function namespaces

Note that deprecated function namespaces will be moved to reserved in the next major Cypher version. For more information about Neo4j and Cypher versioning, see Operations manual → Introduction.

Table 1. Overview of reserved and deprecated function namespaces and names
Reserved Deprecated in Cypher 25 since Neo4j 2025.11

*

abac.*

date

builtin.*

date.realtime

cdc.*

date.statement

coll.*

date.transaction

date.*

date.truncate

datetime.*

datetime

db.*

datetime.fromepoch

dbms.*

datetime.fromepochmillis

duration.*

datetime.realtime

graph.*

datetime.statement

internal.*

datetime.transaction

localdatetime.*

datetime.truncate

localtime.*

db.nameFromElementId

math.*

duration

plugin.*

duration.between

point.*

duration.inDays

stored.*

duration.inMonths

string.*

duration.inSeconds

time.*

graph.byElementId

tx.*

graph.byName

unsupported.*

graph.names

vector.*

graph.propertiesByName

localdatetime

localdatetime.realtime

localdatetime.statement

localdatetime.transaction

localdatetime.truncate

localtime

localtime.realtime

localtime.statement

localtime.transaction

localtime.truncate

point.distance

point.withinBBox

time

time.realtime

time.statement

time.transaction

time.truncate

vector.similarity.cosine

vector.similarity.euclidean