# Phase 2: Transaction Search + Diagrams - Research
**Researched:** 2026-03-11
**Domain:** ClickHouse querying, full-text search, SVG diagram rendering, REST API design
**Confidence:** HIGH
## Summary
Phase 2 transforms the ingestion-only server into a queryable observability platform. The work divides into three domains: (1) structured search with ClickHouse WHERE clauses over the existing `route_executions` table plus schema extensions for exchange snapshot data, (2) full-text search using ClickHouse's `tokenbf_v1` skip indexes (the text index GA feature requires ClickHouse 26.2+ and we run 25.3), and (3) route diagram retrieval and server-side SVG rendering using Eclipse ELK for layout and JFreeSVG for output.
The existing Phase 1 code provides a solid foundation: `ClickHouseExecutionRepository` already flattens processor trees into parallel arrays, `ClickHouseDiagramRepository` already stores diagrams with SHA-256 content-hash deduplication, and `AbstractClickHouseIT` provides the Testcontainers base class. Phase 2 extends these with query methods, schema additions for exchange data and tree reconstruction metadata, and new search/diagram REST controllers.
**Primary recommendation:** Extend the existing repository interfaces with query methods, add a `SearchService` abstraction in core (for future OpenSearch swap), store exchange snapshot data as JSON strings in new columns on `route_executions`, and use Eclipse ELK 0.11.0 + JFreeSVG 5.0.7 for diagram rendering.
## User Constraints (from CONTEXT.md)
### Locked Decisions
- Both GET and POST endpoints for search: GET /api/v1/search/executions for basic filters, POST for advanced filters including full-text and per-field targeting
- Response envelope: `{ "data": [...], "total": N, "offset": 0, "limit": 50 }`
- Substring matching (LIKE '%term%') for full-text search -- not token-based only
- Global `text` parameter searches all text fields; optional per-field targeting: textInBody, textInHeaders, textInErrors
- Search service interface designed for future OpenSearch swap
- Nested JSON tree returned by server for transaction detail -- server reconstructs processor tree from flat storage
- Add depth and parent index arrays to ClickHouse schema (processor_depths, processor_parent_indexes) -- populated at ingestion time
- Exchange snapshot data fetched separately per processor -- not inlined in detail response
- Diagram accessed via separate endpoint; detail response includes diagram content hash for linking
- Both SVG and JSON layout formats via Accept header content negotiation
- Top-to-bottom node layout flow
- Nested processors in swimlanes to highlight nesting/scope
- Color-coded node types matching route-diagram-example.html style
- Store everything the agent sends -- no server-side truncation
- API designed to support future cmd+k cross-entity search UI
### Claude's Discretion
- Pagination implementation details (offset/limit vs cursor)
- ClickHouse schema extension approach for exchange snapshot storage
- SVG rendering library choice
- Layout algorithm for diagram node positioning
- Search service abstraction layer design
### Deferred Ideas (OUT OF SCOPE)
- Cursor-based pagination (ASRCH-01) -- v2
- Saved search queries (ASRCH-02) -- v2
- Web UI with cmd+k search overlay -- v2
- Execution overlay on diagrams -- UI responsibility
- OpenSearch for full-text search -- evaluate after Phase 2
## Phase Requirements
| ID | Description | Research Support |
|----|-------------|-----------------|
| SRCH-01 (#7) | Search transactions by execution status (COMPLETED, FAILED, RUNNING) | WHERE clause on `status` column (LowCardinality, in ORDER BY) -- highly efficient |
| SRCH-02 (#8) | Search transactions by date/time range | WHERE clause on `start_time` (in ORDER BY, partition key) -- primary index range scan |
| SRCH-03 (#9) | Search transactions by duration range (min/max ms) | WHERE clause on `duration_ms` -- simple range filter |
| SRCH-04 (#10) | Search by correlationId for cross-instance correlation | WHERE + bloom_filter skip index on `correlation_id` (already exists) |
| SRCH-05 (#11) | Full-text search across bodies, headers, errors, stack traces | LIKE '%term%' on text columns + tokenbf_v1 skip indexes; schema extension needed for body/header storage |
| SRCH-06 (#12) | Transaction detail with nested processor execution tree | Reconstruct tree from parallel arrays using processor_depths + processor_parent_indexes; ARRAY JOIN query |
| DIAG-01 (#20) | Content-addressable diagram versioning | Already implemented: ReplacingMergeTree with SHA-256 content_hash |
| DIAG-02 (#21) | Transaction links to active diagram version | Add `diagram_content_hash` column to `route_executions`; populated at ingestion from latest diagram |
| DIAG-03 (#22) | Server renders route diagrams from stored definitions | Eclipse ELK for layout + JFreeSVG for SVG output; JSON layout alternative via Accept header |
## Standard Stack
### Core (already in project)
| Library | Version | Purpose | Why Standard |
|---------|---------|---------|--------------|
| Spring Boot | 3.4.3 | Web framework, DI, JdbcTemplate | Already established in Phase 1 |
| ClickHouse JDBC | 0.9.7 | Database driver | Already established in Phase 1 |
| Jackson | 2.17.3 | JSON serialization | Already established in Phase 1 |
| springdoc-openapi | 2.8.6 | API documentation | Already established in Phase 1 |
| Testcontainers | 2.0.3 | ClickHouse integration tests | Already established in Phase 1 |
### New for Phase 2
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| Eclipse ELK Core | 0.11.0 | Graph layout algorithm (layered/hierarchical) | Diagram node positioning |
| Eclipse ELK Layered | 0.11.0 | Sugiyama-style top-to-bottom layout | The actual layout algorithm |
| JFreeSVG | 5.0.7 | Programmatic SVG generation via Graphics2D API | Rendering diagram to SVG string |
### Alternatives Considered
| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| Eclipse ELK | Manual layout algorithm | ELK handles edge crossing minimization, node spacing, layer assignment -- non-trivial to implement correctly |
| JFreeSVG | Apache Batik | Batik is 98x more memory than JSVG; JFreeSVG is lightweight, 5x faster, zero dependencies beyond JDK 11+ |
| JFreeSVG | Manual SVG string building | JFreeSVG handles SVG escaping, coordinate systems, text metrics correctly; manual strings are error-prone |
| Separate exchange table | JSON columns on route_executions | Separate table adds JOINs; JSON strings on the main table keep queries simple and align with "fetch snapshot separately" pattern |
**Installation (new dependencies for app module pom.xml):**
```xml
org.eclipse.elkorg.eclipse.elk.core0.11.0org.eclipse.elkorg.eclipse.elk.alg.layered0.11.0org.jfreeorg.jfree.svg5.0.7
```
## Architecture Patterns
### Recommended Project Structure (additions for Phase 2)
```
cameleer-server-core/src/main/java/com/cameleer/server/core/
├── search/
│ ├── SearchService.java # Orchestrates search, delegates to SearchEngine
│ ├── SearchEngine.java # Interface for search backends (ClickHouse now, OpenSearch later)
│ ├── SearchRequest.java # Immutable search criteria record
│ └── SearchResult.java # Paginated result envelope record
├── detail/
│ ├── DetailService.java # Reconstructs execution tree from flat data
│ └── ExecutionDetail.java # Rich detail model with nested tree
├── diagram/
│ └── DiagramRenderer.java # Interface: render RouteGraph -> SVG or JSON layout
└── storage/
├── ExecutionRepository.java # Extended with query methods
└── DiagramRepository.java # Extended with lookup methods
cameleer-server-app/src/main/java/com/cameleer/server/app/
├── controller/
│ ├── SearchController.java # GET + POST /api/v1/search/executions
│ ├── DetailController.java # GET /api/v1/executions/{id}
│ └── DiagramRenderController.java # GET /api/v1/diagrams/{hash} with content negotiation
├── search/
│ └── ClickHouseSearchEngine.java # SearchEngine impl using JdbcTemplate
├── diagram/
│ ├── ElkDiagramRenderer.java # DiagramRenderer impl: ELK layout + JFreeSVG
│ └── DiagramLayoutResult.java # JSON layout format DTO
└── storage/
└── ClickHouseExecutionRepository.java # Extended with query + detail methods
```
### Pattern 1: Search Engine Abstraction (for future OpenSearch swap)
**What:** Interface in core module, ClickHouse implementation in app module
**When to use:** All search operations go through this interface
**Example:**
```java
// Core module: search engine interface
public interface SearchEngine {
SearchResult search(SearchRequest request);
long count(SearchRequest request);
}
// Core module: search service orchestrates
public class SearchService {
private final SearchEngine engine;
public SearchResult search(SearchRequest request) {
return engine.search(request);
}
}
// App module: ClickHouse implementation
@Repository
public class ClickHouseSearchEngine implements SearchEngine {
private final JdbcTemplate jdbcTemplate;
@Override
public SearchResult search(SearchRequest request) {
// Build dynamic WHERE clause from SearchRequest
// Execute against route_executions table
}
}
```
### Pattern 2: Dynamic SQL Query Building
**What:** Build WHERE clauses from optional filter parameters
**When to use:** Search queries with combinable filters
**Example:**
```java
public SearchResult search(SearchRequest req) {
var conditions = new ArrayList();
var params = new ArrayList