User defined procedure + java.lang.NoSuchMethodError

I read this blogpost:

and clones this github repo:

Once i want to create the schema with this code:
CALL com.maxdemarzi.schema.generate

I run into the following errorcode:

Failed to invoke procedure com.maxdemarzi.schema.generate: Caused by: java.lang.NoSuchMethodError: 'org.neo4j.graphdb.schema.Schema org.neo4j.graphdb.GraphDatabaseService.schema()'

This is the java code which lead to the error (i guess):

package com.maxdemarzi.schema;

import com.maxdemarzi.results.StringResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.logging.Log;
import org.neo4j.procedure.*;

import java.io.IOException;
import java.util.stream.Stream;

public class Schema {

    // This field declares that we need a GraphDatabaseService
    // as context when any procedure in this class is invoked
    @Context
    public GraphDatabaseService db;

    // This gives us a log instance that outputs messages to the
    // standard log, normally found under `data/log/console.log`
    @Context
    public Log log;

    @Procedure(name = "com.maxdemarzi.schema.generate", mode = Mode.SCHEMA)
    @Description("CALL com.maxdemarzi.schema.generate() - generate schema")

    public Stream<StringResult> generate() throws IOException {
        org.neo4j.graphdb.schema.Schema schema = db.schema();
        if (!schema.getIndexes(Labels.Tree).iterator().hasNext()) {
            schema.constraintFor(Labels.Tree)
                    .assertPropertyIsUnique("id")
                    .create();
        }
        if (!schema.getIndexes(Labels.Parameter).iterator().hasNext()) {
            schema.constraintFor(Labels.Parameter)
                    .assertPropertyIsUnique("name")
                    .create();
        }
        return Stream.of(new StringResult("Schema Generated"));
    }
    
}

Any ideas where the problem is?

The problem is in the version. Significant changes in Neo4j 4.x include moving the methods and patterns for directly interfacing with the db, out of the GraphDatabaseService, and into Transaction instances.

GraphDatabaseService 4.2 (Github) only contains methods for transactions, but no schema method. A quick glance to the bottom of Transaction 4.2 (Github), and you'll find all those methods you need, including schema.

However, GraphDatabaseService 3.5.19 (Github) is the latest version of Neo4j which still supported using those methods of interacting with the DB engine from the GraphDatabaseService.

Where to go from here

Two options:

  1. Rollback your Neo4j instance to 3.5.19 or earlier.
  2. Adjust the code to use Transaction.schema instead.

IMO, adjust to use a transaction. It'll remain compatible with all 4.x versions, is more stable, and easier to work with. To be fair, it may take a little bit more working out and massaging to get it all working again, but....

Starting point

public Stream<StringResult> generate() {
    try (
        Transaction tx = db.beginTx();
    ) {
        org.neo4j.graphdb.schema.Schema schema = tx.schema();

        if (!schema.getIndexes(Labels.Tree).iterator().hasNext()) {
            schema.constraintFor(Labels.Tree)
                    .assertPropertyIsUnique("id")
                    .create();
        }
        if (!schema.getIndexes(Labels.Parameter).iterator().hasNext()) {
            schema.constraintFor(Labels.Parameter)
                    .assertPropertyIsUnique("name")
                    .create();
        }
        
        return Stream.of(new StringResult("Schema Generated"));
    }
}
1 Like

Thank you, for now i went on with the older version of neo4j to test the decision stream approach first, before i invest a lot of time into reworking the code for the newer version.

Soon after the fix i ran into another error: Failed to invoke procedure - Caused by: java.lang.IllegalArgumentException: Must hint overloaded method: toArray

Maybe you can solve that issue as well. That would be amazing!