miroir/notes/miroir-9dj.3.md
jedarden 6d91e81b6e P2.3 Search read path: scatter-gather + merge + group selection
Implemented POST /indexes/{uid}/search with:
- Group selection: query_seq % RG (plan §2)
- Intra-group covering set (plan §4 covering_set)
- Fan-out to covering set with showRankingScore: true
- Each node returns offset + limit results (coordinator pagination)
- Merge with RRF or Score-based strategy (P1.4)
- Unavailable shard policies: partial, error, fallback
- X-Miroir-Degraded header with shard IDs

All 10 acceptance tests pass:
- Unique-keyword deduplication
- Facet count aggregation
- Paging consistency
- Node failure handling with RF=2
- Group fallback on full group failure
- Degraded header with shard IDs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 14:02:29 -04:00

93 lines
3.5 KiB
Markdown

# P2.3 Search Read Path Implementation Summary
## Overview
The search read path with scatter-gather + merge + group selection is fully implemented and tested.
## Implementation Components
### 1. Group Selection (`query_group`)
- **Location**: `crates/miroir-core/src/router.rs:98-103`
- **Logic**: `query_seq % RG` (round-robin through replica groups)
- **Status**: ✅ Implemented
### 2. Intra-group Covering Set (`covering_set`)
- **Location**: `crates/miroir-core/src/router.rs:105-116`
- **Logic**: Uses rendezvous hash to select RF replicas per shard
- **Rotation**: Rotates through replicas using `query_seq`
- **Status**: ✅ Implemented
### 3. Scatter Planning (`plan_search_scatter`)
- **Location**: `crates/miroir-core/src/scatter.rs:387-431`
- **Logic**: Builds `ScatterPlan` mapping shards to nodes
- **Features**:
- Supports replica_selector for adaptive selection
- Version floor filtering (plan §13.5)
- Session pinning support (plan §13.6)
- **Status**: ✅ Implemented
### 4. Execute Scatter (`execute_scatter`)
- **Location**: `crates/miroir-core/src/scatter.rs:598-742`
- **Logic**: Fans out search requests to all nodes in covering set
- **Features**:
- Parallel requests with `join_all`
- Unavailable shard policies: Partial, Error, Fallback
- Group fallback on failure (plan §2)
- **Status**: ✅ Implemented
### 5. Request Transformation (`SearchRequest::to_node_body`)
- **Location**: `crates/miroir-core/src/scatter.rs:341-376`
- **Logic**: Injects `showRankingScore: true` unconditionally
- **Pagination**: Sets `limit = offset + limit`, `offset = 0`
- **Status**: ✅ Implemented
### 6. Merge (`scatter_gather_search`, `merge`)
- **Location**: `crates/miroir-core/src/scatter.rs:745-785`, `crates/miroir-core/src/merger.rs`
- **Strategies**:
- RRF (Reciprocal Rank Fusion) - default
- Score-based (for OP#4 global-IDF)
- **Features**:
- Deduplication by primary key
- Facet aggregation (sum across shards)
- `estimatedTotalHits` sum
- `processingTimeMs` max
- **Status**: ✅ Implemented
### 7. HTTP Endpoint (`search_handler`)
- **Location**: `crates/miroir-proxy/src/routes/search.rs:163-686`
- **Route**: `POST /search/:index`
- **Features**:
- DFS query-then-fetch (OP#4 global-IDF preflight)
- Session pinning (plan §13.6)
- Query coalescing (plan §13.10)
- X-Miroir-Degraded header
- Settings version headers
- **Status**: ✅ Implemented
### 8. HTTP Client (`HttpClient::search_node`)
- **Location**: `crates/miroir-proxy/src/client.rs:64-136`
- **Logic**: POST to `/indexes/{uid}/search` on Meilisearch nodes
- **Features**:
- Master key authentication
- Timeout handling
- Error handling
- **Status**: ✅ Implemented
## Acceptance Tests (10/10 Passed)
- ✅ Unique-keyword search returns exactly 1 hit (deduplication)
- ✅ Facet counts sum correctly across shards
- ✅ Paging: 5 pages of 10 = single limit=50 order, no dupes/gaps
- ✅ Node down with RF=2: search still covers all shards
- ✅ Group down: search uses fallback, not degraded
- ✅ X-Miroir-Degraded header includes actual shard IDs
- ✅ Full integration test (hits, facets, metadata)
- ✅ showRankingScore injected unconditionally
- ✅ limit is offset + limit for coordinator pagination
- ✅ Degraded header format
## Integration with Phase 5 Features
The scatter plan cleanly separates routing decisions for future Phase 5 features:
- **Hedging** (§13.2): `ScatterPlan.hedging_eligible` flag
- **Adaptive replica selection** (§13.3): `replica_selector` parameter
- **Query coalescing** (§13.10): Integrated in search_handler