AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Neo4j Query Tuning Skill

skill-neo4j-contrib-neo4j-skills-neo4j-query-tuning-skill · by neo4j-contrib

Diagnoses and fixes slow Neo4j Cypher queries by reading execution plans, identifying

No reviews yet
0 installs
32 views
0.0% view→install

Install

$ agentstack add skill-neo4j-contrib-neo4j-skills-neo4j-query-tuning-skill

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-neo4j-contrib-neo4j-skills-neo4j-query-tuning-skill)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Neo4j Query Tuning Skill? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

When to Use

  • Query takes unexpectedly long; need root-cause analysis
  • EXPLAIN/PROFILE output in hand — needs interpretation
  • Identifying which index is missing or unused
  • Deciding between slotted / pipelined / parallel runtimes
  • Monitoring live queries: SHOW QUERIES, SHOW TRANSACTIONS
  • Cardinality estimates wrong (plan replanning needed)

When NOT to Use

  • Writing Cypher from scratchneo4j-cypher-skill
  • GDS algorithm performanceneo4j-gds-skill
  • Schema design / data modellingneo4j-modeling-skill

EXPLAIN vs PROFILE

| | EXPLAIN | PROFILE | |---|---|---| | Executes query? | No | Yes | | Returns data? | No | Yes | | Shows rows (actual) | No | Yes | | Shows dbHits (actual) | No | Yes | | Shows estimatedRows | Yes | Yes | | Cost | Zero | Full query cost |

Run PROFILE twice — first run warms page cache; second gives representative metrics.

EXPLAIN MATCH (p:Person {email: $email}) RETURN p.name
PROFILE MATCH (p:Person {email: $email}) RETURN p.name

Query API alternative (no driver):

curl -X POST https:///db//query/v2 \
  -u : -H "Content-Type: application/json" \
  -d '{"statement": "EXPLAIN MATCH (p:Person {email: $email}) RETURN p.name", "parameters": {"email": "a@b.com"}}'

Key Plan Metrics

| Metric | Good | Investigate if | |---|---|---| | dbHits | Low; drops after index added | High relative to rows | | rows | Shrinks early in plan | Large until final operator | | estimatedRows | Close to rows | >10× divergence from actual | | pageCacheHitRatio | >0.99 |

Scan output for `AllNodesScan`, `NodeByLabelScan`, `CartesianProduct`, `Eager`.

