Constraints and indexes

Neo4j · Practical · 14 min

Without indexes every lookup scans a label; without constraints MERGE races create duplicates.

A Cypher query usually starts by finding a few anchor nodes and then traverses from them. Indexes make finding those anchors fast; traversals themselves need no index.

Constraints:

  • CREATE CONSTRAINT person_email FOR (p:Person) REQUIRE p.email IS UNIQUE (also creates a backing index)
  • REQUIRE p.email IS NOT NULL property existence and REQUIRE p.age IS :: INTEGER property type (Enterprise Edition)
  • REQUIRE (p.firstName, p.lastName) IS NODE KEY: unique and present (Enterprise)

A uniqueness constraint is what makes concurrent MERGE safe: without it two transactions can both create the same person.

Indexes:

  • range (default): CREATE INDEX person_name FOR (p:Person) ON (p.name) for equality, ranges, prefix and sorting
  • text: for CONTAINS / ENDS WITH on strings
  • full-text: CREATE FULLTEXT INDEX ... ON EACH [p.bio], queried with db.index.fulltext.queryNodes, Lucene-powered with scoring
  • point for spatial, and vector indexes for embeddings (similarity search)
  • composite indexes over several properties, and indexes on relationship properties

Use SHOW CONSTRAINTS and SHOW INDEXES; new indexes populate in the background (state becomes ONLINE). Add IF NOT EXISTS to make schema scripts rerunnable.

Example

CREATE CONSTRAINT person_email IF NOT EXISTS
FOR (p:Person) REQUIRE p.email IS UNIQUE;

CREATE INDEX person_name IF NOT EXISTS
FOR (p:Person) ON (p.name);

CREATE TEXT INDEX company_name_text IF NOT EXISTS
FOR (c:Company) ON (c.name);

SHOW INDEXES YIELD name, type, labelsOrTypes, properties, state;
// "person_email"      RANGE ["Person"]  ["email"] "ONLINE"   (backs the constraint)
// "person_name"       RANGE ["Person"]  ["name"]  "ONLINE"
// "company_name_text" TEXT  ["Company"] ["name"]  "ONLINE"

MERGE (p:Person {email: 'ann@example.com'})   // now safe under concurrency
SET p.name = 'Ann';

Remember

  • Indexes find anchor nodes; traversals need no index
  • Uniqueness constraints prevent duplicates and make MERGE safe
  • Range for equality/ranges, text for CONTAINS, full-text for search, vector for embeddings
  • Use IF NOT EXISTS and check SHOW INDEXES state

Watch out for

  • Running MERGE on a property with no uniqueness constraint in parallel imports and getting duplicate nodes.

Check yourself

  1. Which index type best serves WHERE c.name CONTAINS 'cme'?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: neo4j.com/docs/cypher-manual/current/constraints/managing-constraints/