javi-forge 1.2.0 → 1.4.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/ci-local/ci-local.sh +29 -9
- package/ci-local/hooks/commit-msg +0 -0
- package/ci-local/hooks/pre-commit +1 -1
- package/ci-local/hooks/pre-push +0 -0
- package/ci-local/install.sh +0 -0
- package/ci-local/lib/common.sh +183 -0
- package/dist/__integration__/helpers.d.ts +20 -0
- package/dist/__integration__/helpers.d.ts.map +1 -0
- package/dist/__integration__/helpers.js +31 -0
- package/dist/__integration__/helpers.js.map +1 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +13 -8
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +1 -3
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +14 -6
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/llmstxt.d.ts.map +1 -0
- package/dist/commands/llmstxt.js.map +1 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/constants.d.ts +0 -4
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +0 -4
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -11
- package/dist/index.js.map +1 -0
- package/dist/lib/common.d.ts.map +1 -0
- package/dist/lib/common.js.map +1 -0
- package/dist/lib/docker.d.ts +2 -0
- package/dist/lib/docker.d.ts.map +1 -0
- package/dist/lib/docker.js +2 -1
- package/dist/lib/docker.js.map +1 -0
- package/dist/lib/frontmatter.d.ts.map +1 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/plugin.d.ts.map +1 -0
- package/dist/lib/plugin.js.map +1 -0
- package/dist/lib/template.d.ts.map +1 -0
- package/dist/lib/template.js.map +1 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/AnalyzeUI.d.ts.map +1 -0
- package/dist/ui/AnalyzeUI.js.map +1 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/CI.d.ts.map +1 -0
- package/dist/ui/CI.js.map +1 -0
- package/dist/ui/CIContext.d.ts.map +1 -0
- package/dist/ui/CIContext.js.map +1 -0
- package/dist/ui/CISelector.d.ts.map +1 -0
- package/dist/ui/CISelector.js.map +1 -0
- package/dist/ui/Doctor.d.ts.map +1 -0
- package/dist/ui/Doctor.js +1 -1
- package/dist/ui/Doctor.js.map +1 -0
- package/dist/ui/Header.d.ts.map +1 -0
- package/dist/ui/Header.js.map +1 -0
- package/dist/ui/LlmsTxt.d.ts.map +1 -0
- package/dist/ui/LlmsTxt.js.map +1 -0
- package/dist/ui/MemorySelector.d.ts.map +1 -0
- package/dist/ui/MemorySelector.js.map +1 -0
- package/dist/ui/NameInput.d.ts.map +1 -0
- package/dist/ui/NameInput.js.map +1 -0
- package/dist/ui/OptionSelector.d.ts.map +1 -0
- package/dist/ui/OptionSelector.js +1 -1
- package/dist/ui/OptionSelector.js.map +1 -0
- package/dist/ui/Plugin.d.ts.map +1 -0
- package/dist/ui/Plugin.js.map +1 -0
- package/dist/ui/Progress.d.ts.map +1 -0
- package/dist/ui/Progress.js.map +1 -0
- package/dist/ui/StackSelector.d.ts.map +1 -0
- package/dist/ui/StackSelector.js.map +1 -0
- package/dist/ui/Summary.d.ts.map +1 -0
- package/dist/ui/Summary.js.map +1 -0
- package/dist/ui/Welcome.d.ts.map +1 -0
- package/dist/ui/Welcome.js.map +1 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js.map +1 -0
- package/lib/common.sh +2 -2
- package/modules/ghagga/README.md +2 -2
- package/modules/ghagga/setup-ghagga.sh +1 -1
- package/package.json +25 -12
- package/templates/github/ci-go.yml +1 -1
- package/templates/github/ci-java.yml +2 -2
- package/templates/github/ci-node.yml +1 -1
- package/templates/github/ci-python.yml +1 -1
- package/templates/github/ci-rust.yml +1 -1
- package/templates/github/ghagga-review.yml +28 -0
- package/workflows/reusable-build-go.yml +1 -1
- package/workflows/reusable-build-java.yml +1 -1
- package/workflows/reusable-build-node.yml +1 -1
- package/workflows/reusable-build-python.yml +1 -1
- package/workflows/reusable-build-rust.yml +1 -1
- package/workflows/reusable-docker.yml +1 -1
- package/workflows/reusable-ghagga-review.yml +1 -1
- package/workflows/reusable-release.yml +1 -1
- package/.releaserc +0 -45
- package/ai-config/.skillignore +0 -15
- package/ai-config/AUTO_INVOKE.md +0 -300
- package/ai-config/agents/_TEMPLATE.md +0 -93
- package/ai-config/agents/business/api-designer.md +0 -1657
- package/ai-config/agents/business/business-analyst.md +0 -1331
- package/ai-config/agents/business/product-strategist.md +0 -206
- package/ai-config/agents/business/project-manager.md +0 -178
- package/ai-config/agents/business/requirements-analyst.md +0 -1277
- package/ai-config/agents/business/technical-writer.md +0 -1679
- package/ai-config/agents/creative/ux-designer.md +0 -205
- package/ai-config/agents/data-ai/ai-engineer.md +0 -487
- package/ai-config/agents/data-ai/analytics-engineer.md +0 -953
- package/ai-config/agents/data-ai/data-engineer.md +0 -173
- package/ai-config/agents/data-ai/data-scientist.md +0 -672
- package/ai-config/agents/data-ai/mlops-engineer.md +0 -814
- package/ai-config/agents/data-ai/prompt-engineer.md +0 -772
- package/ai-config/agents/development/angular-expert.md +0 -620
- package/ai-config/agents/development/backend-architect.md +0 -795
- package/ai-config/agents/development/database-specialist.md +0 -212
- package/ai-config/agents/development/frontend-specialist.md +0 -686
- package/ai-config/agents/development/fullstack-engineer.md +0 -668
- package/ai-config/agents/development/golang-pro.md +0 -338
- package/ai-config/agents/development/java-enterprise.md +0 -400
- package/ai-config/agents/development/javascript-pro.md +0 -422
- package/ai-config/agents/development/nextjs-pro.md +0 -474
- package/ai-config/agents/development/python-pro.md +0 -570
- package/ai-config/agents/development/react-pro.md +0 -487
- package/ai-config/agents/development/rust-pro.md +0 -246
- package/ai-config/agents/development/spring-boot-4-expert.md +0 -326
- package/ai-config/agents/development/typescript-pro.md +0 -336
- package/ai-config/agents/development/vue-specialist.md +0 -605
- package/ai-config/agents/infrastructure/cloud-architect.md +0 -472
- package/ai-config/agents/infrastructure/deployment-manager.md +0 -358
- package/ai-config/agents/infrastructure/devops-engineer.md +0 -455
- package/ai-config/agents/infrastructure/incident-responder.md +0 -519
- package/ai-config/agents/infrastructure/kubernetes-expert.md +0 -705
- package/ai-config/agents/infrastructure/monitoring-specialist.md +0 -674
- package/ai-config/agents/infrastructure/performance-engineer.md +0 -658
- package/ai-config/agents/orchestrator.md +0 -241
- package/ai-config/agents/quality/accessibility-auditor.md +0 -1204
- package/ai-config/agents/quality/code-reviewer-compact.md +0 -123
- package/ai-config/agents/quality/code-reviewer.md +0 -363
- package/ai-config/agents/quality/dependency-manager.md +0 -743
- package/ai-config/agents/quality/e2e-test-specialist.md +0 -1005
- package/ai-config/agents/quality/performance-tester.md +0 -1086
- package/ai-config/agents/quality/security-auditor.md +0 -133
- package/ai-config/agents/quality/test-engineer.md +0 -453
- package/ai-config/agents/specialists/api-designer.md +0 -87
- package/ai-config/agents/specialists/backend-architect.md +0 -73
- package/ai-config/agents/specialists/code-reviewer.md +0 -77
- package/ai-config/agents/specialists/db-optimizer.md +0 -75
- package/ai-config/agents/specialists/devops-engineer.md +0 -83
- package/ai-config/agents/specialists/documentation-writer.md +0 -78
- package/ai-config/agents/specialists/frontend-developer.md +0 -75
- package/ai-config/agents/specialists/performance-analyst.md +0 -82
- package/ai-config/agents/specialists/refactor-specialist.md +0 -74
- package/ai-config/agents/specialists/security-auditor.md +0 -74
- package/ai-config/agents/specialists/test-engineer.md +0 -81
- package/ai-config/agents/specialists/ux-consultant.md +0 -76
- package/ai-config/agents/specialized/agent-generator.md +0 -1190
- package/ai-config/agents/specialized/blockchain-developer.md +0 -149
- package/ai-config/agents/specialized/code-migrator.md +0 -892
- package/ai-config/agents/specialized/context-manager.md +0 -978
- package/ai-config/agents/specialized/documentation-writer.md +0 -1078
- package/ai-config/agents/specialized/ecommerce-expert.md +0 -1756
- package/ai-config/agents/specialized/embedded-engineer.md +0 -1714
- package/ai-config/agents/specialized/error-detective.md +0 -1034
- package/ai-config/agents/specialized/fintech-specialist.md +0 -1659
- package/ai-config/agents/specialized/freelance-project-planner-v2.md +0 -1988
- package/ai-config/agents/specialized/freelance-project-planner-v3.md +0 -2136
- package/ai-config/agents/specialized/freelance-project-planner-v4.md +0 -4503
- package/ai-config/agents/specialized/freelance-project-planner.md +0 -722
- package/ai-config/agents/specialized/game-developer.md +0 -1963
- package/ai-config/agents/specialized/healthcare-dev.md +0 -1620
- package/ai-config/agents/specialized/mobile-developer.md +0 -188
- package/ai-config/agents/specialized/parallel-plan-executor.md +0 -506
- package/ai-config/agents/specialized/plan-executor.md +0 -485
- package/ai-config/agents/specialized/solo-dev-planner-modular/00-INDEX.md +0 -485
- package/ai-config/agents/specialized/solo-dev-planner-modular/01-CORE.md +0 -3493
- package/ai-config/agents/specialized/solo-dev-planner-modular/02-SELF-CORRECTION.md +0 -778
- package/ai-config/agents/specialized/solo-dev-planner-modular/03-PROGRESSIVE-SETUP.md +0 -918
- package/ai-config/agents/specialized/solo-dev-planner-modular/04-DEPLOYMENT.md +0 -1537
- package/ai-config/agents/specialized/solo-dev-planner-modular/05-TESTING.md +0 -2633
- package/ai-config/agents/specialized/solo-dev-planner-modular/06-OPERATIONS.md +0 -5610
- package/ai-config/agents/specialized/solo-dev-planner-modular/INSTALL.md +0 -335
- package/ai-config/agents/specialized/solo-dev-planner-modular/QUICK-REFERENCE.txt +0 -215
- package/ai-config/agents/specialized/solo-dev-planner-modular/README.md +0 -260
- package/ai-config/agents/specialized/solo-dev-planner-modular/START-HERE.md +0 -379
- package/ai-config/agents/specialized/solo-dev-planner-modular/WORKFLOW-DIAGRAM.md +0 -355
- package/ai-config/agents/specialized/solo-dev-planner-modular/solo-dev-planner.md +0 -279
- package/ai-config/agents/specialized/template-writer.md +0 -347
- package/ai-config/agents/specialized/test-runner.md +0 -99
- package/ai-config/agents/specialized/vibekanban-smart-worker.md +0 -244
- package/ai-config/agents/specialized/wave-executor.md +0 -138
- package/ai-config/agents/specialized/workflow-optimizer.md +0 -1114
- package/ai-config/commands/git/changelog.md +0 -32
- package/ai-config/commands/git/ci-local.md +0 -70
- package/ai-config/commands/git/commit.md +0 -35
- package/ai-config/commands/git/fix-issue.md +0 -23
- package/ai-config/commands/git/pr-create.md +0 -42
- package/ai-config/commands/git/pr-review.md +0 -50
- package/ai-config/commands/git/worktree.md +0 -39
- package/ai-config/commands/refactoring/cleanup.md +0 -24
- package/ai-config/commands/refactoring/dead-code.md +0 -40
- package/ai-config/commands/refactoring/extract.md +0 -31
- package/ai-config/commands/testing/e2e.md +0 -30
- package/ai-config/commands/testing/tdd.md +0 -36
- package/ai-config/commands/testing/test-coverage.md +0 -30
- package/ai-config/commands/testing/test-fix.md +0 -24
- package/ai-config/commands/workflow/generate-agents-md.md +0 -85
- package/ai-config/commands/workflow/planning.md +0 -47
- package/ai-config/commands/workflows/compound.md +0 -89
- package/ai-config/commands/workflows/diagnose.md +0 -70
- package/ai-config/commands/workflows/discover.md +0 -86
- package/ai-config/commands/workflows/plan.md +0 -77
- package/ai-config/commands/workflows/review.md +0 -78
- package/ai-config/commands/workflows/work.md +0 -75
- package/ai-config/config.yaml +0 -18
- package/ai-config/hooks/_TEMPLATE.md +0 -96
- package/ai-config/hooks/block-dangerous-commands.md +0 -75
- package/ai-config/hooks/commit-guard.md +0 -90
- package/ai-config/hooks/context-loader.md +0 -73
- package/ai-config/hooks/improve-prompt.md +0 -91
- package/ai-config/hooks/learning-log.md +0 -72
- package/ai-config/hooks/model-router.md +0 -86
- package/ai-config/hooks/secret-scanner.md +0 -64
- package/ai-config/hooks/skill-validator.md +0 -102
- package/ai-config/hooks/task-artifact.md +0 -114
- package/ai-config/hooks/validate-workflow.md +0 -100
- package/ai-config/prompts/base.md +0 -71
- package/ai-config/prompts/modes/debug.md +0 -34
- package/ai-config/prompts/modes/deploy.md +0 -40
- package/ai-config/prompts/modes/research.md +0 -32
- package/ai-config/prompts/modes/review.md +0 -33
- package/ai-config/prompts/review-policy.md +0 -79
- package/ai-config/skills/_TEMPLATE.md +0 -157
- package/ai-config/skills/backend/api-gateway/SKILL.md +0 -254
- package/ai-config/skills/backend/bff-concepts/SKILL.md +0 -239
- package/ai-config/skills/backend/bff-spring/SKILL.md +0 -364
- package/ai-config/skills/backend/chi-router/SKILL.md +0 -396
- package/ai-config/skills/backend/error-handling/SKILL.md +0 -255
- package/ai-config/skills/backend/exceptions-spring/SKILL.md +0 -323
- package/ai-config/skills/backend/fastapi/SKILL.md +0 -302
- package/ai-config/skills/backend/gateway-spring/SKILL.md +0 -390
- package/ai-config/skills/backend/go-backend/SKILL.md +0 -457
- package/ai-config/skills/backend/gradle-multimodule/SKILL.md +0 -274
- package/ai-config/skills/backend/graphql-concepts/SKILL.md +0 -352
- package/ai-config/skills/backend/graphql-spring/SKILL.md +0 -398
- package/ai-config/skills/backend/grpc-concepts/SKILL.md +0 -283
- package/ai-config/skills/backend/grpc-spring/SKILL.md +0 -445
- package/ai-config/skills/backend/jwt-auth/SKILL.md +0 -412
- package/ai-config/skills/backend/notifications-concepts/SKILL.md +0 -259
- package/ai-config/skills/backend/recommendations-concepts/SKILL.md +0 -261
- package/ai-config/skills/backend/search-concepts/SKILL.md +0 -263
- package/ai-config/skills/backend/search-spring/SKILL.md +0 -375
- package/ai-config/skills/backend/spring-boot-4/SKILL.md +0 -172
- package/ai-config/skills/backend/websockets/SKILL.md +0 -532
- package/ai-config/skills/data-ai/ai-ml/SKILL.md +0 -423
- package/ai-config/skills/data-ai/analytics-concepts/SKILL.md +0 -195
- package/ai-config/skills/data-ai/analytics-spring/SKILL.md +0 -340
- package/ai-config/skills/data-ai/duckdb-analytics/SKILL.md +0 -440
- package/ai-config/skills/data-ai/langchain/SKILL.md +0 -238
- package/ai-config/skills/data-ai/mlflow/SKILL.md +0 -302
- package/ai-config/skills/data-ai/onnx-inference/SKILL.md +0 -290
- package/ai-config/skills/data-ai/powerbi/SKILL.md +0 -352
- package/ai-config/skills/data-ai/pytorch/SKILL.md +0 -274
- package/ai-config/skills/data-ai/scikit-learn/SKILL.md +0 -321
- package/ai-config/skills/data-ai/vector-db/SKILL.md +0 -301
- package/ai-config/skills/database/graph-databases/SKILL.md +0 -218
- package/ai-config/skills/database/graph-spring/SKILL.md +0 -361
- package/ai-config/skills/database/pgx-postgres/SKILL.md +0 -512
- package/ai-config/skills/database/redis-cache/SKILL.md +0 -343
- package/ai-config/skills/database/sqlite-embedded/SKILL.md +0 -388
- package/ai-config/skills/database/timescaledb/SKILL.md +0 -320
- package/ai-config/skills/docs/api-documentation/SKILL.md +0 -293
- package/ai-config/skills/docs/docs-spring/SKILL.md +0 -377
- package/ai-config/skills/docs/mustache-templates/SKILL.md +0 -190
- package/ai-config/skills/docs/technical-docs/SKILL.md +0 -447
- package/ai-config/skills/frontend/astro-ssr/SKILL.md +0 -441
- package/ai-config/skills/frontend/frontend-design/SKILL.md +0 -54
- package/ai-config/skills/frontend/frontend-web/SKILL.md +0 -368
- package/ai-config/skills/frontend/mantine-ui/SKILL.md +0 -396
- package/ai-config/skills/frontend/tanstack-query/SKILL.md +0 -439
- package/ai-config/skills/frontend/zod-validation/SKILL.md +0 -417
- package/ai-config/skills/frontend/zustand-state/SKILL.md +0 -350
- package/ai-config/skills/infrastructure/chaos-engineering/SKILL.md +0 -244
- package/ai-config/skills/infrastructure/chaos-spring/SKILL.md +0 -378
- package/ai-config/skills/infrastructure/devops-infra/SKILL.md +0 -435
- package/ai-config/skills/infrastructure/docker-containers/SKILL.md +0 -420
- package/ai-config/skills/infrastructure/kubernetes/SKILL.md +0 -456
- package/ai-config/skills/infrastructure/opentelemetry/SKILL.md +0 -546
- package/ai-config/skills/infrastructure/traefik-proxy/SKILL.md +0 -474
- package/ai-config/skills/infrastructure/woodpecker-ci/SKILL.md +0 -315
- package/ai-config/skills/mobile/ionic-capacitor/SKILL.md +0 -504
- package/ai-config/skills/mobile/mobile-ionic/SKILL.md +0 -448
- package/ai-config/skills/prompt-improver/SKILL.md +0 -125
- package/ai-config/skills/quality/ghagga-review/SKILL.md +0 -216
- package/ai-config/skills/references/hooks-patterns/SKILL.md +0 -238
- package/ai-config/skills/references/mcp-servers/SKILL.md +0 -275
- package/ai-config/skills/references/plugins-reference/SKILL.md +0 -110
- package/ai-config/skills/references/skills-reference/SKILL.md +0 -420
- package/ai-config/skills/references/subagent-templates/SKILL.md +0 -193
- package/ai-config/skills/systems-iot/modbus-protocol/SKILL.md +0 -410
- package/ai-config/skills/systems-iot/mqtt-rumqttc/SKILL.md +0 -408
- package/ai-config/skills/systems-iot/rust-systems/SKILL.md +0 -386
- package/ai-config/skills/systems-iot/tokio-async/SKILL.md +0 -324
- package/ai-config/skills/testing/playwright-e2e/SKILL.md +0 -289
- package/ai-config/skills/testing/testcontainers/SKILL.md +0 -299
- package/ai-config/skills/testing/vitest-testing/SKILL.md +0 -381
- package/ai-config/skills/workflow/ci-local-guide/SKILL.md +0 -118
- package/ai-config/skills/workflow/claude-automation-recommender/SKILL.md +0 -299
- package/ai-config/skills/workflow/claude-md-improver/SKILL.md +0 -158
- package/ai-config/skills/workflow/finishing-a-development-branch/SKILL.md +0 -117
- package/ai-config/skills/workflow/git-github/SKILL.md +0 -334
- package/ai-config/skills/workflow/git-github/references/examples.md +0 -160
- package/ai-config/skills/workflow/git-workflow/SKILL.md +0 -214
- package/ai-config/skills/workflow/ide-plugins/SKILL.md +0 -277
- package/ai-config/skills/workflow/ide-plugins-intellij/SKILL.md +0 -401
- package/ai-config/skills/workflow/obsidian-brain-workflow/SKILL.md +0 -199
- package/ai-config/skills/workflow/using-git-worktrees/SKILL.md +0 -100
- package/ai-config/skills/workflow/verification-before-completion/SKILL.md +0 -73
- package/ai-config/skills/workflow/wave-workflow/SKILL.md +0 -178
- package/dist/commands/analyze.test.d.ts +0 -2
- package/dist/commands/doctor.test.d.ts +0 -2
- package/dist/commands/init.test.d.ts +0 -2
- package/dist/commands/llmstxt.test.d.ts +0 -2
- package/dist/commands/plugin.test.d.ts +0 -2
- package/dist/commands/sync.d.ts +0 -8
- package/dist/commands/sync.js +0 -201
- package/dist/e2e/aggressive.e2e.test.d.ts +0 -2
- package/dist/e2e/commands.e2e.test.d.ts +0 -2
- package/dist/lib/common.test.d.ts +0 -2
- package/dist/lib/frontmatter.test.d.ts +0 -2
- package/dist/lib/plugin.test.d.ts +0 -2
- package/dist/lib/template.test.d.ts +0 -2
- package/dist/ui/SyncUI.d.ts +0 -10
- package/dist/ui/SyncUI.js +0 -64
- package/schemas/agent.schema.json +0 -34
- package/schemas/ai-config.schema.json +0 -28
- package/schemas/plugin.schema.json +0 -62
- package/schemas/skill.schema.json +0 -44
- package/tasks/_TEMPLATE/files-edited.md +0 -3
- package/tasks/_TEMPLATE/plan.md +0 -3
- package/tasks/_TEMPLATE/research.md +0 -3
- package/tasks/_TEMPLATE/verification.md +0 -5
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: graph-databases
|
|
3
|
-
description: >
|
|
4
|
-
Graph database concepts. Nodes, relationships, traversals, Cypher/Gremlin queries.
|
|
5
|
-
Trigger: graph database, Neo4j, Neptune, ArangoDB, nodes, relationships, Cypher
|
|
6
|
-
tools:
|
|
7
|
-
- Read
|
|
8
|
-
- Write
|
|
9
|
-
- Edit
|
|
10
|
-
- Grep
|
|
11
|
-
metadata:
|
|
12
|
-
author: apigen-team
|
|
13
|
-
version: "1.0"
|
|
14
|
-
tags: [graph, database, neo4j, neptune]
|
|
15
|
-
scope: ["**/graph/**"]
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
# Graph Database Concepts
|
|
19
|
-
|
|
20
|
-
## Core Concepts
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
Nodes: Entities (vertices)
|
|
24
|
-
- User, Product, Order
|
|
25
|
-
- Have properties (key-value)
|
|
26
|
-
- Have labels (types)
|
|
27
|
-
|
|
28
|
-
Relationships: Connections (edges)
|
|
29
|
-
- FOLLOWS, PURCHASED, KNOWS
|
|
30
|
-
- Have direction (or bidirectional)
|
|
31
|
-
- Have properties (weight, timestamp)
|
|
32
|
-
|
|
33
|
-
Properties: Key-value attributes
|
|
34
|
-
- On nodes: name, email, created_at
|
|
35
|
-
- On relationships: since, strength, role
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## When to Use Graph Databases
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
Good fit:
|
|
42
|
-
✅ Social networks (friends, followers)
|
|
43
|
-
✅ Recommendation engines
|
|
44
|
-
✅ Fraud detection (pattern matching)
|
|
45
|
-
✅ Knowledge graphs
|
|
46
|
-
✅ Network topology
|
|
47
|
-
✅ Access control (who can see what)
|
|
48
|
-
|
|
49
|
-
Poor fit:
|
|
50
|
-
❌ Simple CRUD operations
|
|
51
|
-
❌ High-volume transactions
|
|
52
|
-
❌ Full-text search (use Elasticsearch)
|
|
53
|
-
❌ Time-series data
|
|
54
|
-
❌ Large binary storage
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Graph vs Relational
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
Relational (SQL):
|
|
61
|
-
┌─────────┐ ┌────────────────┐ ┌─────────┐
|
|
62
|
-
│ users │──────│ friendships │──────│ users │
|
|
63
|
-
│ id=1 │ │ user_id=1 │ │ id=2 │
|
|
64
|
-
│ name=A │ │ friend_id=2 │ │ name=B │
|
|
65
|
-
└─────────┘ └────────────────┘ └─────────┘
|
|
66
|
-
|
|
67
|
-
Graph:
|
|
68
|
-
┌──────────┐ FOLLOWS ┌──────────┐
|
|
69
|
-
│ (User) │───────────▶│ (User) │
|
|
70
|
-
│ name: A │ │ name: B │
|
|
71
|
-
└──────────┘ └──────────┘
|
|
72
|
-
|
|
73
|
-
SQL for "friends of friends":
|
|
74
|
-
SELECT DISTINCT u3.name FROM users u1
|
|
75
|
-
JOIN friendships f1 ON u1.id = f1.user_id
|
|
76
|
-
JOIN friendships f2 ON f1.friend_id = f2.user_id
|
|
77
|
-
JOIN users u3 ON f2.friend_id = u3.id
|
|
78
|
-
WHERE u1.id = 1 AND u3.id != 1
|
|
79
|
-
-- Becomes complex for N levels
|
|
80
|
-
|
|
81
|
-
Cypher (Neo4j):
|
|
82
|
-
MATCH (u:User {id: 1})-[:FOLLOWS*2]->(fof:User)
|
|
83
|
-
WHERE fof <> u
|
|
84
|
-
RETURN DISTINCT fof.name
|
|
85
|
-
-- Same query, any depth with *N
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Cypher Query Language (Neo4j)
|
|
89
|
-
|
|
90
|
-
### Basic Patterns
|
|
91
|
-
```cypher
|
|
92
|
-
// Create nodes
|
|
93
|
-
CREATE (u:User {name: 'Alice', email: 'alice@example.com'})
|
|
94
|
-
|
|
95
|
-
// Create relationship
|
|
96
|
-
MATCH (a:User {name: 'Alice'}), (b:User {name: 'Bob'})
|
|
97
|
-
CREATE (a)-[:FOLLOWS {since: date()}]->(b)
|
|
98
|
-
|
|
99
|
-
// Find nodes
|
|
100
|
-
MATCH (u:User {name: 'Alice'})
|
|
101
|
-
RETURN u
|
|
102
|
-
|
|
103
|
-
// Find relationships
|
|
104
|
-
MATCH (a:User)-[r:FOLLOWS]->(b:User)
|
|
105
|
-
RETURN a.name, r.since, b.name
|
|
106
|
-
|
|
107
|
-
// Traversal
|
|
108
|
-
MATCH (u:User {name: 'Alice'})-[:FOLLOWS*1..3]->(friend)
|
|
109
|
-
RETURN DISTINCT friend.name
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Advanced Queries
|
|
113
|
-
```cypher
|
|
114
|
-
// Shortest path
|
|
115
|
-
MATCH path = shortestPath(
|
|
116
|
-
(a:User {name: 'Alice'})-[:FOLLOWS*]-(b:User {name: 'Zara'})
|
|
117
|
-
)
|
|
118
|
-
RETURN path
|
|
119
|
-
|
|
120
|
-
// Aggregation
|
|
121
|
-
MATCH (u:User)-[:FOLLOWS]->(follower)
|
|
122
|
-
RETURN u.name, count(follower) AS followers
|
|
123
|
-
ORDER BY followers DESC
|
|
124
|
-
LIMIT 10
|
|
125
|
-
|
|
126
|
-
// Pattern matching (fraud detection)
|
|
127
|
-
MATCH (a:Account)-[:TRANSFER]->(b:Account)-[:TRANSFER]->(c:Account)-[:TRANSFER]->(a)
|
|
128
|
-
WHERE a.suspicious = true
|
|
129
|
-
RETURN a, b, c
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Gremlin Query Language (Neptune, JanusGraph)
|
|
133
|
-
|
|
134
|
-
```groovy
|
|
135
|
-
// Create vertex
|
|
136
|
-
g.addV('User').property('name', 'Alice')
|
|
137
|
-
|
|
138
|
-
// Create edge
|
|
139
|
-
g.V().has('User', 'name', 'Alice')
|
|
140
|
-
.addE('FOLLOWS').to(g.V().has('User', 'name', 'Bob'))
|
|
141
|
-
|
|
142
|
-
// Traversal
|
|
143
|
-
g.V().has('User', 'name', 'Alice')
|
|
144
|
-
.out('FOLLOWS')
|
|
145
|
-
.out('FOLLOWS')
|
|
146
|
-
.dedup()
|
|
147
|
-
.values('name')
|
|
148
|
-
|
|
149
|
-
// Shortest path
|
|
150
|
-
g.V().has('User', 'name', 'Alice')
|
|
151
|
-
.repeat(out('FOLLOWS').simplePath())
|
|
152
|
-
.until(has('User', 'name', 'Zara'))
|
|
153
|
-
.path()
|
|
154
|
-
.limit(1)
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## Data Modeling Patterns
|
|
158
|
-
|
|
159
|
-
### Social Network
|
|
160
|
-
```
|
|
161
|
-
(:User)-[:FOLLOWS]->(:User)
|
|
162
|
-
(:User)-[:POSTED]->(:Post)
|
|
163
|
-
(:User)-[:LIKES]->(:Post)
|
|
164
|
-
(:Post)-[:TAGGED]->(:Topic)
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### E-commerce Recommendations
|
|
168
|
-
```
|
|
169
|
-
(:Customer)-[:PURCHASED]->(:Product)
|
|
170
|
-
(:Product)-[:IN_CATEGORY]->(:Category)
|
|
171
|
-
(:Customer)-[:VIEWED]->(:Product)
|
|
172
|
-
(:Product)-[:SIMILAR_TO]->(:Product)
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Access Control
|
|
176
|
-
```
|
|
177
|
-
(:User)-[:MEMBER_OF]->(:Group)
|
|
178
|
-
(:Group)-[:HAS_ROLE]->(:Role)
|
|
179
|
-
(:Role)-[:CAN_ACCESS]->(:Resource)
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Performance Considerations
|
|
183
|
-
|
|
184
|
-
```
|
|
185
|
-
Indexing:
|
|
186
|
-
- Index frequently queried properties
|
|
187
|
-
- Composite indexes for common patterns
|
|
188
|
-
- Full-text indexes for search
|
|
189
|
-
|
|
190
|
-
Query optimization:
|
|
191
|
-
- Start traversals from selective nodes
|
|
192
|
-
- Limit traversal depth
|
|
193
|
-
- Use PROFILE to analyze queries
|
|
194
|
-
|
|
195
|
-
Cardinality:
|
|
196
|
-
- Avoid super nodes (millions of edges)
|
|
197
|
-
- Consider edge partitioning
|
|
198
|
-
- Use intermediate nodes for many-to-many
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
## Graph Database Comparison
|
|
202
|
-
|
|
203
|
-
```
|
|
204
|
-
| Feature | Neo4j | Neptune | ArangoDB |
|
|
205
|
-
|---------|-------|---------|----------|
|
|
206
|
-
| Query | Cypher | Gremlin | AQL |
|
|
207
|
-
| Model | LPG | LPG/RDF | Multi-model |
|
|
208
|
-
| Scaling | Read replicas | Auto-scaling | Sharding |
|
|
209
|
-
| Hosting | Self/Cloud | AWS only | Self/Cloud |
|
|
210
|
-
| ACID | Yes | Yes | Yes |
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## Related Skills
|
|
214
|
-
|
|
215
|
-
- `graph-spring`: Spring Boot graph implementation
|
|
216
|
-
- `apigen-architecture`: Overall system architecture
|
|
217
|
-
|
|
218
|
-
|
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: graph-spring
|
|
3
|
-
description: >
|
|
4
|
-
Spring Boot graph database integration. Neo4j, Neptune, ArangoDB with Spring Data.
|
|
5
|
-
Trigger: apigen-graph, Neo4jRepository, GraphService, Spring Data Neo4j
|
|
6
|
-
tools:
|
|
7
|
-
- Read
|
|
8
|
-
- Write
|
|
9
|
-
- Edit
|
|
10
|
-
- Bash
|
|
11
|
-
- Grep
|
|
12
|
-
metadata:
|
|
13
|
-
author: apigen-team
|
|
14
|
-
version: "1.0"
|
|
15
|
-
tags: [graph, spring-boot, neo4j, java]
|
|
16
|
-
scope: ["apigen-graph/**"]
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
# Graph Spring Boot (apigen-graph)
|
|
20
|
-
|
|
21
|
-
## Configuration
|
|
22
|
-
|
|
23
|
-
```yaml
|
|
24
|
-
apigen:
|
|
25
|
-
graph:
|
|
26
|
-
enabled: true
|
|
27
|
-
provider: neo4j # neo4j, neptune, arangodb
|
|
28
|
-
|
|
29
|
-
neo4j:
|
|
30
|
-
uri: bolt://localhost:7687
|
|
31
|
-
username: neo4j
|
|
32
|
-
password: ${NEO4J_PASSWORD}
|
|
33
|
-
database: neo4j
|
|
34
|
-
connection-pool-size: 50
|
|
35
|
-
|
|
36
|
-
neptune:
|
|
37
|
-
endpoint: ${NEPTUNE_ENDPOINT}
|
|
38
|
-
port: 8182
|
|
39
|
-
region: us-east-1
|
|
40
|
-
iam-auth: true
|
|
41
|
-
|
|
42
|
-
arangodb:
|
|
43
|
-
host: localhost
|
|
44
|
-
port: 8529
|
|
45
|
-
database: apigen
|
|
46
|
-
username: root
|
|
47
|
-
password: ${ARANGO_PASSWORD}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Neo4j Entity Mapping
|
|
51
|
-
|
|
52
|
-
```java
|
|
53
|
-
@Node("User")
|
|
54
|
-
public class UserNode {
|
|
55
|
-
|
|
56
|
-
@Id
|
|
57
|
-
@GeneratedValue
|
|
58
|
-
private Long id;
|
|
59
|
-
|
|
60
|
-
@Property("externalId")
|
|
61
|
-
private UUID externalId;
|
|
62
|
-
|
|
63
|
-
private String name;
|
|
64
|
-
private String email;
|
|
65
|
-
private Instant createdAt;
|
|
66
|
-
|
|
67
|
-
@Relationship(type = "FOLLOWS", direction = Direction.OUTGOING)
|
|
68
|
-
private Set<UserNode> following = new HashSet<>();
|
|
69
|
-
|
|
70
|
-
@Relationship(type = "FOLLOWS", direction = Direction.INCOMING)
|
|
71
|
-
private Set<UserNode> followers = new HashSet<>();
|
|
72
|
-
|
|
73
|
-
@Relationship(type = "PURCHASED")
|
|
74
|
-
private List<PurchaseRelationship> purchases = new ArrayList<>();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@RelationshipProperties
|
|
78
|
-
public class PurchaseRelationship {
|
|
79
|
-
|
|
80
|
-
@Id
|
|
81
|
-
@GeneratedValue
|
|
82
|
-
private Long id;
|
|
83
|
-
|
|
84
|
-
@TargetNode
|
|
85
|
-
private ProductNode product;
|
|
86
|
-
|
|
87
|
-
private Instant purchasedAt;
|
|
88
|
-
private BigDecimal amount;
|
|
89
|
-
private Integer quantity;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
@Node("Product")
|
|
93
|
-
public class ProductNode {
|
|
94
|
-
@Id @GeneratedValue
|
|
95
|
-
private Long id;
|
|
96
|
-
|
|
97
|
-
private String sku;
|
|
98
|
-
private String name;
|
|
99
|
-
private String category;
|
|
100
|
-
|
|
101
|
-
@Relationship(type = "SIMILAR_TO")
|
|
102
|
-
private List<ProductNode> similarProducts;
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## Repository Layer
|
|
107
|
-
|
|
108
|
-
```java
|
|
109
|
-
public interface UserNodeRepository extends Neo4jRepository<UserNode, Long> {
|
|
110
|
-
|
|
111
|
-
Optional<UserNode> findByExternalId(UUID externalId);
|
|
112
|
-
|
|
113
|
-
@Query("MATCH (u:User {externalId: $userId})-[:FOLLOWS]->(f:User) RETURN f")
|
|
114
|
-
List<UserNode> findFollowing(UUID userId);
|
|
115
|
-
|
|
116
|
-
@Query("MATCH (u:User {externalId: $userId})<-[:FOLLOWS]-(f:User) RETURN f")
|
|
117
|
-
List<UserNode> findFollowers(UUID userId);
|
|
118
|
-
|
|
119
|
-
@Query("""
|
|
120
|
-
MATCH (u:User {externalId: $userId})-[:FOLLOWS*2]->(fof:User)
|
|
121
|
-
WHERE fof.externalId <> $userId
|
|
122
|
-
RETURN DISTINCT fof
|
|
123
|
-
LIMIT $limit
|
|
124
|
-
""")
|
|
125
|
-
List<UserNode> findFriendsOfFriends(UUID userId, int limit);
|
|
126
|
-
|
|
127
|
-
@Query("""
|
|
128
|
-
MATCH path = shortestPath(
|
|
129
|
-
(a:User {externalId: $fromId})-[:FOLLOWS*1..6]-(b:User {externalId: $toId})
|
|
130
|
-
)
|
|
131
|
-
RETURN path
|
|
132
|
-
""")
|
|
133
|
-
List<UserNode> findShortestPath(UUID fromId, UUID toId);
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Graph Service
|
|
138
|
-
|
|
139
|
-
```java
|
|
140
|
-
@Service
|
|
141
|
-
@RequiredArgsConstructor
|
|
142
|
-
public class GraphService {
|
|
143
|
-
|
|
144
|
-
private final UserNodeRepository userRepository;
|
|
145
|
-
private final ProductNodeRepository productRepository;
|
|
146
|
-
private final Neo4jClient neo4jClient;
|
|
147
|
-
|
|
148
|
-
@Transactional
|
|
149
|
-
public void createFollowRelationship(UUID followerId, UUID followeeId) {
|
|
150
|
-
UserNode follower = userRepository.findByExternalId(followerId)
|
|
151
|
-
.orElseThrow(() -> new NodeNotFoundException("User", followerId));
|
|
152
|
-
UserNode followee = userRepository.findByExternalId(followeeId)
|
|
153
|
-
.orElseThrow(() -> new NodeNotFoundException("User", followeeId));
|
|
154
|
-
|
|
155
|
-
follower.getFollowing().add(followee);
|
|
156
|
-
userRepository.save(follower);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
@Transactional(readOnly = true)
|
|
160
|
-
public List<UserDTO> getRecommendedConnections(UUID userId, int limit) {
|
|
161
|
-
// Friends of friends who aren't already friends
|
|
162
|
-
return neo4jClient.query("""
|
|
163
|
-
MATCH (u:User {externalId: $userId})-[:FOLLOWS]->()-[:FOLLOWS]->(rec:User)
|
|
164
|
-
WHERE NOT (u)-[:FOLLOWS]->(rec) AND rec <> u
|
|
165
|
-
WITH rec, count(*) as mutualFriends
|
|
166
|
-
ORDER BY mutualFriends DESC
|
|
167
|
-
LIMIT $limit
|
|
168
|
-
RETURN rec, mutualFriends
|
|
169
|
-
""")
|
|
170
|
-
.bind(userId).to("userId")
|
|
171
|
-
.bind(limit).to("limit")
|
|
172
|
-
.fetchAs(UserDTO.class)
|
|
173
|
-
.mappedBy((typeSystem, record) -> new UserDTO(
|
|
174
|
-
record.get("rec").get("externalId").asString(),
|
|
175
|
-
record.get("rec").get("name").asString(),
|
|
176
|
-
record.get("mutualFriends").asInt()
|
|
177
|
-
))
|
|
178
|
-
.all();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
@Transactional(readOnly = true)
|
|
182
|
-
public List<ProductNode> getProductRecommendations(UUID userId, int limit) {
|
|
183
|
-
// Products purchased by users who purchased similar products
|
|
184
|
-
return neo4jClient.query("""
|
|
185
|
-
MATCH (u:User {externalId: $userId})-[:PURCHASED]->(p:Product)
|
|
186
|
-
<-[:PURCHASED]-(other:User)-[:PURCHASED]->(rec:Product)
|
|
187
|
-
WHERE NOT (u)-[:PURCHASED]->(rec)
|
|
188
|
-
WITH rec, count(DISTINCT other) as score
|
|
189
|
-
ORDER BY score DESC
|
|
190
|
-
LIMIT $limit
|
|
191
|
-
RETURN rec
|
|
192
|
-
""")
|
|
193
|
-
.bind(userId).to("userId")
|
|
194
|
-
.bind(limit).to("limit")
|
|
195
|
-
.fetchAs(ProductNode.class)
|
|
196
|
-
.all();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
## Neptune Integration (AWS)
|
|
202
|
-
|
|
203
|
-
```java
|
|
204
|
-
@Configuration
|
|
205
|
-
@ConditionalOnProperty(prefix = "apigen.graph", name = "provider", havingValue = "neptune")
|
|
206
|
-
public class NeptuneConfiguration {
|
|
207
|
-
|
|
208
|
-
@Bean
|
|
209
|
-
public Cluster neptuneCluster(GraphProperties props) {
|
|
210
|
-
return Cluster.build()
|
|
211
|
-
.addContactPoint(props.getNeptune().getEndpoint())
|
|
212
|
-
.port(props.getNeptune().getPort())
|
|
213
|
-
.enableSsl(true)
|
|
214
|
-
.channelizer(SigV4WebSocketChannelizer.class)
|
|
215
|
-
.create();
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
@Bean
|
|
219
|
-
public GraphTraversalSource g(Cluster cluster) {
|
|
220
|
-
return AnonymousTraversalSource.traversal()
|
|
221
|
-
.withRemote(DriverRemoteConnection.using(cluster));
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
@Service
|
|
226
|
-
@ConditionalOnProperty(prefix = "apigen.graph", name = "provider", havingValue = "neptune")
|
|
227
|
-
public class NeptuneGraphService {
|
|
228
|
-
|
|
229
|
-
private final GraphTraversalSource g;
|
|
230
|
-
|
|
231
|
-
public List<Map<String, Object>> findFriendsOfFriends(String userId) {
|
|
232
|
-
return g.V().has("User", "userId", userId)
|
|
233
|
-
.out("FOLLOWS")
|
|
234
|
-
.out("FOLLOWS")
|
|
235
|
-
.dedup()
|
|
236
|
-
.hasNot("userId", userId)
|
|
237
|
-
.valueMap("userId", "name")
|
|
238
|
-
.toList();
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## ArangoDB Integration
|
|
244
|
-
|
|
245
|
-
```java
|
|
246
|
-
@Configuration
|
|
247
|
-
@ConditionalOnProperty(prefix = "apigen.graph", name = "provider", havingValue = "arangodb")
|
|
248
|
-
public class ArangoConfiguration {
|
|
249
|
-
|
|
250
|
-
@Bean
|
|
251
|
-
public ArangoDB arangoDB(GraphProperties props) {
|
|
252
|
-
return new ArangoDB.Builder()
|
|
253
|
-
.host(props.getArangodb().getHost(), props.getArangodb().getPort())
|
|
254
|
-
.user(props.getArangodb().getUsername())
|
|
255
|
-
.password(props.getArangodb().getPassword())
|
|
256
|
-
.build();
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
@Bean
|
|
260
|
-
public ArangoDatabase database(ArangoDB arangoDB, GraphProperties props) {
|
|
261
|
-
return arangoDB.db(props.getArangodb().getDatabase());
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
@Repository
|
|
266
|
-
@ConditionalOnProperty(prefix = "apigen.graph", name = "provider", havingValue = "arangodb")
|
|
267
|
-
public class ArangoUserRepository {
|
|
268
|
-
|
|
269
|
-
private final ArangoDatabase db;
|
|
270
|
-
|
|
271
|
-
public List<UserDTO> findFriendsOfFriends(String userId) {
|
|
272
|
-
String aql = """
|
|
273
|
-
FOR u IN users
|
|
274
|
-
FILTER u._key == @userId
|
|
275
|
-
FOR fof IN 2..2 OUTBOUND u follows
|
|
276
|
-
FILTER fof._key != @userId
|
|
277
|
-
COLLECT user = fof WITH COUNT INTO cnt
|
|
278
|
-
SORT cnt DESC
|
|
279
|
-
RETURN {user: user, mutualFriends: cnt}
|
|
280
|
-
""";
|
|
281
|
-
|
|
282
|
-
return db.query(aql, Map.of("userId", userId), UserDTO.class)
|
|
283
|
-
.asListRemaining();
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
## Graph Auto Configuration
|
|
289
|
-
|
|
290
|
-
```java
|
|
291
|
-
@AutoConfiguration
|
|
292
|
-
@ConditionalOnProperty(prefix = "apigen.graph", name = "enabled", havingValue = "true")
|
|
293
|
-
@EnableConfigurationProperties(GraphProperties.class)
|
|
294
|
-
public class GraphAutoConfiguration {
|
|
295
|
-
|
|
296
|
-
@Bean
|
|
297
|
-
@ConditionalOnProperty(prefix = "apigen.graph", name = "provider", havingValue = "neo4j")
|
|
298
|
-
public Neo4jClient neo4jClient(Driver driver) {
|
|
299
|
-
return Neo4jClient.create(driver);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
@Bean
|
|
303
|
-
@ConditionalOnMissingBean
|
|
304
|
-
public GraphService graphService(
|
|
305
|
-
@Autowired(required = false) UserNodeRepository neo4jRepo,
|
|
306
|
-
@Autowired(required = false) NeptuneGraphService neptuneService,
|
|
307
|
-
@Autowired(required = false) ArangoUserRepository arangoRepo) {
|
|
308
|
-
// Factory based on available beans
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
## Testing with TestContainers
|
|
314
|
-
|
|
315
|
-
```java
|
|
316
|
-
@SpringBootTest
|
|
317
|
-
@Testcontainers
|
|
318
|
-
class GraphServiceIT {
|
|
319
|
-
|
|
320
|
-
@Container
|
|
321
|
-
@ServiceConnection
|
|
322
|
-
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5")
|
|
323
|
-
.withAdminPassword("password");
|
|
324
|
-
|
|
325
|
-
@Autowired
|
|
326
|
-
private GraphService graphService;
|
|
327
|
-
|
|
328
|
-
@Autowired
|
|
329
|
-
private UserNodeRepository userRepository;
|
|
330
|
-
|
|
331
|
-
@BeforeEach
|
|
332
|
-
void setUp() {
|
|
333
|
-
userRepository.deleteAll();
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
@Test
|
|
337
|
-
void shouldFindFriendsOfFriends() {
|
|
338
|
-
// Create graph: Alice -> Bob -> Charlie
|
|
339
|
-
UserNode alice = userRepository.save(new UserNode("Alice"));
|
|
340
|
-
UserNode bob = userRepository.save(new UserNode("Bob"));
|
|
341
|
-
UserNode charlie = userRepository.save(new UserNode("Charlie"));
|
|
342
|
-
|
|
343
|
-
graphService.createFollowRelationship(alice.getExternalId(), bob.getExternalId());
|
|
344
|
-
graphService.createFollowRelationship(bob.getExternalId(), charlie.getExternalId());
|
|
345
|
-
|
|
346
|
-
List<UserNode> fof = userRepository.findFriendsOfFriends(
|
|
347
|
-
alice.getExternalId(), 10);
|
|
348
|
-
|
|
349
|
-
assertThat(fof).hasSize(1);
|
|
350
|
-
assertThat(fof.get(0).getName()).isEqualTo("Charlie");
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
## Related Skills
|
|
356
|
-
|
|
357
|
-
- `graph-databases`: Graph database concepts
|
|
358
|
-
- `spring-boot-4`: Spring Boot 4.0 patterns
|
|
359
|
-
- `testcontainers`: Integration testing
|
|
360
|
-
|
|
361
|
-
|