3.4. User database with indexes

This section demonstrates how to retrieve users by name using indexes.

The source code used in this example is found here: EmbeddedNeo4jWithNewIndexing.java

To begin with, we start the database server:

        GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( databaseDirectory );

Then we have to configure the database to index users by name. This only needs to be done once. However, note that schema changes and data changes are not allowed in the same transaction. Each transaction must either change the schema or the data, but not both.

            IndexDefinition indexDefinition;
            try ( Transaction tx = graphDb.beginTx() )
            {
                Schema schema = graphDb.schema();
                indexDefinition = schema.indexFor( Label.label( "User" ) )
                        .on( "username" )
                        .create();
                tx.success();
            }

Indexes are populated asynchronously when they are first created. It is possible to use the core API to wait for index population to complete:

            try ( Transaction tx = graphDb.beginTx() )
            {
                Schema schema = graphDb.schema();
                schema.awaitIndexOnline( indexDefinition, 10, TimeUnit.SECONDS );
            }

It is also possible to query the progress of the index population:

            try ( Transaction tx = graphDb.beginTx() )
            {
                Schema schema = graphDb.schema();
                System.out.println( String.format( "Percent complete: %1.0f%%",
                        schema.getIndexPopulationProgress( indexDefinition ).getCompletedPercentage() ) );
            }

Now we can add the users:

            try ( Transaction tx = graphDb.beginTx() )
            {
                Label label = Label.label( "User" );

                // Create some users
                for ( int id = 0; id < 100; id++ )
                {
                    Node userNode = graphDb.createNode( label );
                    userNode.setProperty( "username", "user" + id + "@neo4j.org" );
                }
                System.out.println( "Users created" );
                tx.success();
            }

Please read Section 3.6, “Managing resources when using long running transactions” on how to properly close ResourceIterators returned from index lookups.

And here is how to find a user by id:

            Label label = Label.label( "User" );
            int idToFind = 45;
            String nameToFind = "user" + idToFind + "@neo4j.org";
            try ( Transaction tx = graphDb.beginTx() )
            {
                try ( ResourceIterator<Node> users =
                              graphDb.findNodes( label, "username", nameToFind ) )
                {
                    ArrayList<Node> userNodes = new ArrayList<>();
                    while ( users.hasNext() )
                    {
                        userNodes.add( users.next() );
                    }

                    for ( Node node : userNodes )
                    {
                        System.out.println(
                                "The username of user " + idToFind + " is " + node.getProperty( "username" ) );
                    }
                }
            }

When updating the name of a user, the index is updated as well:

            try ( Transaction tx = graphDb.beginTx() )
            {
                Label label = Label.label( "User" );
                int idToFind = 45;
                String nameToFind = "user" + idToFind + "@neo4j.org";

                for ( Node node : loop( graphDb.findNodes( label, "username", nameToFind ) ) )
                {
                    node.setProperty( "username", "user" + (idToFind + 1) + "@neo4j.org" );
                }
                tx.success();
            }

When deleting a user, it is automatically removed from the index:

            try ( Transaction tx = graphDb.beginTx() )
            {
                Label label = Label.label( "User" );
                int idToFind = 46;
                String nameToFind = "user" + idToFind + "@neo4j.org";

                for ( Node node : loop( graphDb.findNodes( label, "username", nameToFind ) ) )
                {
                    node.delete();
                }
                tx.success();
            }

In case we change our data model, we can drop the index as well:

            try ( Transaction tx = graphDb.beginTx() )
            {
                Label label = Label.label( "User" );
                for ( IndexDefinition indexDefinition : graphDb.schema()
                        .getIndexes( label ) )
                {
                    // There is only one index
                    indexDefinition.drop();
                }

                tx.success();
            }