This section explains how to work with indexes in Neo4j and Cypher.
WHERE (single-property index)WHERE (composite index)WHERE (single-property index)WHERE using multiple range comparisonsIN (single-property index)IN (composite index)STARTS WITH (single-property index)ENDS WITH (single-property index)CONTAINS (single-property index)exists (single-property index)A database index is a redundant copy of some of the data in the database for the purpose of making searches of related 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 enables the creation of indexes on one or more properties 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 when the graph is changed. Neo4j will automatically pick up and start using the index once it has been created and brought online.
|
Section 3.6.4, “Planner hints and the USING keyword” describes how to make the Cypher planner use specific indexes (especially in cases where the planner would not necessarily have used them). |
| Index configuration and limitations | |
|---|---|
|
For information on index configuration and limitations, refer to Operations Manual → Index configuration. |
An index on a single property for all nodes that have a particular label can be created with CREATE INDEX ON :Label(property). Note that the index is not immediately available, but will be created in the background.
Query.
CREATE INDEX ON :Person(firstname)
Result.
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
An index on multiple properties for all nodes that have a particular label — i.e. a composite index — can be created with
CREATE INDEX ON :Label(prop1, …, propN). Only nodes labeled with the specified label and which contain all the properties in the index definition will be added to
the index. The following statement will create a composite index on all nodes labeled with Person and which have both an age and country property:
Query.
CREATE INDEX ON :Person(age, country)
Assume we execute the query CREATE (a:Person {firstname: 'Bill', age: 34, country: 'USA'}), (b:Person {firstname: 'Sue', age: 39}). Node a has both an age and a country property, and so it will be added to the composite index. However, as node b has no country property, it will not be added to the composite index. Note that the composite index is not immediately available, but will
be created in the background.
Result.
+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1
Calling the built-in procedure db.indexes will list all the indexes in the database.
Query.
CALL db.indexes
Result.
+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| description | label | properties | state | type | provider | failureMessage |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| "INDEX ON :Person(firstname)" | "Person" | ["firstname"] | "ONLINE" | "node_label_property" | {version -> "2.0", key -> "lucene+native"} | "" |
| "INDEX ON :Person(highScore)" | "Person" | ["highScore"] | "ONLINE" | "node_label_property" | {version -> "2.0", key -> "lucene+native"} | "" |
| "INDEX ON :Person(location)" | "Person" | ["location"] | "ONLINE" | "node_label_property" | {version -> "2.0", key -> "lucene+native"} | "" |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
3 rows
An index on all nodes that have a label and single property combination can be dropped with DROP INDEX ON :Label(property).
Query.
DROP INDEX ON :Person(firstname)
Result.
+-------------------+
| No data returned. |
+-------------------+
Indexes removed: 1
A composite index on all nodes that have a label and multiple property combination can be dropped with DROP INDEX ON :Label(prop1, …, propN). The following statement will drop a composite index on all nodes labeled with Person and which have both an age and country property:
Query.
DROP INDEX ON :Person(age, country)
Result.
+-------------------+
| No data returned. |
+-------------------+
Indexes removed: 1
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(firstname) index, if it exists.
Query.
MATCH (person:Person { firstname: 'Andy' })
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| +ProduceResults | 1 | 1 | 0 | 0 | 1 | 0.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| +NodeIndexSeek | 1 | 1 | 3 | 0 | 1 | 0.0000 | person | :Person(firstname) |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
Total database accesses: 3
A query containing equality comparisons of a single indexed property in the WHERE clause is backed automatically by the index. It is also possible for a query with multiple OR predicates to use multiple indexes, if indexes exist on the properties. For example, if indexes exist on both :Label(p1) and :Label(p2), MATCH (n:Label) WHERE n.p1 = 1 OR n.p2 = 2 RETURN n will use both indexes.
Query.
MATCH (person:Person)
WHERE person.firstname = 'Andy'
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| +ProduceResults | 1 | 1 | 0 | 0 | 1 | 0.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| +NodeIndexSeek | 1 | 1 | 3 | 0 | 1 | 0.0000 | person | :Person(firstname) |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
Total database accesses: 3
A query containing equality comparisons for all the properties of a composite index will automatically be backed by the same index. The following query will use the composite index defined earlier:
Query.
MATCH (n:Person)
WHERE n.age = 35 AND n.country = 'UK'
RETURN n
However, the query MATCH (n:Person) WHERE n.age = 35 RETURN n will not be backed by the composite index, as the query does not contain an equality predicate on the country property. It will only be backed by an index on the Person label and age property defined thus: :Person(age); i.e. a single-property index. Moreover, unlike single-property indexes, composite indexes currently do not support queries
containing the following types of predicates on properties in the index: existence check: exists(n.prop); range search: n.prop > value; prefix search: STARTS WITH; suffix search: ENDS WITH; and substring search: CONTAINS.
Result.
+-------------------------------------------------------------------------------------------+
| n |
+-------------------------------------------------------------------------------------------+
| Node[0]{surname:"Smith",age:35,name:"john",highScore:54321,firstname:"John",country:"UK"} |
+-------------------------------------------------------------------------------------------+
1 row
Single-property indexes are also automatically used for inequality (range) comparisons of an indexed property in the WHERE clause. Composite indexes are currently not able to support range comparisons.
Query.
MATCH (person:Person)
WHERE person.firstname > 'B'
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| +ProduceResults | 10 | 1 | 0 | 0 | 1 | 0.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| +NodeIndexSeekByRange | 10 | 1 | 3 | 0 | 1 | 0.0000 | person | :Person(firstname) > { AUTOSTRING0} |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
Total database accesses: 3
When the WHERE clause contains multiple inequality (range) comparisons for the same property, these can be combined in a single index range
seek.
Query.
MATCH (person:Person)
WHERE 10000 < person.highScore < 20000
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+-------------------------------------------------------------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+-------------------------------------------------------------------------+
| +ProduceResults | 5 | 1 | 0 | 0 | 1 | 0.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+-------------------------------------------------------------------------+
| +NodeIndexSeekByRange | 5 | 1 | 3 | 0 | 1 | 0.0000 | person | :Person(highScore) > { AUTOINT1} AND :Person(highScore) < { AUTOINT0} |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+-------------------------------------------------------------------------+
Total database accesses: 3
The IN predicate on person.firstname in the following query will use the single-property index Person(firstname) if it exists.
Query.
MATCH (person:Person)
WHERE person.firstname IN ['Andy', 'John']
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| +ProduceResults | 24 | 2 | 0 | 2 | 0 | 1.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| +NodeIndexSeek | 24 | 2 | 5 | 2 | 0 | 1.0000 | person | :Person(firstname) |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
Total database accesses: 5
The IN predicates on person.age and person.country in the following query will use the composite index Person(age, country) if it exists.
Query.
MATCH (person:Person)
WHERE person.age IN [10, 20, 35] AND person.country IN ['Sweden', 'USA', 'UK']
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+----------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+----------------------+
| +ProduceResults | 451 | 1 | 0 | 0 | 0 | 0.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+----------------------+
| +NodeIndexSeek | 451 | 1 | 11 | 0 | 0 | 0.0000 | person | :Person(age,country) |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+----------------------+
Total database accesses: 11
The STARTS WITH predicate on person.firstname in the following query will use the Person(firstname) index, if it exists. Composite indexes are currently not able to support STARTS WITH.
Query.
MATCH (person:Person)
WHERE person.firstname STARTS WITH 'And'
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+-------------------------------------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+-------------------------------------------------+
| +ProduceResults | 26 | 1 | 0 | 1 | 0 | 1.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+-------------------------------------------------+
| +NodeIndexSeekByRange | 26 | 1 | 3 | 1 | 0 | 1.0000 | person | :Person(firstname STARTS WITH $` AUTOSTRING0`) |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+-------------------------------------------------+
Total database accesses: 3
The ENDS WITH predicate on person.firstname in the following query will use the Person(firstname) index, if it exists. All values stored in the Person(firstname) index will be searched, and entries ending with 'hn' will be returned. This means that although the search will not be optimized to the extent of queries using =, IN, >, < or STARTS WITH, it is still faster than not using an index in the first place. Composite indexes are currently not able to support ENDS WITH.
Query.
MATCH (person:Person)
WHERE person.firstname ENDS WITH 'hn'
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+------------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+------------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| +ProduceResults | 26 | 1 | 0 | 0 | 0 | 0.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| +NodeIndexEndsWithScan | 26 | 1 | 3 | 1 | 0 | 1.0000 | person | :Person(firstname); $` AUTOSTRING0` |
+------------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
Total database accesses: 3
The CONTAINS predicate on person.firstname in the following query will use the Person(firstname) index, if it exists. All values stored in the Person(firstname) index will be searched, and entries containing 'h' will be returned. This means that although the search will not be optimized to the extent of queries using =, IN, >, < or STARTS WITH, it is still faster than not using an index in the first place. Composite indexes are currently not able to support CONTAINS.
Query.
MATCH (person:Person)
WHERE person.firstname CONTAINS 'h'
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+------------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+------------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| +ProduceResults | 26 | 1 | 0 | 0 | 0 | 0.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
| +NodeIndexContainsScan | 26 | 1 | 3 | 1 | 0 | 1.0000 | person | :Person(firstname); $` AUTOSTRING0` |
+------------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------------------------+
Total database accesses: 3
The exists(p.firstname) predicate in the following query will use the Person(firstname) index, if it exists. Composite indexes are currently not able to support the exists predicate.
Query.
MATCH (p:Person)
WHERE exists(p.firstname)
RETURN p
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| +ProduceResults | 2 | 2 | 0 | 0 | 0 | 0.0000 | p | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
| +NodeIndexScan | 2 | 2 | 4 | 0 | 4 | 0.0000 | p | :Person(firstname) |
+-----------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+--------------------+
Total database accesses: 4
If a property with point values is indexed, the index is used for spatial distance searches as well as for range queries.
Query.
MATCH (p:Person)
WHERE distance(p.location, point({ x: 1, y: 2 }))< 2
RETURN p.location
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------------+-----------------------------------------------------------------------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------------+-----------------------------------------------------------------------------------+
| +ProduceResults | 15 | 9 | 0 | 30 | 0 | 1.0000 | p, p.location | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------------+-----------------------------------------------------------------------------------+
| +Projection | 15 | 9 | 9 | 30 | 0 | 1.0000 | p.location -- p | {p.location : p.location} |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------------+-----------------------------------------------------------------------------------+
| +Filter | 15 | 9 | 9 | 30 | 0 | 1.0000 | p | distance(p.location, point({x: $` AUTOINT0`, y: $` AUTOINT1`})) < $` AUTOINT2` |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------------+-----------------------------------------------------------------------------------+
| +NodeIndexSeekByRange | 15 | 9 | 11 | 30 | 0 | 1.0000 | p | :Person(location) WHERE distance(_,point(x,y)) < Parameter( AUTOINT2,Integer) |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------------+-----------------------------------------------------------------------------------+
Total database accesses: 29
The ability to do index seeks on bounded ranges works even with the 2D and 3D spatial Point types.
Query.
MATCH (person:Person)
WHERE point({ x: 1, y: 5 })< person.location < point({ x: 2, y: 6 })
RETURN person
Query Plan.
Compiler CYPHER 3.4
Planner COST
Runtime INTERPRETED
Runtime version 3.4
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------+
| Operator | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables | Other |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------+
| +ProduceResults | 7 | 1 | 0 | 7 | 0 | 1.0000 | person | |
| | +----------------+------+---------+-----------------+-------------------+----------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------+
| +NodeIndexSeekByRange | 7 | 1 | 3 | 7 | 0 | 1.0000 | person | :Person(location) > point({x: { AUTOINT2}, y: { AUTOINT3}}) AND :Person(location) < point({x: { AUTOINT0}, y: { AUTOINT1}}) |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------+
Total database accesses: 3
Explicit indexes are alternative data structures, in which a user can explicitly maintain search and seek data for nodes and relationships. These data structures are special-purpose and the procedures are primarily provided for users who have legacy deployments depending on such structures.
| Signature | Description |
|---|---|
|
Add a node to an explicit index based on a specified key and value |
|
|
Add a relationship to an explicit index based on a specified key and value |
|
|
Search nodes from explicit automatic index. Replaces |
|
|
Search relationship from explicit automatic index. Replaces |
|
|
Get node from explicit automatic index. Replaces |
|
|
Get relationship from explicit automatic index. Replaces |
|
|
Remove an explicit index - YIELD type, name, config |
|
|
Check if a node explicit index exists |
|
|
Check if a relationship explicit index exists |
|
|
Get or create a node explicit index - YIELD type, name, config |
|
|
Get or create a relationship explicit index - YIELD type, name, config |
|
|
List all explicit indexes - YIELD type, name, config |
|
|
Remove a node from an explicit index with an optional key |
|
|
Remove a relationship from an explicit index with an optional key |
|
|
Search nodes from explicit index. Replaces |
|
|
Search relationship from explicit index. Replaces |
|
|
Search relationship in explicit index, starting at the node 'in' and ending at 'out' |
|
|
Search relationship in explicit index, starting at the node 'in' |
|
|
Search relationship in explicit index, ending at the node 'out' |
|
|
Get node from explicit index. Replaces |
|
|
Get relationship from explicit index. Replaces |
| Signature | Description |
|---|---|
|
|
Add a node to an explicit index based on a specified key and value |
| Signature | Description |
|---|---|
|
|
Add a relationship to an explicit index based on a specified key and value |
| Signature | Description |
|---|---|
|
|
Search nodes from explicit automatic index. Replaces |
| Signature | Description |
|---|---|
|
|
Search relationship from explicit automatic index. Replaces |
| Signature | Description |
|---|---|
|
|
Get node from explicit automatic index. Replaces |
| Signature | Description |
|---|---|
|
|
Get relationship from explicit automatic index. Replaces |
| Signature | Description |
|---|---|
|
|
Remove an explicit index - YIELD type, name, config |
| Signature | Description |
|---|---|
|
|
Check if a node explicit index exists |
| Signature | Description |
|---|---|
|
|
Check if a relationship explicit index exists |
| Signature | Description |
|---|---|
|
|
Get or create a node explicit index - YIELD type, name, config |
| Signature | Description |
|---|---|
|
|
Get or create a relationship explicit index - YIELD type, name, config |
| Signature | Description |
|---|---|
|
|
List all explicit indexes - YIELD type, name, config |
| Signature | Description |
|---|---|
|
|
Remove a node from an explicit index with an optional key |
| Signature | Description |
|---|---|
|
|
Remove a relationship from an explicit index with an optional key |
| Signature | Description |
|---|---|
|
|
Search nodes from explicit index. Replaces |
| Signature | Description |
|---|---|
|
|
Search relationship from explicit index. Replaces |
| Signature | Description |
|---|---|
|
|
Search relationship in explicit index, starting at the node 'in' and ending at 'out' |
| Signature | Description |
|---|---|
|
|
Search relationship in explicit index, starting at the node 'in' |
| Signature | Description |
|---|---|
|
|
Search relationship in explicit index, ending at the node 'out' |
| Signature | Description |
|---|---|
|
|
Get node from explicit index. Replaces |
| Signature | Description |
|---|---|
|
|
Get relationship from explicit index. Replaces |