A query that is fast on test data can be slow in production; plans show exactly why.
Prefix a query with EXPLAIN to see the plan without running it, or PROFILE to run it and see actual rows and db hits (storage accesses) per operator. Read plans bottom-up: the leaves find starting points, the rest expands and filters.
What to look for:
- Starting point:
NodeIndexSeekorNodeUniqueIndexSeekis good;NodeByLabelScanmeans scanning all nodes of a label;AllNodesScan(no label) is worst. Add the missing index or label. - CartesianProduct: two disconnected patterns multiplied together; connect them or split the query.
- Eager: the planner must materialise all rows before continuing, usually because a query reads and writes the same data. In big imports this blows up memory; split into separate statements or use
CALL ... IN TRANSACTIONS. - Row counts that explode mid-plan: aggregate or
DISTINCTearlier, or filter before expanding.
The planner is cost-based and uses statistics (counts per label and relationship type, index selectivity). Help it by using parameters (plan cache reuse) and by anchoring patterns on the most selective node. Hints like USING INDEX p:Person(name) exist but are a last resort.
Also check summary.notifications in drivers: Neo4j warns about cartesian products, unbounded paths and deprecated syntax.
Example
PROFILE
MATCH (p:Person {name: 'Ann'})-[:FOLLOWS]->(f)-[:WORKS_AT]->(c:Company)
RETURN c.name, count(*) AS n;
// Before an index exists (read bottom-up):
// NodeByLabelScan p:Person rows 1000000 db hits 1000001
// Filter p.name = $autostring_0 rows 1
// Expand(All) (p)-[:FOLLOWS]->(f)
// ...
CREATE INDEX person_name IF NOT EXISTS FOR (p:Person) ON (p.name);
// After:
// NodeIndexSeek p:Person(name) rows 1 db hits 2Remember
- EXPLAIN shows the plan; PROFILE runs it with rows and db hits
- Aim for index seeks as starting points, not label or all-node scans
- CartesianProduct and Eager operators are red flags
- Filter and aggregate early to keep row counts small
Watch out for
- Profiling only on a tiny dev dataset where every plan looks fast.