javi-forge 1.2.0 → 1.3.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 +20 -8
- package/package.json +1 -1
- 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/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
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: search-spring
|
|
3
|
-
description: >
|
|
4
|
-
Spring Boot search integration. Elasticsearch, Meilisearch, Typesense, Algolia.
|
|
5
|
-
Trigger: apigen-search, SearchService, ElasticsearchRepository, indexing
|
|
6
|
-
tools:
|
|
7
|
-
- Read
|
|
8
|
-
- Write
|
|
9
|
-
- Edit
|
|
10
|
-
- Bash
|
|
11
|
-
- Grep
|
|
12
|
-
metadata:
|
|
13
|
-
author: apigen-team
|
|
14
|
-
version: "1.0"
|
|
15
|
-
tags: [search, spring-boot, elasticsearch, java]
|
|
16
|
-
scope: ["apigen-search/**"]
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
# Search Spring Boot (apigen-search)
|
|
20
|
-
|
|
21
|
-
## Configuration
|
|
22
|
-
|
|
23
|
-
```yaml
|
|
24
|
-
apigen:
|
|
25
|
-
search:
|
|
26
|
-
enabled: true
|
|
27
|
-
provider: elasticsearch # elasticsearch, meilisearch, typesense, algolia
|
|
28
|
-
|
|
29
|
-
elasticsearch:
|
|
30
|
-
uris: ${ELASTICSEARCH_URIS:http://localhost:9200}
|
|
31
|
-
username: ${ES_USERNAME:}
|
|
32
|
-
password: ${ES_PASSWORD:}
|
|
33
|
-
connection-timeout: 5s
|
|
34
|
-
socket-timeout: 30s
|
|
35
|
-
|
|
36
|
-
meilisearch:
|
|
37
|
-
host: ${MEILISEARCH_HOST:http://localhost:7700}
|
|
38
|
-
api-key: ${MEILISEARCH_API_KEY:}
|
|
39
|
-
|
|
40
|
-
typesense:
|
|
41
|
-
host: ${TYPESENSE_HOST:localhost}
|
|
42
|
-
port: 8108
|
|
43
|
-
protocol: http
|
|
44
|
-
api-key: ${TYPESENSE_API_KEY:}
|
|
45
|
-
|
|
46
|
-
algolia:
|
|
47
|
-
application-id: ${ALGOLIA_APP_ID}
|
|
48
|
-
api-key: ${ALGOLIA_API_KEY}
|
|
49
|
-
search-key: ${ALGOLIA_SEARCH_KEY}
|
|
50
|
-
|
|
51
|
-
indexing:
|
|
52
|
-
auto-index: true
|
|
53
|
-
batch-size: 100
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Document Mapping (Elasticsearch)
|
|
57
|
-
|
|
58
|
-
```java
|
|
59
|
-
@Document(indexName = "products")
|
|
60
|
-
@Setting(settingPath = "elasticsearch/product-settings.json")
|
|
61
|
-
public class ProductDocument {
|
|
62
|
-
|
|
63
|
-
@Id
|
|
64
|
-
private String id;
|
|
65
|
-
|
|
66
|
-
@Field(type = FieldType.Text, analyzer = "standard")
|
|
67
|
-
private String name;
|
|
68
|
-
|
|
69
|
-
@Field(type = FieldType.Text, analyzer = "standard")
|
|
70
|
-
private String description;
|
|
71
|
-
|
|
72
|
-
@Field(type = FieldType.Keyword)
|
|
73
|
-
private String category;
|
|
74
|
-
|
|
75
|
-
@Field(type = FieldType.Keyword)
|
|
76
|
-
private String brand;
|
|
77
|
-
|
|
78
|
-
@Field(type = FieldType.Double)
|
|
79
|
-
private BigDecimal price;
|
|
80
|
-
|
|
81
|
-
@Field(type = FieldType.Float)
|
|
82
|
-
private Float rating;
|
|
83
|
-
|
|
84
|
-
@Field(type = FieldType.Integer)
|
|
85
|
-
private Integer reviewCount;
|
|
86
|
-
|
|
87
|
-
@Field(type = FieldType.Keyword)
|
|
88
|
-
private List<String> tags;
|
|
89
|
-
|
|
90
|
-
@Field(type = FieldType.Date, format = DateFormat.date_time)
|
|
91
|
-
private Instant createdAt;
|
|
92
|
-
|
|
93
|
-
@Field(type = FieldType.Dense_Vector, dims = 128)
|
|
94
|
-
private float[] embedding; // For semantic search
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Repository Layer
|
|
99
|
-
|
|
100
|
-
```java
|
|
101
|
-
public interface ProductSearchRepository
|
|
102
|
-
extends ElasticsearchRepository<ProductDocument, String> {
|
|
103
|
-
|
|
104
|
-
List<ProductDocument> findByNameContaining(String name);
|
|
105
|
-
|
|
106
|
-
List<ProductDocument> findByCategoryAndPriceRange(
|
|
107
|
-
String category, BigDecimal minPrice, BigDecimal maxPrice);
|
|
108
|
-
|
|
109
|
-
@Query("""
|
|
110
|
-
{
|
|
111
|
-
"bool": {
|
|
112
|
-
"must": [
|
|
113
|
-
{"match": {"name": "?0"}}
|
|
114
|
-
],
|
|
115
|
-
"filter": [
|
|
116
|
-
{"term": {"category": "?1"}}
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
""")
|
|
121
|
-
Page<ProductDocument> searchByNameAndCategory(
|
|
122
|
-
String query, String category, Pageable pageable);
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Search Service
|
|
127
|
-
|
|
128
|
-
```java
|
|
129
|
-
@Service
|
|
130
|
-
@RequiredArgsConstructor
|
|
131
|
-
public class SearchService {
|
|
132
|
-
|
|
133
|
-
private final ElasticsearchOperations esOperations;
|
|
134
|
-
private final ProductSearchRepository repository;
|
|
135
|
-
private final MeterRegistry meterRegistry;
|
|
136
|
-
|
|
137
|
-
public SearchResult<ProductDocument> search(SearchRequest request) {
|
|
138
|
-
Timer.Sample sample = Timer.start(meterRegistry);
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
NativeQuery query = buildQuery(request);
|
|
142
|
-
SearchHits<ProductDocument> hits = esOperations.search(query, ProductDocument.class);
|
|
143
|
-
|
|
144
|
-
List<ProductDocument> results = hits.getSearchHits().stream()
|
|
145
|
-
.map(SearchHit::getContent)
|
|
146
|
-
.toList();
|
|
147
|
-
|
|
148
|
-
Map<String, List<FacetValue>> facets = extractFacets(hits);
|
|
149
|
-
|
|
150
|
-
return SearchResult.<ProductDocument>builder()
|
|
151
|
-
.results(results)
|
|
152
|
-
.totalHits(hits.getTotalHits())
|
|
153
|
-
.facets(facets)
|
|
154
|
-
.took(hits.getPointInTimeId())
|
|
155
|
-
.build();
|
|
156
|
-
|
|
157
|
-
} finally {
|
|
158
|
-
sample.stop(meterRegistry.timer("search.query",
|
|
159
|
-
"index", "products"));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private NativeQuery buildQuery(SearchRequest request) {
|
|
164
|
-
BoolQuery.Builder boolQuery = new BoolQuery.Builder();
|
|
165
|
-
|
|
166
|
-
// Full-text search
|
|
167
|
-
if (StringUtils.hasText(request.getQuery())) {
|
|
168
|
-
boolQuery.must(MultiMatchQuery.of(mm -> mm
|
|
169
|
-
.query(request.getQuery())
|
|
170
|
-
.fields("name^3", "description", "tags")
|
|
171
|
-
.fuzziness("AUTO")
|
|
172
|
-
)._toQuery());
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Filters
|
|
176
|
-
if (request.getCategory() != null) {
|
|
177
|
-
boolQuery.filter(TermQuery.of(t -> t
|
|
178
|
-
.field("category")
|
|
179
|
-
.value(request.getCategory())
|
|
180
|
-
)._toQuery());
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (request.getMinPrice() != null || request.getMaxPrice() != null) {
|
|
184
|
-
boolQuery.filter(RangeQuery.of(r -> {
|
|
185
|
-
r.field("price");
|
|
186
|
-
if (request.getMinPrice() != null) r.gte(JsonData.of(request.getMinPrice()));
|
|
187
|
-
if (request.getMaxPrice() != null) r.lte(JsonData.of(request.getMaxPrice()));
|
|
188
|
-
return r;
|
|
189
|
-
})._toQuery());
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Build aggregations for facets
|
|
193
|
-
return NativeQuery.builder()
|
|
194
|
-
.withQuery(boolQuery.build()._toQuery())
|
|
195
|
-
.withAggregation("categories", Aggregation.of(a -> a
|
|
196
|
-
.terms(t -> t.field("category").size(20))))
|
|
197
|
-
.withAggregation("brands", Aggregation.of(a -> a
|
|
198
|
-
.terms(t -> t.field("brand").size(20))))
|
|
199
|
-
.withAggregation("price_ranges", Aggregation.of(a -> a
|
|
200
|
-
.range(r -> r.field("price")
|
|
201
|
-
.ranges(
|
|
202
|
-
new RangeAggregationRange.Builder().to("50").key("under_50").build(),
|
|
203
|
-
new RangeAggregationRange.Builder().from("50").to("100").key("50_to_100").build(),
|
|
204
|
-
new RangeAggregationRange.Builder().from("100").key("over_100").build()
|
|
205
|
-
))))
|
|
206
|
-
.withPageable(PageRequest.of(request.getPage(), request.getSize()))
|
|
207
|
-
.withSort(buildSort(request.getSortBy()))
|
|
208
|
-
.build();
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
public List<String> autocomplete(String prefix, int limit) {
|
|
212
|
-
NativeQuery query = NativeQuery.builder()
|
|
213
|
-
.withQuery(PrefixQuery.of(p -> p
|
|
214
|
-
.field("name.autocomplete")
|
|
215
|
-
.value(prefix.toLowerCase())
|
|
216
|
-
)._toQuery())
|
|
217
|
-
.withFields("name")
|
|
218
|
-
.withPageable(PageRequest.of(0, limit))
|
|
219
|
-
.build();
|
|
220
|
-
|
|
221
|
-
return esOperations.search(query, ProductDocument.class)
|
|
222
|
-
.getSearchHits().stream()
|
|
223
|
-
.map(hit -> hit.getContent().getName())
|
|
224
|
-
.distinct()
|
|
225
|
-
.toList();
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
## Meilisearch Provider
|
|
231
|
-
|
|
232
|
-
```java
|
|
233
|
-
@Component
|
|
234
|
-
@ConditionalOnProperty(prefix = "apigen.search", name = "provider", havingValue = "meilisearch")
|
|
235
|
-
public class MeilisearchSearchProvider implements SearchProvider {
|
|
236
|
-
|
|
237
|
-
private final Client client;
|
|
238
|
-
|
|
239
|
-
@Override
|
|
240
|
-
public SearchResult<Map<String, Object>> search(String index, SearchRequest request) {
|
|
241
|
-
Index idx = client.index(index);
|
|
242
|
-
|
|
243
|
-
SearchRequest meilisearchRequest = new SearchRequest(request.getQuery())
|
|
244
|
-
.setOffset(request.getPage() * request.getSize())
|
|
245
|
-
.setLimit(request.getSize())
|
|
246
|
-
.setFilter(buildFilters(request))
|
|
247
|
-
.setFacets(new String[]{"category", "brand"})
|
|
248
|
-
.setAttributesToHighlight(new String[]{"name", "description"});
|
|
249
|
-
|
|
250
|
-
Searchable results = idx.search(meilisearchRequest);
|
|
251
|
-
|
|
252
|
-
return SearchResult.builder()
|
|
253
|
-
.results(results.getHits())
|
|
254
|
-
.totalHits(results.getEstimatedTotalHits())
|
|
255
|
-
.facets(results.getFacetDistribution())
|
|
256
|
-
.build();
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
@Override
|
|
260
|
-
public void index(String indexName, String id, Map<String, Object> document) {
|
|
261
|
-
client.index(indexName).addDocuments(
|
|
262
|
-
new Gson().toJson(List.of(document)), "id");
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
## Indexing Service
|
|
268
|
-
|
|
269
|
-
```java
|
|
270
|
-
@Service
|
|
271
|
-
@RequiredArgsConstructor
|
|
272
|
-
public class IndexingService {
|
|
273
|
-
|
|
274
|
-
private final ProductRepository productRepository;
|
|
275
|
-
private final ElasticsearchOperations esOperations;
|
|
276
|
-
private final SearchProperties props;
|
|
277
|
-
|
|
278
|
-
@Async
|
|
279
|
-
@Scheduled(cron = "${apigen.search.indexing.cron:0 0 2 * * *}")
|
|
280
|
-
public void fullReindex() {
|
|
281
|
-
log.info("Starting full reindex");
|
|
282
|
-
|
|
283
|
-
String newIndex = "products_" + Instant.now().toEpochMilli();
|
|
284
|
-
|
|
285
|
-
// Create new index
|
|
286
|
-
esOperations.indexOps(ProductDocument.class).create();
|
|
287
|
-
|
|
288
|
-
// Index in batches
|
|
289
|
-
int page = 0;
|
|
290
|
-
Page<Product> products;
|
|
291
|
-
do {
|
|
292
|
-
products = productRepository.findAll(
|
|
293
|
-
PageRequest.of(page, props.getIndexing().getBatchSize()));
|
|
294
|
-
|
|
295
|
-
List<ProductDocument> documents = products.getContent().stream()
|
|
296
|
-
.map(this::toDocument)
|
|
297
|
-
.toList();
|
|
298
|
-
|
|
299
|
-
esOperations.save(documents);
|
|
300
|
-
page++;
|
|
301
|
-
} while (products.hasNext());
|
|
302
|
-
|
|
303
|
-
// Switch alias
|
|
304
|
-
switchAlias("products", newIndex);
|
|
305
|
-
|
|
306
|
-
log.info("Full reindex completed: {} documents", products.getTotalElements());
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
|
310
|
-
public void onProductChange(ProductChangedEvent event) {
|
|
311
|
-
if (props.getIndexing().isAutoIndex()) {
|
|
312
|
-
switch (event.getType()) {
|
|
313
|
-
case CREATED, UPDATED -> indexProduct(event.getProduct());
|
|
314
|
-
case DELETED -> deleteProduct(event.getProductId());
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
private void indexProduct(Product product) {
|
|
320
|
-
ProductDocument document = toDocument(product);
|
|
321
|
-
esOperations.save(document);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
## REST API
|
|
327
|
-
|
|
328
|
-
```java
|
|
329
|
-
@RestController
|
|
330
|
-
@RequestMapping("/api/search")
|
|
331
|
-
@RequiredArgsConstructor
|
|
332
|
-
public class SearchController {
|
|
333
|
-
|
|
334
|
-
private final SearchService searchService;
|
|
335
|
-
|
|
336
|
-
@GetMapping
|
|
337
|
-
public SearchResult<ProductDTO> search(
|
|
338
|
-
@RequestParam(required = false) String q,
|
|
339
|
-
@RequestParam(required = false) String category,
|
|
340
|
-
@RequestParam(required = false) BigDecimal minPrice,
|
|
341
|
-
@RequestParam(required = false) BigDecimal maxPrice,
|
|
342
|
-
@RequestParam(defaultValue = "0") int page,
|
|
343
|
-
@RequestParam(defaultValue = "20") int size,
|
|
344
|
-
@RequestParam(defaultValue = "relevance") String sortBy) {
|
|
345
|
-
|
|
346
|
-
SearchRequest request = SearchRequest.builder()
|
|
347
|
-
.query(q)
|
|
348
|
-
.category(category)
|
|
349
|
-
.minPrice(minPrice)
|
|
350
|
-
.maxPrice(maxPrice)
|
|
351
|
-
.page(page)
|
|
352
|
-
.size(size)
|
|
353
|
-
.sortBy(sortBy)
|
|
354
|
-
.build();
|
|
355
|
-
|
|
356
|
-
return searchService.search(request)
|
|
357
|
-
.map(this::toDTO);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
@GetMapping("/autocomplete")
|
|
361
|
-
public List<String> autocomplete(
|
|
362
|
-
@RequestParam String q,
|
|
363
|
-
@RequestParam(defaultValue = "10") int limit) {
|
|
364
|
-
return searchService.autocomplete(q, limit);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
## Related Skills
|
|
370
|
-
|
|
371
|
-
- `search-concepts`: Search engine concepts
|
|
372
|
-
- `spring-boot-4`: Spring Boot 4.0 patterns
|
|
373
|
-
- `testcontainers`: Integration testing
|
|
374
|
-
|
|
375
|
-
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: spring-boot-4
|
|
3
|
-
description: >
|
|
4
|
-
Spring Boot 4.0 patterns, Jakarta EE 10, Java 21+ features, migrations.
|
|
5
|
-
Trigger: Spring Boot 4, Jakarta, Java 21, virtual threads, migration
|
|
6
|
-
tools:
|
|
7
|
-
- Read
|
|
8
|
-
- Write
|
|
9
|
-
- Edit
|
|
10
|
-
- Grep
|
|
11
|
-
metadata:
|
|
12
|
-
author: apigen-team
|
|
13
|
-
version: "1.0"
|
|
14
|
-
tags: [spring-boot, jakarta, java21]
|
|
15
|
-
scope: ["**/src/main/java/**"]
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
# Spring Boot 4.0 Patterns
|
|
19
|
-
|
|
20
|
-
## Quick Reference
|
|
21
|
-
|
|
22
|
-
### Jakarta EE Migration
|
|
23
|
-
|
|
24
|
-
| Antes (javax) | Ahora (jakarta) |
|
|
25
|
-
|---------------|-----------------|
|
|
26
|
-
| `javax.persistence.*` | `jakarta.persistence.*` |
|
|
27
|
-
| `javax.validation.*` | `jakarta.validation.*` |
|
|
28
|
-
| `javax.servlet.*` | `jakarta.servlet.*` |
|
|
29
|
-
| `javax.annotation.*` | `jakarta.annotation.*` |
|
|
30
|
-
|
|
31
|
-
### Security Configuration
|
|
32
|
-
|
|
33
|
-
```java
|
|
34
|
-
// ✅ Spring Security 6.4+
|
|
35
|
-
@Bean
|
|
36
|
-
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
|
37
|
-
return http
|
|
38
|
-
.csrf(csrf -> csrf.disable())
|
|
39
|
-
.sessionManagement(session -> session
|
|
40
|
-
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
41
|
-
.authorizeHttpRequests(auth -> auth
|
|
42
|
-
.requestMatchers("/api/public/**").permitAll()
|
|
43
|
-
.requestMatchers("/api/admin/**").hasRole("ADMIN")
|
|
44
|
-
.anyRequest().authenticated())
|
|
45
|
-
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
|
|
46
|
-
.build();
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Virtual Threads
|
|
51
|
-
|
|
52
|
-
```yaml
|
|
53
|
-
# application.yml
|
|
54
|
-
spring:
|
|
55
|
-
threads:
|
|
56
|
-
virtual:
|
|
57
|
-
enabled: true
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Records as DTOs
|
|
61
|
-
|
|
62
|
-
```java
|
|
63
|
-
public record UserDTO(
|
|
64
|
-
@NotNull UUID id,
|
|
65
|
-
@NotBlank String name,
|
|
66
|
-
@Email String email
|
|
67
|
-
) {}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Problem Details
|
|
71
|
-
|
|
72
|
-
```java
|
|
73
|
-
@ExceptionHandler(EntityNotFoundException.class)
|
|
74
|
-
public ProblemDetail handleNotFound(EntityNotFoundException ex) {
|
|
75
|
-
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
|
|
76
|
-
HttpStatus.NOT_FOUND, ex.getMessage());
|
|
77
|
-
problem.setTitle("Entity Not Found");
|
|
78
|
-
return problem;
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### HTTP Interface (Declarative Client)
|
|
83
|
-
|
|
84
|
-
```java
|
|
85
|
-
public interface UserClient {
|
|
86
|
-
@GetExchange("/users/{id}")
|
|
87
|
-
User getUser(@PathVariable UUID id);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
@Bean
|
|
91
|
-
public UserClient userClient(RestClient.Builder builder) {
|
|
92
|
-
return HttpServiceProxyFactory
|
|
93
|
-
.builderFor(RestClientAdapter.create(builder.baseUrl("http://api").build()))
|
|
94
|
-
.build()
|
|
95
|
-
.createClient(UserClient.class);
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### @ConfigurationProperties with Records
|
|
100
|
-
|
|
101
|
-
```java
|
|
102
|
-
@ConfigurationProperties(prefix = "app")
|
|
103
|
-
public record AppProperties(
|
|
104
|
-
String name,
|
|
105
|
-
Duration timeout,
|
|
106
|
-
CacheProperties cache
|
|
107
|
-
) {
|
|
108
|
-
public record CacheProperties(boolean enabled, Duration ttl) {}
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Observability
|
|
113
|
-
|
|
114
|
-
```java
|
|
115
|
-
@Observed(name = "user.service", contextualName = "findUser")
|
|
116
|
-
public User findById(UUID id) {
|
|
117
|
-
return repository.findById(id).orElseThrow();
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### TestContainers with @ServiceConnection
|
|
122
|
-
|
|
123
|
-
```java
|
|
124
|
-
@SpringBootTest
|
|
125
|
-
@Testcontainers
|
|
126
|
-
class UserRepositoryIT {
|
|
127
|
-
|
|
128
|
-
@Container
|
|
129
|
-
@ServiceConnection
|
|
130
|
-
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
|
|
131
|
-
|
|
132
|
-
// Auto-configures datasource from container
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Anti-Patterns
|
|
137
|
-
|
|
138
|
-
### ❌ WebSecurityConfigurerAdapter (deprecated)
|
|
139
|
-
|
|
140
|
-
```java
|
|
141
|
-
// ❌ No usar
|
|
142
|
-
public class Config extends WebSecurityConfigurerAdapter { }
|
|
143
|
-
|
|
144
|
-
// ✅ Usar SecurityFilterChain bean
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### ❌ antMatchers (removed)
|
|
148
|
-
|
|
149
|
-
```java
|
|
150
|
-
// ❌ No existe
|
|
151
|
-
.antMatchers("/api/**")
|
|
152
|
-
|
|
153
|
-
// ✅ Usar
|
|
154
|
-
.requestMatchers("/api/**")
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### ❌ javax imports
|
|
158
|
-
|
|
159
|
-
```java
|
|
160
|
-
// ❌ Ya no funciona
|
|
161
|
-
import javax.persistence.Entity;
|
|
162
|
-
|
|
163
|
-
// ✅ Jakarta
|
|
164
|
-
import jakarta.persistence.Entity;
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## Related Skills
|
|
168
|
-
|
|
169
|
-
- `apigen-architecture`: Arquitectura APiGen
|
|
170
|
-
- `testcontainers`: Testing integration
|
|
171
|
-
- `gradle-multimodule`: Build config
|
|
172
|
-
|