### Step 2 — Check Indexes
```cypher
SHOW INDEXES YIELD name, type, labelsOrTypes, properties, state
WHERE state = 'ONLINE'

Find whether the label/property from the bad operator has an index.

Step 3 — Create Missing Index

// RANGE index for equality/range predicates:
CREATE INDEX person_email IF NOT EXISTS FOR (n:Person) ON (n.email)
// TEXT index for CONTAINS/ENDS WITH:
CREATE TEXT INDEX person_bio IF NOT EXISTS FOR (n:Person) ON (n.bio)
// Composite for multi-property lookup:
CREATE INDEX order_status_date IF NOT EXISTS FOR (n:Order) ON (n.status, n.createdAt)

Wait for state = 'ONLINE' before measuring.

Step 4 — Profile After Fix

PROFILE 

Compare dbHits and elapsed ms before/after. Target: NodeIndexSeek replaces scan operators.

Step 5 — Stale Statistics (if estimatedRows wildly off)

CALL db.prepareForReplanning()
// or resample a specific index:
CALL db.resampleIndex("person_email")
// or resample all outdated:
CALL db.resampleOutdatedIndexes()

Config: dbms.cypher.statistics_divergence_threshold (default 0.75 — plan expires when stat changes >75%).


Fixing Common Plan Problems

Missing Index → NodeByLabelScan / AllNodesScan

// Force index hint when planner ignores it:
MATCH (p:Person {email: $email})
USING INDEX p:Person(email)
RETURN p.name
// Force label scan (sometimes faster for high selectivity):
MATCH (p:Person {email: $email})
USING SCAN p:Person
RETURN p.name

Wrong Anchor — Planner Picks Wrong Starting Node

Reorder MATCH or use hints:

// Force join at specific node:
MATCH (a:Author)-[:WROTE]->(b:Book)-[:IN_CATEGORY]->(c:Category {name: $cat})
USING JOIN ON b
RETURN a.name, b.title

CartesianProduct — Two Unconnected MATCHes

// Bad (Cartesian product):
MATCH (a:Author {id: $aid})
MATCH (b:Book  {id: $bid})
RETURN a.name, b.title

// Good (explicit join or WITH):
MATCH (a:Author {id: $aid})-[:WROTE]->(b:Book {id: $bid})
RETURN a.name, b.title
// Or: WITH between them to reset planning context

Eager — Read/Write Conflict

Three strategies (pick simplest):

  1. Add specific labels to MATCH nodes so planner distinguishes read/write sets
  2. Collect-then-write: WITH collect(n) AS nodes UNWIND nodes AS n SET n.x = 1
  3. CALL IN TRANSACTIONS: isolates each batch in its own transaction
CYPHER 25
MATCH (p:Person) WHERE p.score > 100
CALL (p) { SET p.tier = 'gold' } IN TRANSACTIONS OF 1000 ROWS

Expensive CONTAINS / ENDS WITH

// Needs TEXT index (RANGE does NOT support these):
CREATE TEXT INDEX person_bio IF NOT EXISTS FOR (n:Person) ON (n.bio)
MATCH (p:Person) WHERE p.bio CONTAINS $keyword RETURN p.name

Over-Traversal — Push LIMIT Early

// Bad: LIMIT after expensive join
MATCH (a:Author)-[:WROTE]->(b:Book)-[:REVIEWED_BY]->(r:Review)
RETURN a.name, b.title, r.text LIMIT 10

// Good: anchor limit before fan-out
MATCH (a:Author)-[:WROTE]->(b:Book)
WITH a, b LIMIT 10
MATCH (b)-[:REVIEWED_BY]->(r:Review)
RETURN a.name, b.title, r.text

Cypher Runtime Selection

| Runtime | Select | Best For | Avoid When | |---|---|---|---| | pipelined | CYPHER runtime=pipelined | Default OLTP; streaming, low memory | Unsupported operators fall back to slotted | | slotted | CYPHER runtime=slotted | Guaranteed stable behavior; debug | Performance-critical OLTP | | parallel | CYPHER 25 runtime=parallel | Large analytical scans; aggregations | OLTP, writes, short queries, Aura Free |

Pipelined is default for most queries. Parallel requires dbms.cypher.parallel.worker_limit configured; available on Enterprise and Aura Pro 2025+.

// Force parallel for large aggregation:
CYPHER 25 runtime=parallel
MATCH (n:Transaction) WHERE n.amount > 1000
RETURN n.currency, count(*), sum(n.amount)

Query Monitoring Commands

// Live queries + resource usage:
SHOW QUERIES YIELD query, queryId, elapsedTimeMillis, allocatedBytes, status, username

// Running transactions:
SHOW TRANSACTIONS YIELD transactionId, currentQuery, currentQueryProgress, elapsedTime, status, username, cpuTime, activeLockCount  // currentQueryProgress added [2026.03]

// Kill a specific transaction:
TERMINATE TRANSACTION $transactionId

// Kill a query:
TERMINATE QUERY $queryId

// Graph count stats (node/rel counts by label/type — feed into planner):
CALL db.stats.retrieve('GRAPH COUNTS') YIELD section, data RETURN section, data

// Token stats (label/property/rel-type IDs):
CALL db.stats.retrieve('TOKENS') YIELD section, data RETURN section, data

Full monitoring reference → [references/stats-and-monitoring.md](references/stats-and-monitoring.md)


Checklist

  • [ ] Run EXPLAIN first — identifies plan problems without execution cost
  • [ ] Check for AllNodesScan / NodeByLabelScan — missing index
  • [ ] Check for CartesianProduct — missing join predicate
  • [ ] Check for Eager — read/write conflict
  • [ ] SHOW INDEXES — confirm relevant index exists and state = 'ONLINE'
  • [ ] Create missing index; wait for ONLINE
  • [ ] Run PROFILE twice — first warms cache, second is representative
  • [ ] Compare dbHits before/after fix
  • [ ] If estimatedRows wildly off → CALL db.prepareForReplanning()
  • [ ] Push LIMIT / WITH n LIMIT k before high-fanout operations
  • [ ] For CONTAINS/ENDS WITH — TEXT index, not RANGE
  • [ ] For large analytical queries — consider runtime=parallel
  • [ ] Kill long-running queries with TERMINATE TRANSACTION

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.