Indexes for search performance

This section explains how to manage indexes used for search performance.

This section describes how to manage indexes. For query performance purposes, it is important to also understand how the indexes are used by the Cypher planner. Refer to Query tuning for examples and in-depth discussions on how query plans result from different index and query scenarios. See specifically The use of indexes for examples of how various index scenarios result in different query plans.

For information on index configuration and limitations, refer to Operations Manual → Index configuration.

1. Introduction

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.

Once an index has been created, it will be managed and kept up to date by the DBMS. Neo4j will automatically pick up and start using the index once it has been created and brought online.

Cypher enables the creation of indexes on one or more properties for all nodes that have a given label:

  • An index that is created on a single property for any given label is called a single-property index.

  • An index that is created on more than one property for any given label is called a composite index.

Differences in the usage patterns between composite and single-property indexes are described in Composite index limitations.

The following is true for indexes:

  • Best practice is to give the index a name when it is created. If the index is not explicitly named, it will get an auto-generated name.

  • The index name must be unique among both indexes and constraints.

  • Index creation is by default not idempotent, and an error will be thrown if you attempt to create the same index twice. Using the keyword IF NOT EXISTS makes the command idempotent, and no error will be thrown if you attempt to create the same index twice.

2. Syntax

Table 1. Syntax for managing indexes
Command Description Comment
CREATE [BTREE] INDEX [index_name] [IF NOT EXISTS]
FOR (n:LabelName)
ON (n.propertyName)
[OPTIONS "{" option: value[,...] "}"]

Create a single-property index.

Best practice is to give the index a name when it is created. If the index is not explicitly named, it will get an auto-generated name.

The index name must be unique among both indexes and constraints.

The command is optionally idempotent, with the default behavior to throw an error if you attempt to create the same index twice. With IF NOT EXISTS, no error is thrown and nothing happens should an index with the same name, schema or both already exist. It may still throw an error should a conflicting constraint exist.

Index provider and configuration can be specified using the OPTIONS clause.

CREATE [BTREE] INDEX [index_name] [IF NOT EXISTS]
FOR (n:LabelName)
ON (n.propertyName_1,
    n.propertyName_2,
    …
    n.propertyName_n)
[OPTIONS "{" option: value[,...] "}"]

Create a composite index.

DROP INDEX index_name [IF EXISTS]

Drop an index

The command is optionally idempotent, with the default behavior to throw an error if you attempt to drop the same index twice. With IF EXISTS, no error is thrown and nothing happens should the index not exist.

SHOW [ALL|BTREE] INDEX[ES] [BRIEF|VERBOSE [OUTPUT]]

List indexes in the database, either all or B-tree only.

DROP INDEX ON :LabelName(propertyName)

Drop a single-property index without specifying a name.

This syntax is deprecated.

DROP INDEX ON :LabelName (n.propertyName_1,
n.propertyName_2,
…
n.propertyName_n)

Drop a composite index without specifying a name.

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).

3. Composite index limitations

Like single-property indexes, composite indexes support all predicates:

  • equality check: n.prop = value

  • list membership check: n.prop IN list

  • existence check: exists(n.prop)

  • range search: n.prop > value

  • prefix search: STARTS WITH

  • suffix search: ENDS WITH

  • substring search: CONTAINS

However, predicates might be planned as existence check and a filter. For most predicates, this can be avoided by following these restrictions:

  • If there is any equality check and list membership check predicates, they need to be for the first properties defined by the index.

  • There can be up to one range search or prefix search predicate.

  • There can be any number of existence check predicates.

  • Any predicate after a range search, prefix search or existence check predicate has to be an existence check predicate.

However, the suffix search and substring search predicates are always planned as existence check and a filter and any predicates following after will therefore also be planned as such.

For example, an index on :Label(prop1,prop2,prop3,prop4,prop5,prop6) and predicates:

WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 < 'e' AND n.prop5 = true AND exists(n.prop6)

will be planned as:

WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND exists(n.prop4) AND exists(n.prop5) AND exists(n.prop6)

with filters on n.prop4 < 'e' and n.prop5 = true, since n.prop3 has a range search predicate.

And an index on :Label(prop1,prop2) with predicates:

WHERE n.prop1 ENDS WITH 'x' AND n.prop2 = false

will be planned as:

WHERE exists(n.prop1) AND exists(n.prop2)

with filters on n.prop1 ENDS WITH 'x' and n.prop2 = false, since n.prop1 has a suffix search predicate.

Composite indexes require predicates on all properties indexed. If there are predicates on only a subset of the indexed properties, it will not be possible to use the composite index. To get this kind of fallback behavior, it is necessary to create additional indexes on the relevant sub-set of properties or on single properties.

4. Examples

4.1. Create a single-property index

A named index on a single property for all nodes that have a particular label can be created with CREATE INDEX index_name FOR (n:Label) ON (n.property). Note that the index is not immediately available, but will be created in the background.

Query
CREATE INDEX index_name FOR (n:Person)
ON (n.surname)

Note that the index name needs to be unique.

+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1

4.2. Create a single-property index only if it does not already exist

If it is unknown if an index exists or not but we want to make sure it does, we add IF NOT EXISTS.

Query
CREATE INDEX index_name IF NOT EXISTS FOR (n:Person)
ON (n.surname)

Note that the index will not be created if there already exists an index with the same name, same schema or both.

+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+

4.3. Create a single-property index with specified index provider

To create a single property index with a specific index provider, the OPTIONS clause is used. Valid values for the index provider is native-btree-1.0 and lucene+native-3.0, default if nothing is specified is native-btree-1.0.

