Neo4j Node Uniqueness Constraints Not Using an Index: Diagnostic Guide
When a MATCH on a constrained property still scans nodes, the constraint may exist but the planner isn't using its automatic unique index. This guide shows how to verify constraint and index state, interpret PROFILE, and fix common schema and cluster causes.
17 Mar 2026, 03:54 UTC

Problem: MATCH on a constrained property still scans
You added a uniqueness constraint expecting constant-time lookups, but PROFILE shows a NodeByLabelScan or the query remains slow. In Neo4j 4.4+ and 5.x, CREATE CONSTRAINT FOR (n:Label) REQUIRE n.property IS UNIQUE automatically creates a unique index that the Cypher planner should use for MATCH patterns on that property. When it does not, the cause is usually constraint state, a schema mismatch, or a planner choice after a cluster change.
Quick cause / diagnostic table
| Symptom | Likely cause | First check |
|---|---|---|
| PROFILE shows NodeByLabelScan for MATCH (n:Label {property: $v}) | Constraint exists but index is not ONLINE, or property/label name mismatch | CALL db.constraints and SHOW INDEXES |
| Constraint creation hangs or times out on a large dataset | Existing duplicates or long uniqueness validation | Check for duplicates before creating |
| Duplicate insert throws ConstraintViolationException | Constraint is enforced, but application expects upserts | Query pattern and write transaction size |
| Index works on one core but not after rolling restart | Temporary index unavailability during Raft sync in clustering | Cluster member roles and index state |
Ordered checks
1. Confirm the constraint exists and is the right schema
Run in Cypher Browser or neo4j-shell as a user with READ access to schema.
CALL db.constraints()
YIELD name, type, description
RETURN name, type, descriptionExpect a row where description matches the label and property you intended, e.g. FOR (n:Label) REQUIRE n.property IS UNIQUE. Type will be reported as a uniqueness constraint. If you are on a pre-4.0 migration, separate CREATE INDEX statements may still exist; constraints unify definition and index.
2. Verify the backing unique index is ONLINE
A uniqueness constraint is backed by an automatically created unique index. Check its state.
SHOW INDEXES
YIELD name, type, entityType, labelsOrTypes, properties, state
WHERE labelsOrTypes CONTAINS 'Label' AND properties CONTAINS 'property'
RETURN name, type, stateRequired permissions: schema read. Risk: none for read. If state is POPULATING or FAILED, the planner will not use it. In clustering, index population is coordinated via Raft; a rolling restart can temporarily leave the index unavailable on a follower.
3. PROFILE the actual query
Run the query with PROFILE to see planner choice.
PROFILE MATCH (n:Label) WHERE n.property = $v RETURN n LIMIT 1Look for an operator that references the index for the label and property. A plan using the automatic index will show an index seek style operator rather than a full NodeByLabelScan. If the plan still scans, check for:
- Property name mismatch, case sensitivity, or different label
- Use of functions or expressions on n.property that prevent index use
- Parameter type issues that force a different plan
4. Check for duplicates before adding or repairing a constraint
Adding a uniqueness constraint on a large dataset validates existing data. Duplicates cause creation to fail with a ConstraintViolationException and block transaction commit.
MATCH (n:Label)
WITH n.property AS p, count(*) AS c
WHERE c > 1
RETURN p, cThis read is safe but can be expensive on large graphs. Run during low load.
5. Validate cluster state after changes
In causal clustering, constraints synchronize through the Raft consensus group. After a rolling restart, wait for all cores to report the index as ONLINE before relying on latency-sensitive reads.
CALL dbms.cluster.overview()Check that writable core is elected and followers are caught up.
Fixes tied to findings
Index not ONLINE. Wait for population to finish. Do not drop and recreate repeatedly; repeated creation attempts increase write load. If state is FAILED, review debug logs for out-of-memory or timeout during population.
Schema mismatch. Drop the incorrect constraint and recreate with the exact label and property names. Creating a constraint is a schema write requiring ADMIN privileges. Example syntax:
CREATE CONSTRAINT FOR (n:Label) REQUIRE n.property IS UNIQUERisk: the statement acquires a schema lock and validates uniqueness across the store. On large datasets this can take time and may time out. Consider creating during maintenance windows.
Planner not using index. Simplify the predicate to direct equality on the constrained property, e.g. MATCH (n:Label {property: $v}). Avoid wrapping the property in functions. Re-run PROFILE after any schema change; the planner caches plans.
Duplicate data blocking creation. Resolve duplicates first with a data remediation job, then create the constraint. Write operations that enforce the constraint add a uniqueness check per transaction, which increases latency for high-throughput CREATE/SET workloads but guarantees integrity.
Limitations and practical verification
Uniqueness constraints guarantee data integrity at the cost of write overhead. They are not a replacement for application-level validation under extreme write concurrency.
Practical verification after a fix:
- Confirm constraint presence with CALL db.constraints
- Confirm index state is ONLINE with SHOW INDEXES
- Run PROFILE on a representative MATCH and confirm the plan references the index rather than a full scan
Do not assume the index is used because the constraint exists; always verify with PROFILE on production-like data volumes.
Escalation criteria
- Constraint creation repeatedly times out after duplicate removal. Escalate for store analysis and possible offline index build.
- Index remains POPULATING for an extended period with growing transaction logs. Check system resources and cluster network.
- ConstraintViolationException occurs on writes that should be idempotent. Review write patterns and consider MERGE with proper keying.
- Planner consistently chooses scan despite ONLINE unique index and simple equality predicate. Collect query plan and schema metadata for Neo4j support review.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.