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 NULLproperty existence andREQUIRE p.age IS :: INTEGERproperty 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 WITHon strings - full-text:
CREATE FULLTEXT INDEX ... ON EACH [p.bio], queried withdb.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.