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.
See Values and types for details on values and types.
For more details, see the Neo4j Javadocs for org.neo4j.procedure.UserFunction.
| The correct way to signal an error from within a function is to throw  | 
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.
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" ));
        }
    }
}