cfsa-antigravity 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +324 -0
- package/package.json +34 -0
- package/template/.agent/instructions/commands.md +48 -0
- package/template/.agent/instructions/patterns.md +61 -0
- package/template/.agent/instructions/structure.md +29 -0
- package/template/.agent/instructions/tech-stack.md +43 -0
- package/template/.agent/instructions/workflow.md +41 -0
- package/template/.agent/kit-sync.md +15 -0
- package/template/.agent/rules/boundary-not-placeholder.md +146 -0
- package/template/.agent/rules/completion-checklist.md +48 -0
- package/template/.agent/rules/decision-classification.md +103 -0
- package/template/.agent/rules/extensibility.md +47 -0
- package/template/.agent/rules/question-vs-command.md +81 -0
- package/template/.agent/rules/security-first.md +43 -0
- package/template/.agent/rules/specificity-standards.md +54 -0
- package/template/.agent/rules/tdd-contract-first.md +57 -0
- package/template/.agent/rules/vertical-slices.md +42 -0
- package/template/.agent/skill-library/MANIFEST.md +480 -0
- package/template/.agent/skill-library/README.md +38 -0
- package/template/.agent/skill-library/meta/brand-guidelines/SKILL.md +73 -0
- package/template/.agent/skill-library/meta/claude-code/README.md +9 -0
- package/template/.agent/skill-library/meta/claude-code/agent-development/SKILL.md +415 -0
- package/template/.agent/skill-library/meta/claude-code/hook-development/SKILL.md +712 -0
- package/template/.agent/skill-library/meta/claude-code/plugin-structure/SKILL.md +476 -0
- package/template/.agent/skill-library/meta/git-advanced/SKILL.md +972 -0
- package/template/.agent/skill-library/meta/mcp-builder/SKILL.md +236 -0
- package/template/.agent/skill-library/meta/product-marketing-context/SKILL.md +241 -0
- package/template/.agent/skill-library/meta/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skill-library/meta/tmux-processes/SKILL.md +210 -0
- package/template/.agent/skill-library/meta/using-tmux-for-interactive-commands/SKILL.md +178 -0
- package/template/.agent/skill-library/stack/3d/threejs-pro/SKILL.md +300 -0
- package/template/.agent/skill-library/stack/ai/ai-sdk/SKILL.md +77 -0
- package/template/.agent/skill-library/stack/ai/langchain/SKILL.md +530 -0
- package/template/.agent/skill-library/stack/ai/ollama/SKILL.md +321 -0
- package/template/.agent/skill-library/stack/ai/openai-sdk/SKILL.md +549 -0
- package/template/.agent/skill-library/stack/analytics/google-analytics/SKILL.md +153 -0
- package/template/.agent/skill-library/stack/api/graphql/SKILL.md +1061 -0
- package/template/.agent/skill-library/stack/api/trpc/SKILL.md +576 -0
- package/template/.agent/skill-library/stack/auth/authjs/SKILL.md +569 -0
- package/template/.agent/skill-library/stack/auth/clerk/SKILL.md +590 -0
- package/template/.agent/skill-library/stack/auth/firebase-auth/SKILL.md +734 -0
- package/template/.agent/skill-library/stack/cms/payload-cms/SKILL.md +573 -0
- package/template/.agent/skill-library/stack/cms/shopify/SKILL.md +1193 -0
- package/template/.agent/skill-library/stack/cms/wordpress/SKILL.md +1104 -0
- package/template/.agent/skill-library/stack/css/sass-scss/SKILL.md +1121 -0
- package/template/.agent/skill-library/stack/css/tailwind-css-patterns/SKILL.md +863 -0
- package/template/.agent/skill-library/stack/css/tailwind-design-system/SKILL.md +490 -0
- package/template/.agent/skill-library/stack/css/vanilla-css/SKILL.md +1078 -0
- package/template/.agent/skill-library/stack/databases/clickhouse/SKILL.md +311 -0
- package/template/.agent/skill-library/stack/databases/influxdb/SKILL.md +280 -0
- package/template/.agent/skill-library/stack/databases/lancedb/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/databases/mongodb/SKILL.md +1169 -0
- package/template/.agent/skill-library/stack/databases/neo4j/SKILL.md +839 -0
- package/template/.agent/skill-library/stack/databases/pgvector/SKILL.md +241 -0
- package/template/.agent/skill-library/stack/databases/pinecone/SKILL.md +212 -0
- package/template/.agent/skill-library/stack/databases/postgresql/SKILL.md +658 -0
- package/template/.agent/skill-library/stack/databases/qdrant/SKILL.md +312 -0
- package/template/.agent/skill-library/stack/databases/redis/SKILL.md +1079 -0
- package/template/.agent/skill-library/stack/databases/spacetimedb/SKILL.md +532 -0
- package/template/.agent/skill-library/stack/databases/sqlite/SKILL.md +1132 -0
- package/template/.agent/skill-library/stack/databases/supabase/SKILL.md +640 -0
- package/template/.agent/skill-library/stack/databases/surrealdb-expert/SKILL.md +945 -0
- package/template/.agent/skill-library/stack/databases/timescaledb/SKILL.md +745 -0
- package/template/.agent/skill-library/stack/databases/weaviate/SKILL.md +218 -0
- package/template/.agent/skill-library/stack/devops/github-actions/SKILL.md +554 -0
- package/template/.agent/skill-library/stack/devops/kubernetes/SKILL.md +950 -0
- package/template/.agent/skill-library/stack/devops/nginx/SKILL.md +841 -0
- package/template/.agent/skill-library/stack/devops/terraform/SKILL.md +860 -0
- package/template/.agent/skill-library/stack/email/resend/SKILL.md +391 -0
- package/template/.agent/skill-library/stack/engines/godot/SKILL.md +488 -0
- package/template/.agent/skill-library/stack/extensions/chrome-extension/SKILL.md +375 -0
- package/template/.agent/skill-library/stack/extensions/vscode-extension/SKILL.md +453 -0
- package/template/.agent/skill-library/stack/frameworks/astro-framework/SKILL.md +162 -0
- package/template/.agent/skill-library/stack/frameworks/electron/SKILL.md +1286 -0
- package/template/.agent/skill-library/stack/frameworks/fastapi/SKILL.md +650 -0
- package/template/.agent/skill-library/stack/frameworks/hono/SKILL.md +90 -0
- package/template/.agent/skill-library/stack/frameworks/nestjs/SKILL.md +878 -0
- package/template/.agent/skill-library/stack/frameworks/nextjs/SKILL.md +635 -0
- package/template/.agent/skill-library/stack/frameworks/nuxt/SKILL.md +564 -0
- package/template/.agent/skill-library/stack/frameworks/sveltekit/SKILL.md +614 -0
- package/template/.agent/skill-library/stack/frameworks/tauri/SKILL.md +920 -0
- package/template/.agent/skill-library/stack/gamedev/godot/SKILL.md +1032 -0
- package/template/.agent/skill-library/stack/gamedev/unity/SKILL.md +1175 -0
- package/template/.agent/skill-library/stack/hosting/aws/SKILL.md +467 -0
- package/template/.agent/skill-library/stack/hosting/cloudflare/SKILL.md +201 -0
- package/template/.agent/skill-library/stack/hosting/docker-expert/SKILL.md +409 -0
- package/template/.agent/skill-library/stack/hosting/vercel/SKILL.md +484 -0
- package/template/.agent/skill-library/stack/languages/bash-scripting/SKILL.md +773 -0
- package/template/.agent/skill-library/stack/languages/c-cpp/SKILL.md +712 -0
- package/template/.agent/skill-library/stack/languages/gdscript/SKILL.md +789 -0
- package/template/.agent/skill-library/stack/languages/go/SKILL.md +664 -0
- package/template/.agent/skill-library/stack/languages/java/SKILL.md +778 -0
- package/template/.agent/skill-library/stack/languages/kotlin/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/languages/python/SKILL.md +678 -0
- package/template/.agent/skill-library/stack/languages/rust/SKILL.md +673 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/SKILL.md +141 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/advanced-generics.md +90 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/branded-types.md +57 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/builder-pattern.md +71 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/common-pitfalls.md +135 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/conditional-types.md +27 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/decorators.md +98 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/discriminated-unions.md +62 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/mapped-types.md +53 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/performance-best-practices.md +104 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/template-literal-types.md +49 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/testing-types.md +112 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-guards.md +70 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-inference.md +101 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/utility-types.md +98 -0
- package/template/.agent/skill-library/stack/languages/vanilla-javascript/SKILL.md +803 -0
- package/template/.agent/skill-library/stack/messaging/kafka/SKILL.md +235 -0
- package/template/.agent/skill-library/stack/mobile/expo-react-native/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/mobile/flutter/SKILL.md +316 -0
- package/template/.agent/skill-library/stack/mobile/react-native/SKILL.md +337 -0
- package/template/.agent/skill-library/stack/monitoring/posthog/SKILL.md +396 -0
- package/template/.agent/skill-library/stack/monitoring/sentry/SKILL.md +509 -0
- package/template/.agent/skill-library/stack/observability/datadog/SKILL.md +179 -0
- package/template/.agent/skill-library/stack/observability/distributed-tracing/SKILL.md +140 -0
- package/template/.agent/skill-library/stack/observability/logging-best-practices/SKILL.md +168 -0
- package/template/.agent/skill-library/stack/observability/opentelemetry/SKILL.md +164 -0
- package/template/.agent/skill-library/stack/observability/prometheus-grafana/SKILL.md +246 -0
- package/template/.agent/skill-library/stack/observability/python-observability/SKILL.md +158 -0
- package/template/.agent/skill-library/stack/orm/drizzle-orm/SKILL.md +613 -0
- package/template/.agent/skill-library/stack/orm/prisma/SKILL.md +744 -0
- package/template/.agent/skill-library/stack/payments/lemonsqueezy/SKILL.md +393 -0
- package/template/.agent/skill-library/stack/payments/stripe-integration/SKILL.md +457 -0
- package/template/.agent/skill-library/stack/queue/bullmq/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/queue/inngest/SKILL.md +438 -0
- package/template/.agent/skill-library/stack/realtime/socketio/SKILL.md +595 -0
- package/template/.agent/skill-library/stack/search/elasticsearch/SKILL.md +248 -0
- package/template/.agent/skill-library/stack/search/meilisearch/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/security/crypto-patterns/SKILL.md +437 -0
- package/template/.agent/skill-library/stack/security/csp-cors-headers/SKILL.md +588 -0
- package/template/.agent/skill-library/stack/security/dependency-auditing/SKILL.md +560 -0
- package/template/.agent/skill-library/stack/security/input-sanitization/SKILL.md +430 -0
- package/template/.agent/skill-library/stack/security/owasp-web-security/SKILL.md +421 -0
- package/template/.agent/skill-library/stack/state/tanstack-query/SKILL.md +637 -0
- package/template/.agent/skill-library/stack/state/zustand/SKILL.md +483 -0
- package/template/.agent/skill-library/stack/storage/aws-s3/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/testing/playwright/SKILL.md +641 -0
- package/template/.agent/skill-library/stack/testing/storybook/SKILL.md +923 -0
- package/template/.agent/skill-library/stack/testing/testing-library/SKILL.md +872 -0
- package/template/.agent/skill-library/stack/testing/vitest/SKILL.md +714 -0
- package/template/.agent/skill-library/stack/ui/react-best-practices/SKILL.md +877 -0
- package/template/.agent/skill-library/stack/ui/react-composition-patterns/SKILL.md +1107 -0
- package/template/.agent/skill-library/stack/ui/react-flow/SKILL.md +425 -0
- package/template/.agent/skill-library/stack/ui/shadcn-ui/SKILL.md +703 -0
- package/template/.agent/skill-library/surface/api/api-caching/SKILL.md +458 -0
- package/template/.agent/skill-library/surface/api/api-documentation-openapi/SKILL.md +697 -0
- package/template/.agent/skill-library/surface/api/api-error-handling/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/api-security-checklist/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/api-versioning/SKILL.md +420 -0
- package/template/.agent/skill-library/surface/api/email-best-practices/SKILL.md +59 -0
- package/template/.agent/skill-library/surface/api/rate-limiting-abuse-protection/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/webhook-design/SKILL.md +752 -0
- package/template/.agent/skill-library/surface/cli/cli-configuration-management/SKILL.md +445 -0
- package/template/.agent/skill-library/surface/cli/cli-error-diagnostics/SKILL.md +515 -0
- package/template/.agent/skill-library/surface/cli/cli-shell-integration/SKILL.md +479 -0
- package/template/.agent/skill-library/surface/cli/cli-ux-design/SKILL.md +477 -0
- package/template/.agent/skill-library/surface/desktop/desktop-app-distribution/SKILL.md +416 -0
- package/template/.agent/skill-library/surface/desktop/desktop-security-sandboxing/SKILL.md +407 -0
- package/template/.agent/skill-library/surface/desktop/desktop-ux-conventions/SKILL.md +361 -0
- package/template/.agent/skill-library/surface/desktop/native-os-integration/SKILL.md +563 -0
- package/template/.agent/skill-library/surface/extension/browser-extension-patterns/SKILL.md +482 -0
- package/template/.agent/skill-library/surface/extension/plugin-architecture-design/SKILL.md +632 -0
- package/template/.agent/skill-library/surface/extension/vscode-extension-development/SKILL.md +728 -0
- package/template/.agent/skill-library/surface/mobile/app-store-submission/SKILL.md +304 -0
- package/template/.agent/skill-library/surface/mobile/mobile-offline-sync/SKILL.md +443 -0
- package/template/.agent/skill-library/surface/mobile/mobile-responsive-patterns/SKILL.md +432 -0
- package/template/.agent/skill-library/surface/mobile/push-notifications/SKILL.md +495 -0
- package/template/.agent/skill-library/surface/web/accessibility-compliance/SKILL.md +827 -0
- package/template/.agent/skill-library/surface/web/ai-seo/SKILL.md +398 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/content-patterns.md +285 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/platform-ranking-factors.md +152 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/SKILL.md +309 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/event-library.md +260 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/ga4-implementation.md +300 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/gtm-implementation.md +390 -0
- package/template/.agent/skill-library/surface/web/authentication-ui-flows/SKILL.md +530 -0
- package/template/.agent/skill-library/surface/web/dark-mode-theming/SKILL.md +516 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/SKILL.md +105 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/charts.csv +26 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/colors.csv +97 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/landing.csv +31 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/styles.csv +59 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/typography.csv +58 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/ux-guidelines.csv +100 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/core.py +258 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/design_system.py +1067 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/search.py +106 -0
- package/template/.agent/skill-library/surface/web/form-handling-validation/SKILL.md +675 -0
- package/template/.agent/skill-library/surface/web/frontend-design/SKILL.md +1393 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/cppn-hero.tsx +299 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/wave-hero.tsx +875 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/SKILL.md +111 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/scripts/ux_audit.py +739 -0
- package/template/.agent/skill-library/surface/web/i18n-localization/SKILL.md +154 -0
- package/template/.agent/skill-library/surface/web/offline-first-pwa/SKILL.md +657 -0
- package/template/.agent/skill-library/surface/web/page-cro/SKILL.md +182 -0
- package/template/.agent/skill-library/surface/web/page-cro/references/experiments.md +248 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/SKILL.md +238 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/references/playbooks.md +308 -0
- package/template/.agent/skill-library/surface/web/schema-markup/SKILL.md +179 -0
- package/template/.agent/skill-library/surface/web/schema-markup/references/schema-examples.md +398 -0
- package/template/.agent/skill-library/surface/web/seo-audit/SKILL.md +394 -0
- package/template/.agent/skill-library/surface/web/seo-audit/references/ai-writing-detection.md +200 -0
- package/template/.agent/skill-library/surface/web/web-performance-optimization/SKILL.md +646 -0
- package/template/.agent/skill-library/surface/web/web-scraping/SKILL.md +58 -0
- package/template/.agent/skills/accessibility/SKILL.md +522 -0
- package/template/.agent/skills/accessibility/references/WCAG.md +162 -0
- package/template/.agent/skills/adversarial-review/SKILL.md +90 -0
- package/template/.agent/skills/antigravity-workflows/SKILL.md +81 -0
- package/template/.agent/skills/antigravity-workflows/resources/implementation-playbook.md +36 -0
- package/template/.agent/skills/api-design-principles/SKILL.md +37 -0
- package/template/.agent/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/template/.agent/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/template/.agent/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/template/.agent/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/template/.agent/skills/api-design-principles/resources/implementation-playbook.md +513 -0
- package/template/.agent/skills/api-versioning/SKILL.md +420 -0
- package/template/.agent/skills/architecture-mapping/SKILL.md +219 -0
- package/template/.agent/skills/bootstrap-agents/SKILL.md +259 -0
- package/template/.agent/skills/brainstorming/SKILL.md +236 -0
- package/template/.agent/skills/brand-guidelines/SKILL.md +44 -0
- package/template/.agent/skills/clean-code/SKILL.md +94 -0
- package/template/.agent/skills/code-review-pro/SKILL.md +152 -0
- package/template/.agent/skills/concise-planning/SKILL.md +68 -0
- package/template/.agent/skills/cross-layer-consistency/SKILL.md +117 -0
- package/template/.agent/skills/database-schema-design/SKILL.md +429 -0
- package/template/.agent/skills/deployment-procedures/SKILL.md +241 -0
- package/template/.agent/skills/design-anti-cliche/SKILL.md +159 -0
- package/template/.agent/skills/design-direction/SKILL.md +45 -0
- package/template/.agent/skills/error-handling-patterns/SKILL.md +721 -0
- package/template/.agent/skills/find-skills/SKILL.md +145 -0
- package/template/.agent/skills/git-advanced/SKILL.md +972 -0
- package/template/.agent/skills/git-workflow/SKILL.md +420 -0
- package/template/.agent/skills/idea-extraction/SKILL.md +271 -0
- package/template/.agent/skills/logging-best-practices/SKILL.md +851 -0
- package/template/.agent/skills/migration-management/SKILL.md +384 -0
- package/template/.agent/skills/minimalist-surgical-development/SKILL.md +69 -0
- package/template/.agent/skills/parallel-agents/SKILL.md +165 -0
- package/template/.agent/skills/parallel-debugging/SKILL.md +135 -0
- package/template/.agent/skills/parallel-feature-development/SKILL.md +166 -0
- package/template/.agent/skills/performance-budgeting/SKILL.md +144 -0
- package/template/.agent/skills/pipeline-rubrics/SKILL.md +51 -0
- package/template/.agent/skills/pipeline-rubrics/references/architecture-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +21 -0
- package/template/.agent/skills/pipeline-rubrics/references/fe-rubric.md +20 -0
- package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/scoring.md +28 -0
- package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +11 -0
- package/template/.agent/skills/prd-templates/SKILL.md +88 -0
- package/template/.agent/skills/prd-templates/references/architecture-design-template.md +88 -0
- package/template/.agent/skills/prd-templates/references/be-spec-template.md +101 -0
- package/template/.agent/skills/prd-templates/references/data-placement-template.md +74 -0
- package/template/.agent/skills/prd-templates/references/decomposition-templates.md +211 -0
- package/template/.agent/skills/prd-templates/references/design-system-decisions.md +198 -0
- package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +124 -0
- package/template/.agent/skills/prd-templates/references/fe-classification-procedures.md +47 -0
- package/template/.agent/skills/prd-templates/references/fe-spec-template.md +84 -0
- package/template/.agent/skills/prd-templates/references/infrastructure-report-template.md +71 -0
- package/template/.agent/skills/prd-templates/references/operational-templates.md +116 -0
- package/template/.agent/skills/prd-templates/references/placeholder-guard-template.md +21 -0
- package/template/.agent/skills/prd-templates/references/surface-model.md +61 -0
- package/template/.agent/skills/prd-templates/references/vision-template.md +66 -0
- package/template/.agent/skills/prompt-engineer/README.md +659 -0
- package/template/.agent/skills/prompt-engineer/SKILL.md +249 -0
- package/template/.agent/skills/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skills/resolve-ambiguity/SKILL.md +278 -0
- package/template/.agent/skills/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skills/security-scanning-security-hardening/SKILL.md +231 -0
- package/template/.agent/skills/session-continuity/SKILL.md +730 -0
- package/template/.agent/skills/session-continuity/protocols/01-session-resumption.md +38 -0
- package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +85 -0
- package/template/.agent/skills/session-continuity/protocols/03-progress-update.md +70 -0
- package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +60 -0
- package/template/.agent/skills/session-continuity/protocols/05-session-close.md +37 -0
- package/template/.agent/skills/session-continuity/protocols/06-decision-analysis.md +84 -0
- package/template/.agent/skills/session-continuity/protocols/07-spec-pipeline-generation.md +48 -0
- package/template/.agent/skills/session-continuity/protocols/08-spec-pipeline-update.md +43 -0
- package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +122 -0
- package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +104 -0
- package/template/.agent/skills/session-continuity/protocols/ambiguity-gates.md +48 -0
- package/template/.agent/skills/skill-creator/LICENSE.txt +202 -0
- package/template/.agent/skills/skill-creator/README.md +270 -0
- package/template/.agent/skills/skill-creator/SKILL.md +590 -0
- package/template/.agent/skills/skill-creator/references/output-patterns.md +82 -0
- package/template/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/template/.agent/skills/skill-creator/scripts/init_skill.py +303 -0
- package/template/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/template/.agent/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/template/.agent/skills/spec-writing/SKILL.md +110 -0
- package/template/.agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/template/.agent/skills/systematic-debugging/SKILL.md +297 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/template/.agent/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/template/.agent/skills/systematic-debugging/find-polluter.sh +63 -0
- package/template/.agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/template/.agent/skills/systematic-debugging/test-academic.md +14 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/template/.agent/skills/tdd-workflow/SKILL.md +409 -0
- package/template/.agent/skills/tech-stack-catalog/SKILL.md +49 -0
- package/template/.agent/skills/tech-stack-catalog/references/constraint-questions.md +21 -0
- package/template/.agent/skills/tech-stack-catalog/references/dev-tooling-decisions.md +37 -0
- package/template/.agent/skills/tech-stack-catalog/references/surface-decision-tables.md +69 -0
- package/template/.agent/skills/technical-writer/SKILL.md +242 -0
- package/template/.agent/skills/testing-strategist/SKILL.md +932 -0
- package/template/.agent/skills/verification-before-completion/SKILL.md +145 -0
- package/template/.agent/skills/workflow-automation/SKILL.md +73 -0
- package/template/.agent/workflows/audit-ambiguity-execute.md +165 -0
- package/template/.agent/workflows/audit-ambiguity-rubrics.md +83 -0
- package/template/.agent/workflows/audit-ambiguity.md +64 -0
- package/template/.agent/workflows/bootstrap-agents-fill.md +201 -0
- package/template/.agent/workflows/bootstrap-agents-provision.md +197 -0
- package/template/.agent/workflows/bootstrap-agents.md +66 -0
- package/template/.agent/workflows/create-prd-architecture.md +119 -0
- package/template/.agent/workflows/create-prd-compile.md +138 -0
- package/template/.agent/workflows/create-prd-design-system.md +135 -0
- package/template/.agent/workflows/create-prd-security.md +113 -0
- package/template/.agent/workflows/create-prd-stack.md +91 -0
- package/template/.agent/workflows/create-prd.md +168 -0
- package/template/.agent/workflows/decompose-architecture-structure.md +82 -0
- package/template/.agent/workflows/decompose-architecture-validate.md +119 -0
- package/template/.agent/workflows/decompose-architecture.md +111 -0
- package/template/.agent/workflows/evolve-contract.md +98 -0
- package/template/.agent/workflows/evolve-feature-cascade.md +140 -0
- package/template/.agent/workflows/evolve-feature-classify.md +116 -0
- package/template/.agent/workflows/evolve-feature.md +56 -0
- package/template/.agent/workflows/ideate-discover.md +144 -0
- package/template/.agent/workflows/ideate-extract.md +129 -0
- package/template/.agent/workflows/ideate-validate.md +117 -0
- package/template/.agent/workflows/ideate.md +113 -0
- package/template/.agent/workflows/implement-slice-setup.md +113 -0
- package/template/.agent/workflows/implement-slice-tdd.md +198 -0
- package/template/.agent/workflows/implement-slice.md +50 -0
- package/template/.agent/workflows/plan-phase.md +202 -0
- package/template/.agent/workflows/propagate-decision-apply.md +135 -0
- package/template/.agent/workflows/propagate-decision-scan.md +147 -0
- package/template/.agent/workflows/propagate-decision.md +56 -0
- package/template/.agent/workflows/remediate-pipeline-assess.md +138 -0
- package/template/.agent/workflows/remediate-pipeline-execute.md +135 -0
- package/template/.agent/workflows/remediate-pipeline.md +55 -0
- package/template/.agent/workflows/resolve-ambiguity.md +82 -0
- package/template/.agent/workflows/sync-kit.md +209 -0
- package/template/.agent/workflows/update-architecture-map.md +74 -0
- package/template/.agent/workflows/validate-phase.md +219 -0
- package/template/.agent/workflows/verify-infrastructure.md +207 -0
- package/template/.agent/workflows/write-architecture-spec-deepen.md +139 -0
- package/template/.agent/workflows/write-architecture-spec-design.md +202 -0
- package/template/.agent/workflows/write-architecture-spec.md +63 -0
- package/template/.agent/workflows/write-be-spec-classify.md +165 -0
- package/template/.agent/workflows/write-be-spec-write.md +98 -0
- package/template/.agent/workflows/write-be-spec.md +76 -0
- package/template/.agent/workflows/write-fe-spec-classify.md +170 -0
- package/template/.agent/workflows/write-fe-spec-write.md +94 -0
- package/template/.agent/workflows/write-fe-spec.md +71 -0
- package/template/AGENTS.md +176 -0
- package/template/GEMINI.md +177 -0
- package/template/docs/README.md +187 -0
- package/template/docs/audits/.gitkeep +0 -0
- package/template/docs/audits/README.md +10 -0
- package/template/docs/plans/.gitkeep +0 -0
- package/template/docs/plans/README.md +21 -0
- package/template/docs/plans/be/.gitkeep +0 -0
- package/template/docs/plans/be/README.md +11 -0
- package/template/docs/plans/fe/.gitkeep +0 -0
- package/template/docs/plans/fe/README.md +11 -0
- package/template/docs/plans/ia/.gitkeep +0 -0
- package/template/docs/plans/ia/README.md +17 -0
- package/template/docs/plans/ia/deep-dives/.gitkeep +0 -0
- package/template/docs/plans/ia/deep-dives/README.md +5 -0
- package/template/docs/plans/phases/.gitkeep +0 -0
- package/template/docs/plans/phases/README.md +11 -0
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: neo4j
|
|
3
|
+
description: "Expert Neo4j guide covering graph data modeling, Cypher query patterns, index strategy, driver setup, cross-store coordination with primary stores, and security hardening. Use when designing graph schemas, traversing relationships, or integrating Neo4j with a polyglot persistence architecture."
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Neo4j Expert Guide
|
|
8
|
+
|
|
9
|
+
> Use this skill when designing graph schemas, writing Cypher queries, planning index strategies, setting up drivers, coordinating Neo4j with a primary relational/document store, or hardening a Neo4j deployment. This skill assumes Neo4j 5.x unless noted otherwise.
|
|
10
|
+
|
|
11
|
+
## When to Use This Skill
|
|
12
|
+
|
|
13
|
+
- Designing a graph data model for relationship-heavy domains (social networks, recommendation engines, access control hierarchies, knowledge graphs, fraud detection)
|
|
14
|
+
- Writing or reviewing Cypher queries
|
|
15
|
+
- Planning indexes and constraints for query performance
|
|
16
|
+
- Setting up Neo4j driver connections in any language
|
|
17
|
+
- Integrating Neo4j as a secondary store alongside a primary relational/document database
|
|
18
|
+
- Hardening Neo4j for production deployment
|
|
19
|
+
|
|
20
|
+
## When NOT to Use This Skill
|
|
21
|
+
|
|
22
|
+
- Flat tabular data with no meaningful relationships → use the primary relational store
|
|
23
|
+
- Simple key-value lookups → use Redis or the primary store
|
|
24
|
+
- Full-text search as the primary use case → use a dedicated search engine (Meilisearch, Elasticsearch)
|
|
25
|
+
- Time-series data → use TimescaleDB or InfluxDB
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Graph Data Modeling
|
|
30
|
+
|
|
31
|
+
### Nodes and Labels
|
|
32
|
+
|
|
33
|
+
Nodes represent entities. Every node has one or more **labels** that classify it.
|
|
34
|
+
|
|
35
|
+
**Label strategy — single responsibility per label:**
|
|
36
|
+
|
|
37
|
+
```cypher
|
|
38
|
+
// GOOD: Each label represents one concept
|
|
39
|
+
(:User)
|
|
40
|
+
(:Content)
|
|
41
|
+
(:Organization)
|
|
42
|
+
|
|
43
|
+
// BAD: Over-labelling with redundant or overlapping labels
|
|
44
|
+
(:User:Person:Account:Entity)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Labels should be:
|
|
48
|
+
- **Singular nouns** — `:User` not `:Users`
|
|
49
|
+
- **PascalCase** — `:ContentItem` not `:content_item`
|
|
50
|
+
- **Non-overlapping** — a node should not have two labels that mean the same thing
|
|
51
|
+
|
|
52
|
+
Use multiple labels only when they represent genuinely independent classification axes:
|
|
53
|
+
|
|
54
|
+
```cypher
|
|
55
|
+
// GOOD: Independent axes
|
|
56
|
+
(:User:Admin) -- Admin is a role, User is the entity type
|
|
57
|
+
(:Content:Published) -- Published is a state, Content is the entity type
|
|
58
|
+
|
|
59
|
+
// BAD: Redundant
|
|
60
|
+
(:User:Person) -- Person adds no information beyond User
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Relationship Types
|
|
64
|
+
|
|
65
|
+
Relationships connect nodes and always have a **type** and a **direction**.
|
|
66
|
+
|
|
67
|
+
**Naming convention — uppercase verbs:**
|
|
68
|
+
|
|
69
|
+
```cypher
|
|
70
|
+
// GOOD: Verb-based, uppercase, descriptive
|
|
71
|
+
(user)-[:FOLLOWS]->(otherUser)
|
|
72
|
+
(user)-[:AUTHORED]->(article)
|
|
73
|
+
(article)-[:BELONGS_TO]->(category)
|
|
74
|
+
(user)-[:VIEWED {at: datetime()}]->(content)
|
|
75
|
+
|
|
76
|
+
// BAD: Noun-based, lowercase, or vague
|
|
77
|
+
(user)-[:friend]->(otherUser)
|
|
78
|
+
(user)-[:article_relationship]->(article)
|
|
79
|
+
(user)-[:RELATED_TO]->(content) -- too vague
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Directionality:**
|
|
83
|
+
|
|
84
|
+
Relationships are always stored with a direction, but **traversal is bidirectional** — Cypher can traverse against the arrow direction. Direction matters when:
|
|
85
|
+
|
|
86
|
+
- The relationship is inherently asymmetric: `(a)-[:FOLLOWS]->(b)` (a follows b, but b may not follow a)
|
|
87
|
+
- You need to distinguish roles: `(employee)-[:WORKS_AT]->(company)` vs `(company)-[:EMPLOYS]->(employee)`
|
|
88
|
+
|
|
89
|
+
Direction does NOT matter when:
|
|
90
|
+
|
|
91
|
+
- The relationship is symmetric: `(a)-[:FRIENDS_WITH]->(b)` — direction is arbitrary, queries should omit the arrow: `(a)-[:FRIENDS_WITH]-(b)`
|
|
92
|
+
|
|
93
|
+
### Properties on Nodes vs Relationships
|
|
94
|
+
|
|
95
|
+
**Node properties** — attributes of the entity itself:
|
|
96
|
+
|
|
97
|
+
```cypher
|
|
98
|
+
CREATE (u:User {
|
|
99
|
+
userId: "uuid-from-postgres", // canonical ID from primary store
|
|
100
|
+
username: "janedoe",
|
|
101
|
+
displayName: "Jane Doe",
|
|
102
|
+
createdAt: datetime()
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Relationship properties** — attributes of the connection, not the entities:
|
|
107
|
+
|
|
108
|
+
```cypher
|
|
109
|
+
CREATE (u)-[:FOLLOWS {
|
|
110
|
+
since: datetime(),
|
|
111
|
+
notificationsEnabled: true
|
|
112
|
+
}]->(other)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Rule of thumb:** If the property describes *when*, *how*, or *why* two entities are connected, it belongs on the relationship. If it describes the entity itself, it belongs on the node.
|
|
116
|
+
|
|
117
|
+
### Cardinality Notation
|
|
118
|
+
|
|
119
|
+
Document cardinality in your schema design:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
(:User)-[:FOLLOWS]->(:User) 1:N (a user can follow many users)
|
|
123
|
+
(:User)-[:AUTHORED]->(:Article) 1:N (a user authors many articles)
|
|
124
|
+
(:Article)-[:BELONGS_TO]->(:Category) N:1 (many articles in one category)
|
|
125
|
+
(:User)-[:RATED {score: int}]->(:Content) N:M (many users rate many contents)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### When NOT to Use a Graph Store
|
|
129
|
+
|
|
130
|
+
Do not model the following in Neo4j:
|
|
131
|
+
|
|
132
|
+
| Data Type | Better Store | Why |
|
|
133
|
+
|-----------|-------------|-----|
|
|
134
|
+
| Flat tabular data (users table, settings) | PostgreSQL | No relationship traversal needed; relational queries are faster |
|
|
135
|
+
| Simple key-value lookups | Redis | Sub-millisecond reads without graph overhead |
|
|
136
|
+
| Time-series metrics | TimescaleDB | Chunk-based indexing outperforms graph for time-range queries |
|
|
137
|
+
| Binary blobs, images, files | S3/object storage | Neo4j is not a file store |
|
|
138
|
+
| Full-text search corpus | Meilisearch/Elasticsearch | Purpose-built for full-text ranking |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 2. Cypher Query Patterns
|
|
143
|
+
|
|
144
|
+
### MATCH — Reading Data
|
|
145
|
+
|
|
146
|
+
```cypher
|
|
147
|
+
// Simple node match
|
|
148
|
+
MATCH (u:User {userId: $userId})
|
|
149
|
+
RETURN u.displayName, u.username
|
|
150
|
+
|
|
151
|
+
// Match with relationship
|
|
152
|
+
MATCH (u:User {userId: $userId})-[:AUTHORED]->(a:Article)
|
|
153
|
+
RETURN a.title, a.publishedAt
|
|
154
|
+
ORDER BY a.publishedAt DESC
|
|
155
|
+
LIMIT 10
|
|
156
|
+
|
|
157
|
+
// Match multiple relationship types
|
|
158
|
+
MATCH (u:User {userId: $userId})-[:FOLLOWS|BLOCKS]->(other:User)
|
|
159
|
+
RETURN other.username, type(r) AS relationshipType
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### OPTIONAL MATCH — Left Join Equivalent
|
|
163
|
+
|
|
164
|
+
```cypher
|
|
165
|
+
// Return user even if they have no articles
|
|
166
|
+
MATCH (u:User {userId: $userId})
|
|
167
|
+
OPTIONAL MATCH (u)-[:AUTHORED]->(a:Article)
|
|
168
|
+
RETURN u.displayName, collect(a.title) AS articles
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### CREATE — Writing Data
|
|
172
|
+
|
|
173
|
+
```cypher
|
|
174
|
+
// Create a node
|
|
175
|
+
CREATE (u:User {
|
|
176
|
+
userId: $userId,
|
|
177
|
+
username: $username,
|
|
178
|
+
createdAt: datetime()
|
|
179
|
+
})
|
|
180
|
+
RETURN u
|
|
181
|
+
|
|
182
|
+
// Create a relationship
|
|
183
|
+
MATCH (u:User {userId: $userId})
|
|
184
|
+
MATCH (other:User {userId: $targetUserId})
|
|
185
|
+
CREATE (u)-[:FOLLOWS {since: datetime()}]->(other)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### MERGE — Idempotent Upsert
|
|
189
|
+
|
|
190
|
+
```cypher
|
|
191
|
+
// Create if not exists, match if exists
|
|
192
|
+
MERGE (u:User {userId: $userId})
|
|
193
|
+
ON CREATE SET u.username = $username, u.createdAt = datetime()
|
|
194
|
+
ON MATCH SET u.lastSeen = datetime()
|
|
195
|
+
RETURN u
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### WITH — Pipeline Results Between Clauses
|
|
199
|
+
|
|
200
|
+
```cypher
|
|
201
|
+
// Find users who follow the same people as a given user
|
|
202
|
+
MATCH (me:User {userId: $userId})-[:FOLLOWS]->(mutual)
|
|
203
|
+
WITH me, collect(mutual) AS myFollows
|
|
204
|
+
MATCH (other:User)-[:FOLLOWS]->(mutual)
|
|
205
|
+
WHERE mutual IN myFollows AND other <> me
|
|
206
|
+
RETURN other.username, count(mutual) AS sharedFollows
|
|
207
|
+
ORDER BY sharedFollows DESC
|
|
208
|
+
LIMIT 10
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### UNWIND — List Expansion
|
|
212
|
+
|
|
213
|
+
```cypher
|
|
214
|
+
// Batch create relationships from a list
|
|
215
|
+
UNWIND $userIds AS targetId
|
|
216
|
+
MATCH (u:User {userId: $userId})
|
|
217
|
+
MATCH (target:User {userId: targetId})
|
|
218
|
+
MERGE (u)-[:FOLLOWS {since: datetime()}]->(target)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Aggregation
|
|
222
|
+
|
|
223
|
+
```cypher
|
|
224
|
+
// Count, collect, sum, avg
|
|
225
|
+
MATCH (u:User)-[:AUTHORED]->(a:Article)
|
|
226
|
+
RETURN u.username,
|
|
227
|
+
count(a) AS articleCount,
|
|
228
|
+
collect(a.title) AS titles,
|
|
229
|
+
avg(a.wordCount) AS avgWords
|
|
230
|
+
ORDER BY articleCount DESC
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Path Patterns
|
|
234
|
+
|
|
235
|
+
```cypher
|
|
236
|
+
// Shortest path
|
|
237
|
+
MATCH p = shortestPath(
|
|
238
|
+
(a:User {userId: $startId})-[:FOLLOWS*]-(b:User {userId: $endId})
|
|
239
|
+
)
|
|
240
|
+
RETURN p, length(p) AS hops
|
|
241
|
+
|
|
242
|
+
// Variable-length paths (bounded)
|
|
243
|
+
MATCH (u:User {userId: $userId})-[:FOLLOWS*1..3]->(distant:User)
|
|
244
|
+
RETURN DISTINCT distant.username, min(length(p)) AS minHops
|
|
245
|
+
|
|
246
|
+
// All shortest paths
|
|
247
|
+
MATCH p = allShortestPaths(
|
|
248
|
+
(a:User {userId: $startId})-[:FOLLOWS*]-(b:User {userId: $endId})
|
|
249
|
+
)
|
|
250
|
+
RETURN p
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
> ⚠️ **Always bound variable-length paths** — `[:FOLLOWS*]` without bounds can traverse the entire graph. Use `[:FOLLOWS*1..N]` with a sensible upper bound.
|
|
254
|
+
|
|
255
|
+
### Pattern Comprehensions
|
|
256
|
+
|
|
257
|
+
```cypher
|
|
258
|
+
// Inline sub-query as a list
|
|
259
|
+
MATCH (u:User {userId: $userId})
|
|
260
|
+
RETURN u.username,
|
|
261
|
+
[(u)-[:FOLLOWS]->(f:User) | f.username] AS following,
|
|
262
|
+
[(u)<-[:FOLLOWS]-(f:User) | f.username] AS followers
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### CALL {} Subqueries
|
|
266
|
+
|
|
267
|
+
```cypher
|
|
268
|
+
// Subquery for complex aggregation
|
|
269
|
+
MATCH (u:User)
|
|
270
|
+
CALL {
|
|
271
|
+
WITH u
|
|
272
|
+
MATCH (u)-[:AUTHORED]->(a:Article)
|
|
273
|
+
RETURN count(a) AS articleCount
|
|
274
|
+
}
|
|
275
|
+
RETURN u.username, articleCount
|
|
276
|
+
ORDER BY articleCount DESC
|
|
277
|
+
LIMIT 10
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Multi-Step Example
|
|
281
|
+
|
|
282
|
+
Find all users within 2 hops of a given user who also follow the same content:
|
|
283
|
+
|
|
284
|
+
```cypher
|
|
285
|
+
// Step 1: Find users within 2 hops
|
|
286
|
+
MATCH (me:User {userId: $userId})-[:FOLLOWS*1..2]->(nearby:User)
|
|
287
|
+
WHERE nearby <> me
|
|
288
|
+
WITH me, collect(DISTINCT nearby) AS nearbyUsers
|
|
289
|
+
|
|
290
|
+
// Step 2: Find content I follow
|
|
291
|
+
MATCH (me)-[:FOLLOWS]->(c:Content)
|
|
292
|
+
WITH nearbyUsers, collect(c) AS myContent
|
|
293
|
+
|
|
294
|
+
// Step 3: Filter nearby users who follow the same content
|
|
295
|
+
UNWIND nearbyUsers AS candidate
|
|
296
|
+
MATCH (candidate)-[:FOLLOWS]->(c:Content)
|
|
297
|
+
WHERE c IN myContent
|
|
298
|
+
RETURN candidate.username,
|
|
299
|
+
count(c) AS sharedContentCount,
|
|
300
|
+
collect(c.title) AS sharedContent
|
|
301
|
+
ORDER BY sharedContentCount DESC
|
|
302
|
+
LIMIT 20
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## 3. Index Strategy
|
|
308
|
+
|
|
309
|
+
### Node Property Indexes
|
|
310
|
+
|
|
311
|
+
```cypher
|
|
312
|
+
// Range index (default) — for equality, range, prefix, existence
|
|
313
|
+
CREATE INDEX user_userId FOR (u:User) ON (u.userId)
|
|
314
|
+
|
|
315
|
+
// Text index — for full-text-like CONTAINS and ENDS WITH queries
|
|
316
|
+
CREATE TEXT INDEX user_username_text FOR (u:User) ON (u.username)
|
|
317
|
+
|
|
318
|
+
// Point index — for geospatial queries
|
|
319
|
+
CREATE POINT INDEX location_coords FOR (l:Location) ON (l.coordinates)
|
|
320
|
+
|
|
321
|
+
// Composite index — for multi-property lookups
|
|
322
|
+
CREATE INDEX user_org_role FOR (u:User) ON (u.organizationId, u.role)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Relationship Property Indexes
|
|
326
|
+
|
|
327
|
+
```cypher
|
|
328
|
+
// Index on relationship properties (Neo4j 5.x)
|
|
329
|
+
CREATE INDEX follows_since FOR ()-[r:FOLLOWS]-() ON (r.since)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Full-Text Indexes
|
|
333
|
+
|
|
334
|
+
```cypher
|
|
335
|
+
// Create a full-text index across multiple node properties
|
|
336
|
+
CREATE FULLTEXT INDEX content_search FOR (c:Content)
|
|
337
|
+
ON EACH [c.title, c.description, c.body]
|
|
338
|
+
|
|
339
|
+
// Query the full-text index
|
|
340
|
+
CALL db.index.fulltext.queryNodes("content_search", "graph database tutorial")
|
|
341
|
+
YIELD node, score
|
|
342
|
+
RETURN node.title, score
|
|
343
|
+
ORDER BY score DESC
|
|
344
|
+
LIMIT 10
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Constraints
|
|
348
|
+
|
|
349
|
+
```cypher
|
|
350
|
+
// Uniqueness constraint (also creates an index)
|
|
351
|
+
CREATE CONSTRAINT user_userId_unique FOR (u:User)
|
|
352
|
+
REQUIRE u.userId IS UNIQUE
|
|
353
|
+
|
|
354
|
+
// Existence constraint (property must be present)
|
|
355
|
+
CREATE CONSTRAINT user_userId_exists FOR (u:User)
|
|
356
|
+
REQUIRE u.userId IS NOT NULL
|
|
357
|
+
|
|
358
|
+
// Node key (composite uniqueness + existence)
|
|
359
|
+
CREATE CONSTRAINT org_user_key FOR (m:Membership)
|
|
360
|
+
REQUIRE (m.organizationId, m.userId) IS NODE KEY
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Verifying Index Usage
|
|
364
|
+
|
|
365
|
+
```cypher
|
|
366
|
+
// EXPLAIN — shows the query plan without executing
|
|
367
|
+
EXPLAIN MATCH (u:User {userId: $userId}) RETURN u
|
|
368
|
+
|
|
369
|
+
// PROFILE — executes the query and shows actual row counts per step
|
|
370
|
+
PROFILE MATCH (u:User {userId: $userId}) RETURN u
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Look for `NodeIndexSeek` or `RelationshipIndexSeek` in the plan. If you see `NodeByLabelScan` or `AllNodesScan` on a frequently queried property, you need an index.
|
|
374
|
+
|
|
375
|
+
### When to Index
|
|
376
|
+
|
|
377
|
+
| Scenario | Index Type |
|
|
378
|
+
|----------|-----------|
|
|
379
|
+
| Lookup by canonical ID (`userId`) | Range index + uniqueness constraint |
|
|
380
|
+
| Prefix search on usernames | Text index |
|
|
381
|
+
| Date range queries on relationships | Relationship property index |
|
|
382
|
+
| Free-text search across properties | Full-text index |
|
|
383
|
+
| Geospatial queries (nearby locations) | Point index |
|
|
384
|
+
| Multi-property lookups (org + role) | Composite index |
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 4. Driver Setup
|
|
389
|
+
|
|
390
|
+
### Connection Pattern (Language-Agnostic)
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
URI: bolt://localhost:7687 (unencrypted, dev only)
|
|
394
|
+
neo4j://host:7687 (routed, production)
|
|
395
|
+
neo4j+s://host:7687 (routed + TLS, production)
|
|
396
|
+
Auth: neo4j / <password>
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### JavaScript/TypeScript (neo4j-driver)
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import neo4j, { Driver, Session } from 'neo4j-driver';
|
|
403
|
+
|
|
404
|
+
// Create driver (application lifecycle — create once, close on shutdown)
|
|
405
|
+
const driver: Driver = neo4j.driver(
|
|
406
|
+
process.env.NEO4J_URI!,
|
|
407
|
+
neo4j.auth.basic(process.env.NEO4J_USER!, process.env.NEO4J_PASSWORD!),
|
|
408
|
+
{
|
|
409
|
+
maxConnectionPoolSize: 50,
|
|
410
|
+
connectionAcquisitionTimeout: 30000, // ms
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
// Verify connectivity on startup
|
|
415
|
+
await driver.verifyConnectivity();
|
|
416
|
+
|
|
417
|
+
// Transaction function (recommended — auto-retries on transient errors)
|
|
418
|
+
async function findFollowers(userId: string) {
|
|
419
|
+
const session: Session = driver.session({ database: 'neo4j' });
|
|
420
|
+
try {
|
|
421
|
+
const result = await session.executeRead(async (tx) => {
|
|
422
|
+
return tx.run(
|
|
423
|
+
'MATCH (u:User {userId: $userId})<-[:FOLLOWS]-(f:User) RETURN f.username AS username',
|
|
424
|
+
{ userId }
|
|
425
|
+
);
|
|
426
|
+
});
|
|
427
|
+
return result.records.map((r) => r.get('username'));
|
|
428
|
+
} finally {
|
|
429
|
+
await session.close(); // ALWAYS close sessions in finally
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Shutdown
|
|
434
|
+
process.on('SIGTERM', async () => {
|
|
435
|
+
await driver.close();
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Python (neo4j package)
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
from neo4j import GraphDatabase
|
|
443
|
+
|
|
444
|
+
driver = GraphDatabase.driver(
|
|
445
|
+
os.environ["NEO4J_URI"],
|
|
446
|
+
auth=(os.environ["NEO4J_USER"], os.environ["NEO4J_PASSWORD"]),
|
|
447
|
+
max_connection_pool_size=50,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
def find_followers(user_id: str) -> list[str]:
|
|
451
|
+
with driver.session(database="neo4j") as session:
|
|
452
|
+
result = session.execute_read(
|
|
453
|
+
lambda tx: tx.run(
|
|
454
|
+
"MATCH (u:User {userId: $userId})<-[:FOLLOWS]-(f:User) "
|
|
455
|
+
"RETURN f.username AS username",
|
|
456
|
+
userId=user_id,
|
|
457
|
+
).data()
|
|
458
|
+
)
|
|
459
|
+
return [r["username"] for r in result]
|
|
460
|
+
|
|
461
|
+
# Shutdown
|
|
462
|
+
driver.close()
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Go (neo4j-go-driver)
|
|
466
|
+
|
|
467
|
+
```go
|
|
468
|
+
package main
|
|
469
|
+
|
|
470
|
+
import (
|
|
471
|
+
"context"
|
|
472
|
+
"os"
|
|
473
|
+
|
|
474
|
+
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
func main() {
|
|
478
|
+
ctx := context.Background()
|
|
479
|
+
|
|
480
|
+
// Create driver (application lifecycle — create once, close on shutdown)
|
|
481
|
+
driver, err := neo4j.NewDriverWithContext(
|
|
482
|
+
os.Getenv("NEO4J_URI"),
|
|
483
|
+
neo4j.BasicAuth(os.Getenv("NEO4J_USER"), os.Getenv("NEO4J_PASSWORD"), ""),
|
|
484
|
+
)
|
|
485
|
+
if err != nil {
|
|
486
|
+
panic(err)
|
|
487
|
+
}
|
|
488
|
+
defer driver.Close(ctx)
|
|
489
|
+
|
|
490
|
+
// Verify connectivity
|
|
491
|
+
if err := driver.VerifyConnectivity(ctx); err != nil {
|
|
492
|
+
panic(err)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Transaction function (recommended — auto-retries on transient errors)
|
|
496
|
+
session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "neo4j"})
|
|
497
|
+
defer session.Close(ctx)
|
|
498
|
+
|
|
499
|
+
followers, err := neo4j.ExecuteRead(ctx, session, func(tx neo4j.ManagedTransaction) ([]string, error) {
|
|
500
|
+
result, err := tx.Run(ctx,
|
|
501
|
+
"MATCH (u:User {userId: $userId})<-[:FOLLOWS]-(f:User) RETURN f.username AS username",
|
|
502
|
+
map[string]any{"userId": "target-user-id"},
|
|
503
|
+
)
|
|
504
|
+
if err != nil {
|
|
505
|
+
return nil, err
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
var usernames []string
|
|
509
|
+
for result.Next(ctx) {
|
|
510
|
+
usernames = append(usernames, result.Record().Values[0].(string))
|
|
511
|
+
}
|
|
512
|
+
return usernames, result.Err()
|
|
513
|
+
})
|
|
514
|
+
if err != nil {
|
|
515
|
+
panic(err)
|
|
516
|
+
}
|
|
517
|
+
_ = followers
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Java (neo4j-java-driver)
|
|
522
|
+
|
|
523
|
+
```java
|
|
524
|
+
import org.neo4j.driver.*;
|
|
525
|
+
import org.neo4j.driver.summary.ResultSummary;
|
|
526
|
+
import java.util.List;
|
|
527
|
+
import java.util.Map;
|
|
528
|
+
|
|
529
|
+
public class Neo4jExample {
|
|
530
|
+
public static void main(String[] args) {
|
|
531
|
+
// Create driver (application lifecycle — create once, close on shutdown)
|
|
532
|
+
Driver driver = GraphDatabase.driver(
|
|
533
|
+
System.getenv("NEO4J_URI"),
|
|
534
|
+
AuthTokens.basic(System.getenv("NEO4J_USER"), System.getenv("NEO4J_PASSWORD")),
|
|
535
|
+
Config.builder()
|
|
536
|
+
.withMaxConnectionPoolSize(50)
|
|
537
|
+
.build()
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
// Verify connectivity
|
|
541
|
+
driver.verifyConnectivity();
|
|
542
|
+
|
|
543
|
+
// Transaction function (recommended — auto-retries on transient errors)
|
|
544
|
+
try (Session session = driver.session(SessionConfig.forDatabase("neo4j"))) {
|
|
545
|
+
List<String> followers = session.executeRead(tx -> {
|
|
546
|
+
Result result = tx.run(
|
|
547
|
+
"MATCH (u:User {userId: $userId})<-[:FOLLOWS]-(f:User) RETURN f.username AS username",
|
|
548
|
+
Map.of("userId", "target-user-id")
|
|
549
|
+
);
|
|
550
|
+
return result.list(r -> r.get("username").asString());
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Shutdown
|
|
555
|
+
driver.close();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Session Lifecycle Rules
|
|
561
|
+
|
|
562
|
+
1. **Create one driver per application** — the driver manages a connection pool internally
|
|
563
|
+
2. **Create a new session per unit of work** — sessions are lightweight
|
|
564
|
+
3. **Always close sessions in a finally block** — leaked sessions exhaust the pool
|
|
565
|
+
4. **Use transaction functions** (`executeRead`, `executeWrite`) — they auto-retry on transient errors (leader changes in a cluster)
|
|
566
|
+
5. **Never share sessions across threads/coroutines** — sessions are not thread-safe
|
|
567
|
+
|
|
568
|
+
### Connection Pool Configuration
|
|
569
|
+
|
|
570
|
+
| Setting | Default | Recommendation |
|
|
571
|
+
|---------|---------|---------------|
|
|
572
|
+
| `maxConnectionPoolSize` | 100 | Set to expected concurrent operations × 1.5 |
|
|
573
|
+
| `connectionAcquisitionTimeout` | 60s | Reduce to 30s to fail fast under load |
|
|
574
|
+
| `maxTransactionRetryTime` | 30s | Keep default unless you have very fast transactions |
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## 5. Cross-Store Coordination
|
|
579
|
+
|
|
580
|
+
### Canonical ID Rule
|
|
581
|
+
|
|
582
|
+
**The primary store's UUID is the canonical identifier.** Store it as a node property in Neo4j and create a uniqueness constraint:
|
|
583
|
+
|
|
584
|
+
```cypher
|
|
585
|
+
CREATE CONSTRAINT user_userId_unique FOR (u:User)
|
|
586
|
+
REQUIRE u.userId IS UNIQUE
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Never use Neo4j's internal identifiers** (`id()` or element IDs) as canonical identifiers:
|
|
590
|
+
- Internal IDs are recycled after node deletion
|
|
591
|
+
- Element IDs are not portable across database exports/restores
|
|
592
|
+
- They are implementation details, not business identifiers
|
|
593
|
+
|
|
594
|
+
```cypher
|
|
595
|
+
// GOOD: Use the primary store's UUID
|
|
596
|
+
MATCH (u:User {userId: "a1b2c3d4-e5f6-..."})
|
|
597
|
+
RETURN u
|
|
598
|
+
|
|
599
|
+
// BAD: Use Neo4j internal ID
|
|
600
|
+
MATCH (u:User) WHERE id(u) = 42
|
|
601
|
+
RETURN u
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Creation Sequence
|
|
605
|
+
|
|
606
|
+
1. **Write to the primary store first** (e.g., PostgreSQL) — this generates the canonical UUID
|
|
607
|
+
2. **Then create the Neo4j node** using the returned UUID
|
|
608
|
+
3. **If Neo4j write fails:**
|
|
609
|
+
- The primary store record exists but the graph node does not
|
|
610
|
+
- Queue a retry (async retry queue or background job)
|
|
611
|
+
- The application should handle the "graph node not found" case gracefully (return data from the primary store without graph enrichment)
|
|
612
|
+
- Never roll back the primary store write because of a Neo4j failure — the primary store is the source of truth
|
|
613
|
+
|
|
614
|
+
### Deletion Cascade
|
|
615
|
+
|
|
616
|
+
1. Delete from the primary store (or soft-delete)
|
|
617
|
+
2. Delete the Neo4j node and all its relationships:
|
|
618
|
+
|
|
619
|
+
```cypher
|
|
620
|
+
// Delete node and all connected relationships
|
|
621
|
+
MATCH (u:User {userId: $userId})
|
|
622
|
+
DETACH DELETE u
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
3. If the Neo4j delete fails, queue a cleanup job — orphaned graph nodes are annoying but not data-corrupting
|
|
626
|
+
|
|
627
|
+
### Read Strategy
|
|
628
|
+
|
|
629
|
+
When a read requires data from both stores:
|
|
630
|
+
|
|
631
|
+
```
|
|
632
|
+
1. Query primary store for entity attributes
|
|
633
|
+
2. Query Neo4j for relationship data (followers, connections, paths)
|
|
634
|
+
3. Merge results at the application layer
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Name this pattern explicitly in your architecture: **"Application-Layer Graph Enrichment"** — the primary store answers "what is this entity?" and Neo4j answers "how is this entity connected?"
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
## 6. Security
|
|
642
|
+
|
|
643
|
+
### Credential Handling
|
|
644
|
+
|
|
645
|
+
```bash
|
|
646
|
+
# GOOD: Environment variables
|
|
647
|
+
NEO4J_URI=neo4j+s://production-host:7687
|
|
648
|
+
NEO4J_USER=neo4j
|
|
649
|
+
NEO4J_PASSWORD=<from-secret-manager>
|
|
650
|
+
|
|
651
|
+
# BAD: Hardcoded in source
|
|
652
|
+
const driver = neo4j.driver("bolt://localhost:7687", neo4j.auth.basic("neo4j", "password123"));
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
### Query Injection Prevention
|
|
656
|
+
|
|
657
|
+
**Always use parameterized Cypher.** Never interpolate user input into query strings.
|
|
658
|
+
|
|
659
|
+
```cypher
|
|
660
|
+
// GOOD: Parameterized query
|
|
661
|
+
MATCH (u:User {userId: $userId}) RETURN u
|
|
662
|
+
|
|
663
|
+
// BAD: String interpolation — VULNERABLE TO INJECTION
|
|
664
|
+
MATCH (u:User {userId: '${userId}'}) RETURN u
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
In code:
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
// GOOD
|
|
671
|
+
await tx.run('MATCH (u:User {userId: $userId}) RETURN u', { userId });
|
|
672
|
+
|
|
673
|
+
// BAD — NEVER DO THIS
|
|
674
|
+
await tx.run(`MATCH (u:User {userId: '${userId}'}) RETURN u`);
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Network Exposure
|
|
678
|
+
|
|
679
|
+
- **Bolt port 7687 must not be public-facing** — keep behind a VPC, private network, or firewall
|
|
680
|
+
- In cloud deployments, use a VPN or private service endpoint
|
|
681
|
+
- In Docker, bind to internal network only: `--publish 127.0.0.1:7687:7687`
|
|
682
|
+
|
|
683
|
+
### TLS Configuration
|
|
684
|
+
|
|
685
|
+
```
|
|
686
|
+
# Production: Always use encrypted connections
|
|
687
|
+
URI: neo4j+s://host:7687 (TLS with certificate verification)
|
|
688
|
+
URI: neo4j+ssc://host:7687 (TLS with self-signed certificate — dev/staging only)
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### Role-Based Access Control
|
|
692
|
+
|
|
693
|
+
Neo4j native auth roles:
|
|
694
|
+
|
|
695
|
+
| Role | Permissions |
|
|
696
|
+
|------|------------|
|
|
697
|
+
| `reader` | Read-only access to all data |
|
|
698
|
+
| `editor` | Read + write to all data |
|
|
699
|
+
| `publisher` | Read + write + create/drop indexes |
|
|
700
|
+
| `architect` | Publisher + create/drop constraints |
|
|
701
|
+
| `admin` | Full control including user management |
|
|
702
|
+
|
|
703
|
+
```cypher
|
|
704
|
+
// Create a user with reader role
|
|
705
|
+
CREATE USER appReader SET PASSWORD $password SET STATUS ACTIVE
|
|
706
|
+
GRANT ROLE reader TO appReader
|
|
707
|
+
|
|
708
|
+
// Application connections should use the least-privilege role
|
|
709
|
+
// Read-only services → reader
|
|
710
|
+
// Write services → editor
|
|
711
|
+
// Migration scripts → architect
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## 7. Common Anti-Patterns
|
|
717
|
+
|
|
718
|
+
### 1. Using Neo4j for Non-Graph Data
|
|
719
|
+
|
|
720
|
+
**Problem:** Storing flat tabular data (user profiles, settings, configuration) as nodes with no meaningful relationships.
|
|
721
|
+
|
|
722
|
+
**Why it's bad:** Neo4j stores properties on the heap with node/relationship overhead. For flat data, a relational table is more space-efficient and faster for simple lookups.
|
|
723
|
+
|
|
724
|
+
**Fix:** Only store in Neo4j what requires relationship traversal. Keep flat entity data in the primary store. Use the cross-store coordination pattern to link them via canonical IDs.
|
|
725
|
+
|
|
726
|
+
### 2. Cartesian Products in Cypher
|
|
727
|
+
|
|
728
|
+
**Problem:** A `MATCH` with two unconnected node patterns produces a cross product.
|
|
729
|
+
|
|
730
|
+
```cypher
|
|
731
|
+
// BAD: Cartesian product — returns |users| × |articles| rows
|
|
732
|
+
MATCH (u:User), (a:Article)
|
|
733
|
+
RETURN u.username, a.title
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
**Fix:** Always specify the relationship pattern between matched nodes.
|
|
737
|
+
|
|
738
|
+
```cypher
|
|
739
|
+
// GOOD: Only returns user-article pairs connected by AUTHORED
|
|
740
|
+
MATCH (u:User)-[:AUTHORED]->(a:Article)
|
|
741
|
+
RETURN u.username, a.title
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
**Detection:** `PROFILE` the query — look for an `CartesianProduct` operator in the query plan.
|
|
745
|
+
|
|
746
|
+
### 3. Missing Indexes on Frequently Matched Properties
|
|
747
|
+
|
|
748
|
+
**Problem:** Querying by a property without an index triggers a `NodeByLabelScan` — scanning every node with that label.
|
|
749
|
+
|
|
750
|
+
```cypher
|
|
751
|
+
// If no index exists on User.email, this scans ALL User nodes
|
|
752
|
+
MATCH (u:User {email: $email}) RETURN u
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
**Detection:**
|
|
756
|
+
|
|
757
|
+
```cypher
|
|
758
|
+
PROFILE MATCH (u:User {email: $email}) RETURN u
|
|
759
|
+
// Look for NodeByLabelScan in the plan → needs an index
|
|
760
|
+
// Should show NodeIndexSeek after index creation
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
**Fix:** Create an index on any property used in `MATCH` or `WHERE` clauses:
|
|
764
|
+
|
|
765
|
+
```cypher
|
|
766
|
+
CREATE INDEX user_email FOR (u:User) ON (u.email)
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### 4. Storing Large Blobs as Node Properties
|
|
770
|
+
|
|
771
|
+
**Problem:** Storing images, PDFs, or large text bodies as node properties.
|
|
772
|
+
|
|
773
|
+
**Why it's bad:** Neo4j stores all properties on the heap. Large blobs increase memory pressure and slow down traversals because the page cache fills with blob data instead of graph structure.
|
|
774
|
+
|
|
775
|
+
**Fix:** Store blobs in S3/object storage. Store the reference (URL or key) as a node property:
|
|
776
|
+
|
|
777
|
+
```cypher
|
|
778
|
+
CREATE (d:Document {
|
|
779
|
+
documentId: $docId,
|
|
780
|
+
title: $title,
|
|
781
|
+
storageKey: "s3://bucket/documents/abc123.pdf", // reference, not the file
|
|
782
|
+
sizeBytes: 2048576
|
|
783
|
+
})
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### 5. Over-Normalising Graph Schemas
|
|
787
|
+
|
|
788
|
+
**Problem:** Breaking entities into too many tiny nodes connected by structural relationships, mimicking a relational normal form.
|
|
789
|
+
|
|
790
|
+
```cypher
|
|
791
|
+
// BAD: Over-normalised — Address as a separate node for a single-use relationship
|
|
792
|
+
(:User)-[:HAS_ADDRESS]->(:Address)-[:IN_CITY]->(:City)-[:IN_COUNTRY]->(:Country)
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
**Fix:** If the relationship is 1:1 and the related entity has no independent lifecycle, store it as properties on the parent node:
|
|
796
|
+
|
|
797
|
+
```cypher
|
|
798
|
+
// GOOD: Address as properties (if no independent queries on addresses)
|
|
799
|
+
(:User {city: "Berlin", country: "DE", postalCode: "10115"})
|
|
800
|
+
|
|
801
|
+
// GOOD AS A GRAPH: Only if addresses are shared or queried independently
|
|
802
|
+
(:User)-[:LIVES_IN]->(:City {name: "Berlin"})-[:IN_COUNTRY]->(:Country {code: "DE"})
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### 6. Unbounded Variable-Length Paths
|
|
806
|
+
|
|
807
|
+
**Problem:** Using `*` without bounds in variable-length path patterns.
|
|
808
|
+
|
|
809
|
+
```cypher
|
|
810
|
+
// BAD: Traverses the ENTIRE connected component — can be millions of nodes
|
|
811
|
+
MATCH p = (u:User {userId: $id})-[:FOLLOWS*]->(other)
|
|
812
|
+
RETURN other
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
**Fix:** Always set an upper bound:
|
|
816
|
+
|
|
817
|
+
```cypher
|
|
818
|
+
// GOOD: Bounded to 3 hops
|
|
819
|
+
MATCH p = (u:User {userId: $id})-[:FOLLOWS*1..3]->(other)
|
|
820
|
+
RETURN DISTINCT other.username
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
### 7. Not Using DETACH DELETE
|
|
824
|
+
|
|
825
|
+
**Problem:** Attempting to delete a node that still has relationships.
|
|
826
|
+
|
|
827
|
+
```cypher
|
|
828
|
+
// BAD: Fails if the node has any relationships
|
|
829
|
+
DELETE (u:User {userId: $id})
|
|
830
|
+
// Error: Cannot delete node with relationships
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
**Fix:** Always use `DETACH DELETE` to remove the node and all its relationships:
|
|
834
|
+
|
|
835
|
+
```cypher
|
|
836
|
+
// GOOD
|
|
837
|
+
MATCH (u:User {userId: $userId})
|
|
838
|
+
DETACH DELETE u
|
|
839
|
+
```
|