Query
CREATE BTREE INDEX index_with_provider FOR (n:Label)
ON (n.prop1)OPTIONS { indexProvider: 'native-btree-1.0' }

Can be combined with specifying index configuration.

+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1

4.4. Create a single-property index with specified index configuration

To create a single property index with a specific index configuration, the OPTIONS clause is used. Valid configuration settings are spatial.cartesian.min, spatial.cartesian.max, spatial.cartesian-3d.min, spatial.cartesian-3d.max, spatial.wgs-84.min, spatial.wgs-84.max, spatial.wgs-84-3d.min, and spatial.wgs-84-3d.max. Non-specified settings get their respective default values.

Query
CREATE BTREE INDEX index_with_config FOR (n:Label)
ON (n.prop2)OPTIONS { indexConfig: { `spatial.cartesian.min`: [-100.0, -100.0],
  `spatial.cartesian.max`: [100.0, 100.0]}}

Can be combined with specifying index provider.

+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1

4.5. Create a composite index

A named index on multiple properties for all nodes that have a particular label — i.e. a composite index — can be created with CREATE INDEX index_name FOR (n:Label) ON (n.prop1, …​, n.propN). Only nodes labeled with the specified label and which contain all the properties in the index definition will be added to the index. Note that the composite index is not immediately available, but will be created in the background. The following statement will create a named composite index on all nodes labeled with Person and which have both an age and country property:

Query
CREATE INDEX index_name FOR (n:Person)
ON (n.age, n.country)

Note that the index name needs to be unique.

+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1

4.6. Create a composite index with specified index provider and configuration

To create a composite index with a specific index provider and configuration, the OPTIONS clause is used. Valid values for the index provider is native-btree-1.0 and lucene+native-3.0, default if nothing is specified is native-btree-1.0. Valid configuration settings are spatial.cartesian.min, spatial.cartesian.max, spatial.cartesian-3d.min, spatial.cartesian-3d.max, spatial.wgs-84.min, spatial.wgs-84.max, spatial.wgs-84-3d.min, and spatial.wgs-84-3d.max. Non-specified settings get their respective default values.

Query
CREATE INDEX index_with_options FOR (n:Label)
ON (n.prop1, n.prop2)OPTIONS { indexProvider: 'lucene+native-3.0',
  indexConfig: { `spatial.wgs-84.min`: [-100.0, -80.0], `spatial.wgs-84.max`: [100.0, 80.0]}}

Specifying index provider and configuration can be done individually.

+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1

4.7. Drop an index

An index on all nodes that have a label and property/properties combination can be dropped using the name with the DROP INDEX index_name command. The name of the index can be found using the SHOW INDEXES command, given in the output column name.

Query
DROP INDEX index_name
+-------------------+
| No data returned. |
+-------------------+
Indexes removed: 1

4.8. Drop a non-existing index

If it is uncertain if an index exists and you want to drop it if it does but not get an error should it not, use:

Query
DROP INDEX missing_index_name IF EXISTS
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+

4.9. List indexes

Listing all indexes can be done with SHOW INDEXES, which will produce a table with the following columns:

Table 2. List indexes output
Column Description Brief output Verbose output

id

The id of the index.

+

+

name

Name of the index (explicitly set by the user or automatically assigned).

+

+

state

Current state of the index.

+

+

populationPercent

% of index population.

+

+

uniqueness

Tells if the index is only meant to allow one value per key.

+

+

type

The IndexType of this index (BTREE or FULLTEXT).

+

+

entityType

Type of entities this index represents (nodes or relationship).

+

+

labelsOrTypes

The labels or relationship types of this index.

+

+

properties

The properties of this index.

+

+

indexProvider

The index provider for this index.

+

+

options

The options passed to CREATE command.

+

failureMessage

The failure description of a failed index.

+

createStatement

Statement used to create the index.

+

4.9.1. Options for listing indexes

The old built-in procedures for listing indexes, such as db.indexes, work as before and are not affected by the SHOW INDEXES privilege.

Query
SHOW INDEXES

To show all columns, use SHOW INDEXES VERBOSE. To show only BTREE indexes, use SHOW BTREE INDEXES. One of the output columns from SHOW INDEXES is the name of the index. This can be used to drop the index with the DROP INDEX command.

+------------------------------------------------------------------------------------------------------------------------------------------------+
| id | name             | state    | populationPercent | uniqueness  | type    | entityType | labelsOrTypes | properties    | indexProvider      |
+------------------------------------------------------------------------------------------------------------------------------------------------+
| 2  | "index_58a1c03e" | "ONLINE" | 100.0             | "NONUNIQUE" | "BTREE" | "NODE"     | ["Person"]    | ["location"]  | "native-btree-1.0" |
| 3  | "index_d7c12ba3" | "ONLINE" | 100.0             | "NONUNIQUE" | "BTREE" | "NODE"     | ["Person"]    | ["highScore"] | "native-btree-1.0" |
| 1  | "index_deeafdb2" | "ONLINE" | 100.0             | "NONUNIQUE" | "BTREE" | "NODE"     | ["Person"]    | ["firstname"] | "native-btree-1.0" |
+------------------------------------------------------------------------------------------------------------------------------------------------+
3 rows

4.10. Deprecated syntax

4.10.1. Drop a single-property index

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)
+-------------------+
| No data returned. |
+-------------------+
Indexes removed: 1

4.10.2. Drop a composite index

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)
+-------------------+
| No data returned. |
+-------------------+
Indexes removed: 1