Using Neo4j embedded in Java applications

This chapter describes how to use Neo4j embedded in Java applications.

This chapter describes the following:

When running your own code and Neo4j in the same JVM, there are a few things you should keep in mind:

  • Do not create or retain more objects than you strictly need to. Large caches in particular tend to promote more objects to the old generation, thus increasing the need for expensive full garbage collections.

  • Do not use internal Neo4j APIs. They are internal to Neo4j and subject to change without notice, which may break or change the behavior of your code.

  • Do not enable the -XX:+TrustFinalNonStaticFields JVM flag when running in embedded mode.

Uniqueness of Paths in traversals

This example is demonstrating the use of node uniqueness. Below an imaginary domain graph with Principals that own pets that are descendant to other pets.
Diagram

In order to return all descendants of Pet0 which have the relation owns to Principal1 (Pet1 and Pet3), the Uniqueness of the traversal needs to be set to NODE_PATH rather than the default NODE_GLOBAL. This way nodes can be traversed more that once, and paths that have different nodes but can have some nodes in common (like the start and end node) can be returned.

        Node dataTarget = data.get().get( "Principal1" );
        String output = "";
        int count = 0;
        try ( Transaction transaction = graphdb().beginTx() )
        {
            start = transaction.getNodeById( start.getId() );
            final Node target = transaction.getNodeById( dataTarget.getId() );
            TraversalDescription td = transaction.traversalDescription()
                    .uniqueness( Uniqueness.NODE_PATH )
                    .evaluator( new Evaluator()
            {
                @Override
                public Evaluation evaluate( Path path )
                {
                    boolean endNodeIsTarget = path.endNode().equals( target );
                    return Evaluation.of( endNodeIsTarget, !endNodeIsTarget );
                }
            } );

            Traverser results = td.traverse( start );

This will return the following paths:

(2)-[descendant,2]->(0)<-[owns,5]-(1)
(2)-[descendant,0]->(5)<-[owns,3]-(1)

In the default path.toString() implementation, (1)--[knows,2]-→(4) denotes a node with ID=1 having a relationship with ID=2 or type knows to a node with ID=4.

Let’s create a new TraversalDescription from the old one, having NODE_GLOBAL uniqueness to see the difference.

The TraversalDescription object is immutable, so we have to use the new instance returned with the new uniqueness setting.

            TraversalDescription nodeGlobalTd = tx.traversalDescription().uniqueness( Uniqueness.NODE_PATH ).evaluator( new Evaluator()
            {
                @Override
                public Evaluation evaluate( Path path )
                {
                    boolean endNodeIsTarget = path.endNode().equals( target );
                    return Evaluation.of( endNodeIsTarget, !endNodeIsTarget );
                }
            } ).uniqueness( Uniqueness.NODE_GLOBAL );
            Traverser results = nodeGlobalTd.traverse( start );

Now only one path is returned:

(2)-[descendant,2]->(0)<-[owns,5]-(1)