Using indexes

It is possible to create and use all the index types described in Cypher Manual → Indexes.

This section demonstrates how to work with indexes with an example of a user database. For information about how to create an index on all User nodes that have a username property, see Cypher Manual → Create a single-property index for nodes.

The source code used in this example is found at: EmbeddedNeo4jWithIndexing.java

Begin with starting the database server:

DatabaseManagementService managementService = new DatabaseManagementServiceBuilder( databaseDirectory ).build();
GraphDatabaseService graphDb = managementService.database( DEFAULT_DATABASE_NAME );

Then, you can configure the database to index users by name. This only needs to be done once.

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 usernamesIndex;
try ( Transaction tx = graphDb.beginTx() )
{
    Schema schema = tx.schema();
    usernamesIndex = schema.indexFor( Label.label( "User" ) )  (1)
            .on( "username" )                                  (2)
            .withName( "usernames" )                           (3)
            .create();                                         (4)
    tx.commit();                                               (5)
}
1 A single-property index is defined on a label in combination with a property name. Start your index definition by specifying the node label.
2 Next, define the property that should be part of this index. Index all nodes with the User label, that also have a username property. This way, you can find User nodes by their username properties.
3 An index always has a name. If not specified, it will be generated for you.
4 Calling create is necessary for the index definition to be created in the database. This index is now created, but it still only exists in your current transaction.
5 Committing the transaction commits your new index to the database. It will become available for use once it has finished populating itself with the existing data in your database.

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

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

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

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

Now you 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 = tx.createNode( label );
        userNode.setProperty( "username", "user" + id + "@neo4j.org" );
    }
    System.out.println( "Users created" );
    tx.commit();
}

For information on how to properly close ResourceIterators returned from index lookups, read Resource iterator.

And this 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 =
                  tx.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( tx.findNodes( label, "username", nameToFind ) ) )
    {
        node.setProperty( "username", "user" + (idToFind + 1) + "@neo4j.org" );
    }
    tx.commit();
}

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( tx.findNodes( label, "username", nameToFind ) ) )
    {
        node.delete();
    }
    tx.commit();
}

In case you change your data model, you can drop the index as well:

try ( Transaction tx = graphDb.beginTx() )
{
    IndexDefinition usernamesIndex = tx.schema().getIndexByName( "usernames" ); (1)
    usernamesIndex.drop();
    tx.commit();
}
1 You look up the index by the index name you gave it when you created it. Index names are guaranteed to be unique, to ensure that you will not mistakenly find and drop the wrong index.