14.1. Indexes

A database index is a redundant copy of information in the database for the purpose of making retrieving said data more efficient. This comes at the cost of additional storage space and slower writes, so deciding what to index and what not to index is an important and often non-trivial task.

Cypher allows the creation of indexes over a property for all nodes that have a given label. Once an index has been created, it will automatically be managed and kept up to date by the database whenever the graph is changed. Neo4j will automatically pick up and start using the index once it has been created and brought online.

Create an index

To create an index on a property for all nodes that have a label, use CREATE INDEX ON. Note that the index is not immediately available, but will be created in the background.

Query 

CREATE INDEX ON :Person(name)

Result

(empty result)

Try this query live create index on :`Person`(`name`); create (_0:`Person` {`name`:"Andres"}) create (_1:`Person` {`name`:"Mark"}) create (_2:`Person`) create (_3:`Person`) create (_4:`Person`) create (_5:`Person`) create (_6:`Person`) create (_7:`Person`) create (_8:`Person`) create (_9:`Person`) create (_10:`Person`) create (_11:`Person`) create (_12:`Person`) create (_13:`Person`) create (_14:`Person`) create (_15:`Person`) create (_16:`Person`) create (_17:`Person`) create (_18:`Person`) create (_19:`Person`) create (_20:`Person`) create (_21:`Person`) create (_0)-[:`KNOWS`]->(_1) ; create index on :Person(name)

Drop an index

To drop an index on all nodes that have a label and property combination, use the DROP INDEX clause.

Query 

DROP INDEX ON :Person(name)

Result

Indexes removed: 1

(empty result)

Try this query live create index on :`Person`(`name`); create (_0:`Person` {`name`:"Andres"}) create (_1:`Person` {`name`:"Mark"}) create (_2:`Person`) create (_3:`Person`) create (_4:`Person`) create (_5:`Person`) create (_6:`Person`) create (_7:`Person`) create (_8:`Person`) create (_9:`Person`) create (_10:`Person`) create (_11:`Person`) create (_12:`Person`) create (_13:`Person`) create (_14:`Person`) create (_15:`Person`) create (_16:`Person`) create (_17:`Person`) create (_18:`Person`) create (_19:`Person`) create (_20:`Person`) create (_21:`Person`) create (_0)-[:`KNOWS`]->(_1) ; drop index on :Person(name)

Use index

There is usually no need to specify which indexes to use in a query, Cypher will figure that out by itself. For example the query below will use the Person(name) index, if it exists. If you want Cypher to use specific indexes, you can enforce it using hints. See Section 15.4, “Using”.

Query 

MATCH (person:Person { name: 'Andres' })
RETURN person

Query Plan 

+-----------------+----------------+------+---------+-----------+---------------+
| Operator        | Estimated Rows | Rows | DB Hits | Variables | Other         |
+-----------------+----------------+------+---------+-----------+---------------+
| +ProduceResults |              1 |    1 |       0 | person    | person        |
| |               +----------------+------+---------+-----------+---------------+
| +NodeIndexSeek  |              1 |    1 |       2 | person    | :Person(name) |
+-----------------+----------------+------+---------+-----------+---------------+

Total database accesses: 2

Use index with WHERE using equality

Indexes are also automatically used for equality comparisons of an indexed property in the WHERE clause. If you want Cypher to use specific indexes, you can enforce it using hints. See Section 15.4, “Using”.

Query 

MATCH (person:Person)
WHERE person.name = 'Andres'
RETURN person

Query Plan 

+-----------------+----------------+------+---------+-----------+---------------+
| Operator        | Estimated Rows | Rows | DB Hits | Variables | Other         |
+-----------------+----------------+------+---------+-----------+---------------+
| +ProduceResults |              1 |    1 |       0 | person    | person        |
| |               +----------------+------+---------+-----------+---------------+
| +NodeIndexSeek  |              1 |    1 |       2 | person    | :Person(name) |
+-----------------+----------------+------+---------+-----------+---------------+

Total database accesses: 2

Use index with WHERE using inequality

Indexes are also automatically used for inequality (range) comparisons of an indexed property in the WHERE clause. If you want Cypher to use specific indexes, you can enforce it using hints. See Section 15.4, “Using”.

Query 

MATCH (person:Person)
WHERE person.name > 'B'
RETURN person

Query Plan 

+-----------------------+----------------+------+---------+-----------+---------------------------------+
| Operator              | Estimated Rows | Rows | DB Hits | Variables | Other                           |
+-----------------------+----------------+------+---------+-----------+---------------------------------+
| +ProduceResults       |             10 |    1 |       0 | person    | person                          |
| |                     +----------------+------+---------+-----------+---------------------------------+
| +NodeIndexSeekByRange |             10 |    1 |       2 | person    | :Person(name) > {  AUTOSTRING0} |
+-----------------------+----------------+------+---------+-----------+---------------------------------+

Total database accesses: 2

Use index with IN

The IN predicate on person.name in the following query will use the Person(name) index, if it exists. If you want Cypher to use specific indexes, you can enforce it using hints. See Section 15.4, “Using”.

Query 

MATCH (person:Person)
WHERE person.name IN ['Andres', 'Mark']
RETURN person

Query Plan 

+-----------------+----------------+------+---------+-----------+---------------+
| Operator        | Estimated Rows | Rows | DB Hits | Variables | Other         |
+-----------------+----------------+------+---------+-----------+---------------+
| +ProduceResults |             24 |    2 |       0 | person    | person        |
| |               +----------------+------+---------+-----------+---------------+
| +NodeIndexSeek  |             24 |    2 |       4 | person    | :Person(name) |
+-----------------+----------------+------+---------+-----------+---------------+

Total database accesses: 4

Use index with STARTS WITH

The STARTS WITH predicate on person.name in the following query will use the Person(name) index, if it exists.

Query 

MATCH (person:Person)
WHERE person.name STARTS WITH 'And'
RETURN person

Query Plan 

+-----------------------+----------------+------+---------+-----------+-------------------------------------------+
| Operator              | Estimated Rows | Rows | DB Hits | Variables | Other                                     |
+-----------------------+----------------+------+---------+-----------+-------------------------------------------+
| +ProduceResults       |             26 |    1 |       0 | person    | person                                    |
| |                     +----------------+------+---------+-----------+-------------------------------------------+
| +NodeIndexSeekByRange |             26 |    1 |       2 | person    | :Person(name STARTS WITH {  AUTOSTRING0}) |
+-----------------------+----------------+------+---------+-----------+-------------------------------------------+

Total database accesses: 2

Use index when checking for the existence of a property

The has(p.name) predicate in the following query will use the Person(name) index, if it exists.

Query 

MATCH (p:Person)
WHERE exists(p.name)
RETURN p

Query Plan 

+-----------------+----------------+------+---------+-----------+---------------+
| Operator        | Estimated Rows | Rows | DB Hits | Variables | Other         |
+-----------------+----------------+------+---------+-----------+---------------+
| +ProduceResults |              2 |    2 |       0 | p         | p             |
| |               +----------------+------+---------+-----------+---------------+
| +NodeIndexScan  |              2 |    2 |       3 | p         | :Person(name) |
+-----------------+----------------+------+---------+-----------+---------------+

Total database accesses: 3