feat: show distinct attribute keys in cmd-k Attributes tab
All checks were successful
CI / build (push) Successful in 1m58s
CI / cleanup-branch (push) Has been skipped
CI / docker (push) Successful in 1m46s
CI / deploy (push) Successful in 47s
CI / deploy-feature (push) Has been skipped

Add GET /search/attributes/keys endpoint that queries distinct
attribute key names from ClickHouse using JSONExtractKeys. Attribute
keys appear in the cmd-k Attributes tab alongside attribute value
matches from exchange results.

- SearchIndex.distinctAttributeKeys() interface method
- ClickHouseSearchIndex implementation using arrayJoin(JSONExtractKeys)
- SearchController /attributes/keys endpoint
- useAttributeKeys() React Query hook
- buildSearchData includes attribute keys as 'attribute' category items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-01 21:39:27 +02:00
parent 9057981cf7
commit f3feaddbfe
6 changed files with 62 additions and 3 deletions

View File

@@ -172,6 +172,12 @@ public class SearchController {
return ResponseEntity.ok(searchService.punchcard(from, to, application));
}
@GetMapping("/attributes/keys")
@Operation(summary = "Distinct attribute key names across all executions")
public ResponseEntity<List<String>> attributeKeys() {
return ResponseEntity.ok(searchService.distinctAttributeKeys());
}
@GetMapping("/errors/top")
@Operation(summary = "Top N errors with velocity trend")
public ResponseEntity<List<TopError>> topErrors(

View File

@@ -305,6 +305,21 @@ public class ClickHouseSearchIndex implements SearchIndex {
.replace("_", "\\_");
}
@Override
public List<String> distinctAttributeKeys() {
try {
return jdbc.queryForList("""
SELECT DISTINCT arrayJoin(JSONExtractKeys(attributes)) AS attr_key
FROM executions FINAL
WHERE tenant_id = 'default' AND attributes != '' AND attributes != '{}'
ORDER BY attr_key
""", String.class);
} catch (Exception e) {
log.error("Failed to query distinct attribute keys", e);
return List.of();
}
}
private static Map<String, String> parseAttributesJson(String json) {
if (json == null || json.isBlank()) return null;
try {