Claude-skill-registry drasi-queries
Guide for writing Drasi continuous queries using Cypher. Use this when asked to create, modify, or troubleshoot Drasi queries.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/drasi-queries" ~/.claude/skills/majiayu000-claude-skill-registry-drasi-queries && rm -rf "$T"
skills/data/drasi-queries/SKILL.mdDrasi Query Development Skill
Use this skill when developing Drasi ContinuousQuery definitions with Cypher. If the user is not working with Drasi, do not apply this skill.
IMPORTANT: Use the
context7 MCP server with library ID /drasi-project/docs to get the latest Drasi documentation and Cypher syntax. Do not assume—verify current supported features.
Verify-first any Drasi capability or function behavior that may be version-dependent:
[VERIFY] EvidenceType = Docs | ReleaseNotes | Issue | Repro WhereToCheck = <URL, repo, command, or repro steps>
Capability & Version Notes
- Tested against Drasi X.Y (record
output and keep this line current; version not verified in this repo).drasi version - Cypher support follows Drasi's documented subset; GQL support is additive and must be explicitly enabled via
.queryLanguage: GQL - Verified against Drasi Cypher docs: features not listed in the supported subset (e.g.,
,collect
,DISTINCT
,ORDER BY
) should be treated as unsupported unless release notes state otherwise.LIMIT
Drasi Function Guidance (Select the Right Tool)
| Task type | Function | Notes / When to use |
|---|---|---|
| Detect state transition | | Ignores repeated identical values. |
| Compare last-known values | | Detect any change between revisions. |
| Evaluate sustained truth | | Requires continuous "true" state for duration. |
| Evaluate timed condition | | Pending result until specified timestamp. |
| Retrieve version snapshot | | Requires temporal index. |
| Compute trend | | Aggregate slope detection (telemetry, rate rise). |
| Change timestamps | | Windowing and recency checks against element's last change time. Evidence: Drasi custom functions docs (link below). |
| Historical lookup (range) | | Requires temporal index. Evidence: Drasi custom functions docs (link below). |
Evidence: Drasi custom functions docs (https://github.com/drasi-project/docs/blob/main/docs/content/reference/query-language/drasi-custom-functions.md).
When to Use Drasi vs Brokers
- Use Drasi for continuous queries over changing graphs, temporal reasoning (e.g.,
, historical versions), and when you need query results that reflect graph state transitions.trueFor - Use Event Hubs/Kafka for high-throughput event streaming with replay and retention when you want to reprocess raw events or build multiple downstream projections.
- Use Service Bus/queues for command-style workflows, strict ordering per key, or guaranteed processing with dead-letter workflows.
- If you need change-graph outputs with declarative querying, Drasi is the right fit; if you need raw event replay and fanout without query semantics, prefer a broker.
Query Performance & Reliability Guardrails
- Avoid Cartesian joins (
) unless absolutely required; they scale poorly.MATCH (a), (b) - Filter early with
before aggregations to reduce per-diff work.WHERE - Prefer indexed properties in Source data where possible; query cost is driven by change volume + aggregation.
- Reactions are at-least-once: design consumers to be idempotent and safe on duplicates.
- Document a replay plan when queries drive external side effects (e.g., Service Bus/webhooks).
Supported Cypher Features (Safe to Use)
| Feature | Example |
|---|---|
, , | |
| |
| Aggregations | , , , , |
| |
| |
| |
| |
| Graph relationships | |
| Identifier escaping | Use backticks for special chars: |
Unsupported Features (Will Fail)
| Feature | Status | Workaround |
|---|---|---|
| ⚠️ Not in supported subset | Use pattern |
| ❌ Not in supported subset | Use for grouping |
| ❌ Not in supported subset | Sort client-side |
| ❌ Not in supported subset | Filter client-side |
Source: Drasi Cypher support docs (https://github.com/drasi-project/docs/blob/main/docs/content/reference/query-language/cypher.md)
Query Patterns
Aggregation Pattern (Correct)
MATCH (w:WishlistItem) WITH w.text AS item, count(*) AS frequency WHERE frequency > 0 RETURN item, frequency
Duplicate Detection Pattern
MATCH (w1:WishlistItem), (w2:WishlistItem) WHERE w1.toyName = w2.toyName AND w1.childId <> w2.childId RETURN w1.childId AS child1, w2.childId AS child2, w1.toyName AS duplicate
Time-Windowed Pattern
MATCH (w:WishlistItem) WHERE drasi.changeDateTime() > datetime() - duration('PT24H') WITH w.toyName AS toy, count(*) AS requests WHERE requests >= 5 RETURN toy, requests
Critical: Label Configuration
Queries must use the middleware label, NOT the event hub name:
# In Source definition middleware: - kind: map my-event-hub: insert: - label: WishlistItem # <-- Use THIS in queries
# Map event types to labels (event type != hub/topic name) middleware: - kind: map toy-events-hub: insert: - label: WishlistItem when: eventType: wishlist.item.created - label: ChildProfile when: eventType: child.profile.updated
# ✅ CORRECT MATCH (w:WishlistItem) # ❌ WRONG MATCH (w:`my-event-hub`)
Deployment Commands
Always use Drasi CLI, NOT kubectl. Deploy in this order and verify each step:
- Source
- Namespace (if used)
- ContinuousQuery
- Reaction
drasi apply -f queries.yaml -n drasi-system drasi describe query my-query -n drasi-system
GQL Support
New Feature: Drasi now supports Graph Query Language (GQL) in addition to Cypher!
kind: ContinuousQuery spec: queryLanguage: GQL # or "Cypher" (default) query: | MATCH (v:Vehicle) WHERE v.color = 'Red' RETURN v.color
GQL-Specific Features:
- Post-query filtering (like SQL HAVING)FILTER
- Create computed variablesLET
- Project and rename columnsYIELD
- Chain multiple query statementsNEXT
- Explicit aggregation groupingGROUP BY
Example with FILTER (post-aggregation):
MATCH (v:Vehicle) RETURN v.color AS color, count(v) AS vehicle_count GROUP BY color NEXT FILTER vehicle_count > 5 RETURN color, vehicle_count
Common Parser Errors:
- "Identifier not found in scope" - Check variable names match your MATCH clause
- Unexpected token errors - Ensure proper escaping with backticks for special characters
Security Best Practices
Input Validation
- Never concatenate user input directly into Cypher queries
- Validate and sanitize all external inputs before use in queries
- Use typed parameters where possible to prevent injection attacks
Principle of Least Privilege
- Grant Sources only the minimum permissions needed to read data
- Configure Reactions with minimal write access to target systems
- Use separate service accounts for different query contexts
Query Safety
- Avoid runtime construction of query strings; keep queries static and validate inputs before they reach Drasi.
- Prefer managed identities or workload identities for downstream reactions instead of embedding secrets.
Contract Evolution Guidance
- Additive schema changes: add new properties/labels without removing existing ones; update queries to tolerate missing fields.
- Renames: dual-write old and new property names/labels during a transition period, then remove after consumers migrate.
- Deprecations: annotate query comments with deprecation date and migration path.
YAML Structure Best Practices
Naming Conventions
- Use descriptive, lowercase names with hyphens:
wishlist-trending-items - Prefix with domain/feature:
inventory-low-stock-alert - Include purpose in the name:
order-fraud-detection
Complete ContinuousQuery Example
apiVersion: v1 kind: ContinuousQuery name: wishlist-trending-items # Descriptive, hyphenated name spec: # Specify query language explicitly queryLanguage: Cypher # Document the query purpose # Purpose: Detect trending wishlist items requested by 5+ children in 24h # Trigger: New WishlistItem events from event hub # Output: List of trending toy names with request counts sources: input: - wishlist-source # Reference to Source definition query: | MATCH (w:WishlistItem) WHERE drasi.changeDateTime() > datetime() - duration('PT24H') WITH w.toyName AS toy, count(*) AS requests WHERE requests >= 5 RETURN toy, requests
Complete Source Example
apiVersion: v1 kind: Source name: wishlist-source spec: kind: EventHub # or PostgreSQL, CosmosDB, etc. properties: connectionString: ${EVENTHUB_CONNECTION_STRING} # Use env vars for secrets consumerGroup: drasi-consumer middleware: - kind: map my-event-hub: insert: - label: WishlistItem # Label used in MATCH clauses properties: - name: toyName - name: childId - name: priority
Complete Reaction Example
apiVersion: v1 kind: Reaction name: trending-alert-reaction spec: kind: SignalR # or Webhook, StoredProc, etc. queries: - wishlist-trending-items # Reference to ContinuousQuery properties: connectionString: ${SIGNALR_CONNECTION_STRING} hubName: trending-alerts
Testing Guidance
Local Loop
to a test namespacedrasi apply- Inspect output with a debug Reaction and
drasi logs - Validate query lag and expected match counts
- Adjust and redeploy using versioned YAML
Local Testing
- Use representative sample data that covers edge cases
- Test with the Drasi debug reaction to inspect query output:
drasi apply -f debug-reaction.yaml -n drasi-system drasi logs reaction debug-reaction -n drasi-system -f
Validation Checklist
- Query returns expected results with sample data
- Aggregations produce correct counts/sums
- Time-windowed queries trigger at appropriate intervals
- Middleware labels match MATCH clause labels exactly
- No unsupported Cypher features used
- Duplicate detection queries include deterministic keys for idempotent reactions
- Time-window boundaries behave as expected at edge values
Common Test Scenarios
- Empty result set handling
- Single item vs. multiple items
- Boundary conditions for thresholds
- Time window edge cases
Replay & Idempotency
- Reactions are at-least-once: design outputs to be idempotent with deterministic keys (e.g.,
).${queryId}:${entityId}:${windowStart} - For external side effects, document a replay plan and how duplicates are deduplicated.
Replay checklist:
- Rebuild source with known input range
- Clear or version downstream projections
- Validate idempotency keys and dedupe logic
- Re-run query and compare counts before/after
Backpressure & Retry Bounds
- Tighten
filters early to reduce per-change workload and avoid backlog growth.WHERE - Bound retries in downstream reactions; send poison messages to DLQ after max attempts.
- Monitor backlog and tune reaction concurrency to match downstream capacity.
Troubleshooting
If query shows TerminalError:
- Check for unsupported Cypher features (see table above)
- Verify middleware label matches MATCH clause exactly
- Use
for error detailsdrasi describe query <name> -n drasi-system
Debug Steps:
# Check query status drasi list query -n drasi-system # Get detailed error information drasi describe query my-query -n drasi-system # View reaction logs for output inspection drasi logs reaction my-reaction -n drasi-system -f
Observability & Cost
- Propagate correlation IDs from sources through reactions (headers or message properties).
- Emit metrics: query lag, reaction throughput, DLQ counts, and duplicate rate.
- Maintain a runbook: query purpose, rollback steps, replay steps, and alert thresholds.
- Minimize payload size in reactions; project only required fields.
- Avoid high-cardinality aggregations unless explicitly needed.
References
MCP Server
- Use
MCP server with library ID:context7/drasi-project/docs
Official Documentation
- Drasi Documentation - Official docs and tutorials
- Drasi GitHub Repositories - Source code and examples
Known Issues
- Issue #308 -
500 errors on reapplydrasi apply - Issue #43 -
function implementation (in progress)collect()