nieyuan1980
nieyuan1980

Reputation: 57

Neo4j Java Driver - Cannot access records on this result

I'm using the neo4j java driver and trying to execute a Cyphers.I got an exception.I am searching for a long time on net. But no use. Please help or try to give some ideas how to achieve this.

1.This is the driver that I added:

        <dependency>
            <groupId>org.neo4j.driver</groupId>
            <artifactId>neo4j-java-driver-spring-boot-starter</artifactId>
            <version>4.1.1.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.neo4j/neo4j-ogm-core -->
        <dependency>
            <groupId>org.neo4j</groupId>
            <artifactId>neo4j-ogm-core</artifactId>
            <version>3.2.20</version>
        </dependency>

2.The function:

     public Result readCyphers(String cypher) {

        Result result = null;

        Driver driver = null;
        Session session = null;

        try {
            //driver
            driver = GraphDatabase.driver(uri, AuthTokens.basic(username, password));
            //session
            session = driver.session();

            //run
            result = session.run(cypher);

        } catch (Exception e) {
            throw e;
        } finally {
            session.close();
            driver.close();
        }

        return result;
    }

3.call function

  @Test
    void readCyphersTest() {

        String cypher = "MATCH(N:WechatDepartment) \n" +
                "WHERE N.departmentId = 117 \n" +
                "RETURN ID(N) as id, N.departmentId as departmentId, N.name as name, N.order as order, N.enable as enable";
        Result result = readCyphers(cypher);

        //There is an exception here
        System.out.println("result.list().size():" + result.list().size());
    }

4.exception:

org.neo4j.driver.exceptions.ResultConsumedException: Cannot access records on this result any more as the result has already been consumed or the query runner where the result is created has already been closed.

Thanks in advance

Upvotes: 1

Views: 1979

Answers (1)

fbiville
fbiville

Reputation: 8960

(If you only plan to use the driver, you do not need Neo4j OGM)

Once the session is closed, you cannot access the driver's Result instance. You should instead iterate over the records while the session is open and return the result you want, something like:

    public <T> List<T> readCyphers(String cypher, Function<Record, T> mapper) {
        try (Driver driver = GraphDatabase.driver(uri, AuthTokens.basic(username, password));
             Session session = driver.session()) {

            Result result = session.run(cypher);
            return result.list(mapper);
        }
    }

Note, however, that you should not create a Driver instance every time you want to run a query, so the driver should be created before, like this for instance:

class CypherExecutor implements AutoCloseable {

    private final Driver driver;

    public CypherExecutor(String uri, String username, String password) {
        this.driver = GraphDatabase.driver(uri, AuthTokens.basic(username, password))
    }

    public <T> List<T> readCyphers(String cypher, Function<Record, T> mapper) {
        try (Session session = driver.session()) {
            Result result = session.run(cypher);
            return result.list(mapper);
        }
    }

    @Override
    public void close() throws Exception {
        driver.close();
    }
}

CypherExecutor should ideally be closed (via a try-with-resources block) only when the application stops.

Also, transaction functions are more robust (in face of cluster machine failures, leader re-elections etc) and should be used instead, so readCyphers should rather be (assuming read-only queries):

    public <T> List<T> readCyphers(String cypher, Function<Record, T> mapper) {
        try (Session session = driver.session()) {
            return session.readTransaction(tx -> tx.run(cypher).list(mapper));
        }
    }

On a final note, this abstraction does not bring much to the table, so you could directly use the Driver API in your codebase instead of using this indirection.

The full code, with imports:

import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.Record;
import org.neo4j.driver.Session;

import java.util.List;
import java.util.function.Function;

class CypherExecutor implements AutoCloseable {

    private final Driver driver;

    public CypherExecutor(String username, String password, String uri) {
        this.driver = GraphDatabase.driver(uri, AuthTokens.basic(username, password))
    }

    public <T> List<T> readCyphers(String cypher, Function<Record, T> mapper) {
        try (Session session = driver.session()) {
            return session.readTransaction(tx -> tx.run(cypher).list(mapper));
        }
    }

    @Override
    public void close() throws Exception {
        driver.close();
    }
}

Upvotes: 1

Related Questions