Migrate Neo4j embedded from 2.3.8 to 5.21 in Java

Hi,

One of our Java applications is currently running on Neo4j 2.3.8 embedded (community edition). I am working on migrating to Neo4j 5.21 embedded by following Using Neo4j embedded in Java applications - Java Reference.

Our application mainly using indexes to create and query nodes & relationships. However, I am not finding any sources to create a "Composite index for nodes and relationships" and query using the same. Below are two classes that we have to create a composite index and query using index (which returns org.neo4j.graphdb.index.IndexHits).

Can you please provide details and examples to achieve the same in Neo4j 5.21 embedded?

Also, how to iterate the results and convert them to records. I am trying to use "Entity" instead of "PropertyContainer"?

-- Neo4j 2.3.8---
<
import static au.com.pwc.nodalgeography.graph.GraphDatabaseUtils.QueryFactory.buildPrefixQuery;
import static com.googlecode.totallylazy.Option.option;
import static org.neo4j.kernel.Traversal.expanderForTypes;

import java.net.URL;

import org.apache.lucene.index.Term;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.neo4j.graphalgo.CostEvaluator;
import org.neo4j.graphalgo.GraphAlgoFactory;
import org.neo4j.graphalgo.PathFinder;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PathExpander;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.graphdb.traversal.InitialBranchState;
import org.neo4j.test.TestGraphDatabaseFactory;

import com.googlecode.lazyrecords.Definition;
import com.googlecode.lazyrecords.Keyword;
import com.googlecode.totallylazy.Option;
import com.googlecode.totallylazy.Triple;

import au.com.pwc.nodalgeography.graph.fastest.DijkstraPathFinder;
import au.com.pwc.nodalgeography.graph.fastest.MultiplePathsFinder;
import au.com.pwc.nodalgeography.graph.fastest.WeightedPathFinder;
import au.com.pwc.nodalgeography.model.Board;
import au.com.pwc.nodalgeography.model.GraphNode;
import au.com.pwc.nodalgeography.model.TripHeader;
import au.com.pwc.tcdc.boards.model.BoardId;
import au.com.pwc.tcdc.model.NodeName;
import au.com.pwc.tcdc.model.RosterId;
import au.com.pwc.tcdc.model.RosterSequenceId;
import au.com.pwc.tcdc.model.StopName;
import au.com.pwc.tcdc.model.TinyType;
import au.com.pwc.tcdc.model.TripId;
import au.com.pwc.tcdc.model.TripVersion;
import au.com.pwc.tcdc.shared.utils.FileUtils;

public class GraphDatabaseUtils {
private final static org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(GraphDatabaseUtils.class);

public static GraphDatabaseService impermanentDb() {
    return impermanentDb(new GraphDatabaseConfigUri());
}

public static GraphDatabaseService impermanentDb(GraphDatabaseConfigUri graphDatabaseConfigUri) {
    GraphDatabaseService graphDatabaseService = new TestGraphDatabaseFactory().
            newImpermanentDatabaseBuilder().
            loadPropertiesFromURL(configUrl(graphDatabaseConfigUri)).
            newGraphDatabase();

    LOGGER.info("Starting Neo4J...");
    return graphDatabaseService;
}

public static GraphDatabaseService embeddedDb(GraphDatabaseConfigUri graphDatabaseConfigUri, String dbPath) {

    GraphDatabaseService graphDatabaseService = new
            GraphDatabaseFactory().
            newEmbeddedDatabaseBuilder(dbPath).
            loadPropertiesFromURL(configUrl(graphDatabaseConfigUri)).
            newGraphDatabase();

    LOGGER.info("Starting Neo4J...");
    return graphDatabaseService;
}



public static Index<Node> indexForNodes(GraphDatabaseService graphDatabaseService, Definition definition) {
    // A Transaction may be required if the index has not yet been created
    Index<Node> index = null;
    Transaction transaction = graphDatabaseService.beginTx();
    try {
        index = graphDatabaseService.index().forNodes(definition.name());
    } finally {
        successAndFinish(transaction);
        return index;
    }
}

/**
 * TODO - Verify the following transaction management is correct...
 * What about nesting of transactions...
 */
public static RelationshipIndex indexForRelationships(GraphDatabaseService graphDatabaseService, Definition definition) {
    // A Transaction may be required if the index has not yet been created
    RelationshipIndex index = null;
    Transaction transaction = graphDatabaseService.beginTx();
    try {
        index = graphDatabaseService.index().forRelationships(definition.name());
    } finally {
        successAndFinish(transaction);
        return index;
    }
}

public static void successAndFinish(Transaction transaction) {
    try {
        if (transaction != null) {
            transaction.success();
            transaction.finish();
        }
    } catch (Exception e) {
    	LOGGER.info("EXCEPTION in GRAPHDATABASEUTILS : " + e.getMessage());
        throw new RuntimeException(e);
    }
}

public static void closeIndexResults(IndexHits<?> hits) {
    if (hits != null) {
        hits.close();
    }
}

}

