36.12. Automatic Indexing

Neo4j provides a single index for nodes and one for relationships in each database that automatically follow property values as they are added, deleted and changed on database primitives. This functionality is called auto indexing and is controlled both from the database configuration Map and through its own API.

Configuration

By default Auto Indexing is off for both Nodes and Relationships. To configure this in the conf/neo4j.properties file, use the configuration keys node_auto_indexing and relationship_auto_indexing. For embedded mode, use the configuration options GraphDatabaseSettings.node_auto_indexing and GraphDatabaseSettings.relationship_auto_indexing. In both cases, set the value to true. This will enable automatic indexing on startup. Just note that we’re not done yet, see below!

To actually auto index something, you have to set which properties should get indexed. You do this by listing the property keys to index on. In the configuration file, use the node_keys_indexable and relationship_keys_indexable configuration keys. When using embedded mode, use the GraphDatabaseSettings.node_keys_indexable and GraphDatabaseSettings.relationship_keys_indexable configuration keys. In all cases, the value should be a comma separated list of property keys to index on.

When coding in Java, it’s done like this:

/*
 * Creating the configuration, adding nodeProp1 and nodeProp2 as
 * auto indexed properties for Nodes and relProp1 and relProp2 as
 * auto indexed properties for Relationships. Only those will be
 * indexed. We also have to enable auto indexing for both these
 * primitives explicitly.
 */
GraphDatabaseService graphDb = new GraphDatabaseFactory().
    newEmbeddedDatabaseBuilder( storeDirectory ).
    setConfig( GraphDatabaseSettings.node_keys_indexable, "nodeProp1,nodeProp2" ).
    setConfig( GraphDatabaseSettings.relationship_keys_indexable, "relProp1,relProp2" ).
    setConfig( GraphDatabaseSettings.node_auto_indexing, "true" ).
    setConfig( GraphDatabaseSettings.relationship_auto_indexing, "true" ).
    newGraphDatabase();

Node node1 = null, node2 = null;
Relationship rel = null;
try ( Transaction tx = graphDb.beginTx() )
{
    // Create the primitives
    node1 = graphDb.createNode();
    node2 = graphDb.createNode();
    rel = node1.createRelationshipTo( node2,
            DynamicRelationshipType.withName( "DYNAMIC" ) );

    // Add indexable and non-indexable properties
    node1.setProperty( "nodeProp1", "nodeProp1Value" );
    node2.setProperty( "nodeProp2", "nodeProp2Value" );
    node1.setProperty( "nonIndexed", "nodeProp2NonIndexedValue" );
    rel.setProperty( "relProp1", "relProp1Value" );
    rel.setProperty( "relPropNonIndexed", "relPropValueNonIndexed" );

    // Make things persistent
    tx.success();
}

Search

The usefulness of the auto indexing functionality comes of course from the ability to actually query the index and retrieve results. To that end, you can acquire a ReadableIndex object from the AutoIndexer that exposes all the query and get methods of a full Index with exactly the same functionality. Continuing from the previous example, accessing the index is done like this:

try ( Transaction tx = graphDb.beginTx() )
{
    // Get the Node auto index
    ReadableIndex<Node> autoNodeIndex = graphDb.index()
            .getNodeAutoIndexer()
            .getAutoIndex();
    // node1 and node2 both had auto indexed properties, get them
    assertEquals( node1,
            autoNodeIndex.get( "nodeProp1", "nodeProp1Value" ).getSingle() );
    assertEquals( node2,
            autoNodeIndex.get( "nodeProp2", "nodeProp2Value" ).getSingle() );
    // node2 also had a property that should be ignored.
    assertFalse( autoNodeIndex.get( "nonIndexed",
            "nodeProp2NonIndexedValue" ).hasNext() );

    // Get the relationship auto index
    ReadableIndex<Relationship> autoRelIndex = graphDb.index()
            .getRelationshipAutoIndexer()
            .getAutoIndex();
    // One property was set for auto indexing
    assertEquals( rel,
            autoRelIndex.get( "relProp1", "relProp1Value" ).getSingle() );
    // The rest should be ignored
    assertFalse( autoRelIndex.get( "relPropNonIndexed",
            "relPropValueNonIndexed" ).hasNext() );
}

Runtime Configuration

The same options that are available during database creation via the configuration can also be set during runtime via the AutoIndexer API.

Gaining access to the AutoIndexer API and adding two Node and one Relationship properties to auto index is done like so:

// Start without any configuration
GraphDatabaseService graphDb = new GraphDatabaseFactory().
        newEmbeddedDatabase( storeDirectory );

// Get the Node AutoIndexer, set nodeProp1 and nodeProp2 as auto
// indexed.
AutoIndexer<Node> nodeAutoIndexer = graphDb.index()
        .getNodeAutoIndexer();
nodeAutoIndexer.startAutoIndexingProperty( "nodeProp1" );
nodeAutoIndexer.startAutoIndexingProperty( "nodeProp2" );

// Get the Relationship AutoIndexer
AutoIndexer<Relationship> relAutoIndexer = graphDb.index()
        .getRelationshipAutoIndexer();
relAutoIndexer.startAutoIndexingProperty( "relProp1" );

// None of the AutoIndexers are enabled so far. Do that now
nodeAutoIndexer.setEnabled( true );
relAutoIndexer.setEnabled( true );

[Note]Note

Parameters to the AutoIndexers passed through the Configuration and settings made through the API are cumulative. So you can set some beforehand known settings, do runtime checks to augment the initial configuration and then enable the desired auto indexers - the final configuration is the same regardless of the method used to reach it.

Updating the Automatic Index

Updates to the auto indexed properties happen of course automatically as you update them. Removal of properties from the auto index similarly happens when you remove the actual property, but only while the property is auto indexed. In particular, if you had configured your database to auto index a property, but later removed it from the configuration, deleting that property will not remove it from the auto index.