/>

<
import com.googlecode.lazyrecords.Record;
import com.googlecode.totallylazy.Callable1;
import com.googlecode.totallylazy.iterators.StatefulIterator;
import org.apache.lucene.search.Query;
//import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Transaction;
//import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
//import org.neo4j.test.TestGraphDatabaseFactory;

import static au.com.pwc.nodalgeography.graph.GraphDatabaseUtils.closeIndexResults;
import static au.com.pwc.nodalgeography.graph.GraphDatabaseUtils.successAndFinish;

import java.io.Closeable;
import java.io.IOException;
//import java.util.Properties;

public class GraphNodeIterator extends StatefulIterator implements Closeable {

private final Index<T> index;
private Neo4j neo;
private final Query query;
private final Callable1<T, ? extends Record> nodeToRecord;
private IndexHits<T> hits;

public GraphNodeIterator(final Index<T> index, final Query query, final Callable1<T, ? extends Record> nodeToRecord,
		Neo4j neo) {
	this.index = index;
	this.query = query;
	this.nodeToRecord = nodeToRecord;
	this.neo = neo;
}

@Override
protected Record getNext() throws Exception {
	//CODE CHANGE: After upgrade to neo4j2.3.8
	Transaction tx = neo.beginTx();
	try {
		if (hits == null) {
			hits = index.query(query);
		}

		T next = hits.next();
		if (next == null) {
			return finished();
		} else {
			return nodeToRecord.call(next);
		}
	} catch (Exception ex) {
		throw ex;

	} finally {

		closeIndexResults(hits);
		successAndFinish(tx);
	}
}

@Override
public void close() throws IOException {
	if (hits != null) {
		hits.close();
	}
}

}

/>

---Neo4j 5.21 migration---

<
public static GraphDatabaseService embeddedDb(GraphDatabaseConfigUri graphDatabaseConfigUri, String dbPath) {

    try {
        java.nio.file.Path homeDirectory = java.nio.file.Path.of(dbPath);
        org.neo4j.io.fs.FileUtils.deleteDirectory(homeDirectory);

        managementService = new DatabaseManagementServiceBuilder(homeDirectory)
                .loadPropertiesFromFile(java.nio.file.Path.of(graphDatabaseConfigUri.toString())).build();

        graphDatabaseService = managementService.database(DEFAULT_DATABASE_NAME);

        registerShutdownHook(managementService);
        LOGGER.info("Starting Neo4J...");
    } catch (Exception e) {
        LOGGER.info("EXCEPTION in embeddedDb : " + e.getMessage());
        throw new RuntimeException(e);
    }

public static IndexDefinition indexForNodes(GraphDatabaseService graphDatabaseService, Definition definition) {
// A Transaction may be required if the index has not yet been created
//Index index = null;
IndexDefinition index = null;

    try (Transaction transaction = graphDatabaseService.beginTx()) {
        Schema schema = transaction.schema();

        index = schema.indexFor(Label.label(definition.name()))  // <1>
                //.on("username")                                  // <2>
                .withName(definition.name())                           // <3>
                .create();
        schema.awaitIndexOnline(index, 10, TimeUnit.SECONDS);
        successAndFinish(transaction);
        //index = graphDatabaseService.index().forNodes(definition.name());

    } finally {
        //successAndFinish(transaction);
        return index;
    }
}

/>

<
protected Record getNext() throws Exception {
//CODE CHANGE: After upgrade to neo4j2.3.8
Transaction tx = neo.beginTx();

    try {
        Result result= tx.execute(query.toString());

        if (result.hasNext()) {
            Map<String, Object> resultSet = result.next();

            //Entry<String,Object> column = resultSet.entrySet();
            if (resultSet == null ) {
                return finished();
            } else {
                return nodeToRecord.call(resultSet);
            }
        }
    } catch (Exception ex) {
        throw ex;

    } finally {

        closeIndexResults(hits);
        successAndFinish(tx);
    }
}

/>

Can someone please help me on this. I need to mainly understand how composite indexes can be created for nodes & relationships using java API in Neo4j 5.21 embedded community edition?