Skip to content

Commit 7d3c623

Browse files
add: Serialize to ProbeStats
1 parent 2ebe974 commit 7d3c623

3 files changed

Lines changed: 74 additions & 7 deletions

File tree

src/vector/backend.rs

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl<T: VectorElement> VectorBackend<T> {
141141
}
142142

143143
/// How the probe loop stopped.
144-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
144+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize)]
145145
pub enum ProbeTermination {
146146
/// The filter-effective probe budget reached `max_probe_count` — the
147147
/// probe ceiling.
@@ -158,7 +158,7 @@ pub enum ProbeTermination {
158158
/// touched. Returned by [`VectorBackend::top_n`] alongside the hits.
159159
/// The flat/exact path fills only `exact_rows_read`; every other field
160160
/// is IVF-probe-only.
161-
#[derive(Debug, Default)]
161+
#[derive(Debug, Default, serde::Serialize)]
162162
pub struct ProbeStats {
163163
/// Clusters visited by the probe loop, in probe order. A cluster
164164
/// appears here once we've passed the stop-condition gate for it,
@@ -625,8 +625,9 @@ mod tests {
625625
use crate::schema::{IndexRecordOption, Schema, Term, STORED, STRING};
626626
use crate::vector::tests::{exhaustive_params, TestVectorIndex};
627627
use crate::vector::{
628-
IvfCentroids, IvfClusterer, IvfMatrix, IvfMergeSettings, IvfVectors, VectorClusterStats,
629-
VectorDType, VectorInfo, VectorOptions, VectorStorageFormat,
628+
IvfCentroids, IvfClusterer, IvfMatrix, IvfMergeSettings, IvfVectors,
629+
NeighborhoodGraphSearchMetrics, SearchTerminationReason, VectorClusterStats, VectorDType,
630+
VectorInfo, VectorOptions, VectorStorageFormat,
630631
};
631632
use crate::{Index, IndexWriter, TantivyDocument};
632633

@@ -2341,6 +2342,72 @@ mod tests {
23412342
Ok(())
23422343
}
23432344

2345+
/// `ProbeStats` (and nested routing / optional graph metrics) round-trip
2346+
/// through `serde_json` with the field names callers rely on.
2347+
#[test]
2348+
fn probe_stats_serializes_to_json() {
2349+
let stats = ProbeStats {
2350+
probed_clusters: vec![2, 5],
2351+
candidates_scored: 10,
2352+
vectors_visited: 20,
2353+
pruned_filter: 4,
2354+
pruned_dead: 3,
2355+
pruned_seen: 3,
2356+
postings_row: 1,
2357+
postings_skipped: 1,
2358+
exact_rows_read: 0,
2359+
routing: IvfSearchMetrics {
2360+
visited_count: 7,
2361+
graph: Some(NeighborhoodGraphSearchMetrics {
2362+
visited_count: 7,
2363+
expanded_count: 4,
2364+
edges_scanned: 12,
2365+
evictions: 1,
2366+
result_count: 3,
2367+
termination_reason: SearchTerminationReason::SearchConverged,
2368+
}),
2369+
},
2370+
min_candidates: 5,
2371+
termination: ProbeTermination::Gate,
2372+
};
2373+
2374+
let value = serde_json::to_value(&stats).expect("ProbeStats should serialize to JSON");
2375+
assert_eq!(
2376+
value,
2377+
serde_json::json!({
2378+
"probed_clusters": [2, 5],
2379+
"candidates_scored": 10,
2380+
"vectors_visited": 20,
2381+
"pruned_filter": 4,
2382+
"pruned_dead": 3,
2383+
"pruned_seen": 3,
2384+
"postings_row": 1,
2385+
"postings_skipped": 1,
2386+
"exact_rows_read": 0,
2387+
"routing": {
2388+
"visited_count": 7,
2389+
"graph": {
2390+
"visited_count": 7,
2391+
"expanded_count": 4,
2392+
"edges_scanned": 12,
2393+
"evictions": 1,
2394+
"result_count": 3,
2395+
"termination_reason": "SearchConverged"
2396+
}
2397+
},
2398+
"min_candidates": 5,
2399+
"termination": "Gate"
2400+
})
2401+
);
2402+
2403+
// Exact routing leaves `graph` unset — still must serialize as null.
2404+
let mut exact_routing = stats;
2405+
exact_routing.routing.graph = None;
2406+
let exact_value =
2407+
serde_json::to_value(&exact_routing).expect("ProbeStats should serialize to JSON");
2408+
assert_eq!(exact_value["routing"]["graph"], serde_json::Value::Null);
2409+
}
2410+
23442411
// ============================================================
23452412
// Filter-aware posting fetches.
23462413
//

src/vector/ivf/graph.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ impl EdgeListMut<'_> {
378378
}
379379

380380
/// Why a [`RelativeNeighborhoodGraph::search`] stopped expanding.
381-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
381+
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
382382
pub enum SearchTerminationReason {
383383
/// The best unexpanded candidate could not beat the worst kept result of
384384
/// a full beam: the search converged.
@@ -390,7 +390,7 @@ pub enum SearchTerminationReason {
390390

391391
/// Per-query cost and convergence counters returned by
392392
/// [`RelativeNeighborhoodGraph::search`].
393-
#[derive(Clone, Copy, Debug)]
393+
#[derive(Clone, Copy, Debug, serde::Serialize)]
394394
pub struct NeighborhoodGraphSearchMetrics {
395395
/// Nodes visited — and therefore scored — by the query; the search's
396396
/// navigation cost.

src/vector/ivf/index.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ impl Iterator for ClusterRanking<'_> {
331331
/// [`ClusterRanking::metrics`] snapshot): how many centroids were scored to
332332
/// pick the probe order, and — when routing went through the centroid RNG —
333333
/// the beam search's full [`NeighborhoodGraphSearchMetrics`].
334-
#[derive(Clone, Copy, Debug, Default)]
334+
#[derive(Clone, Copy, Debug, Default, serde::Serialize)]
335335
pub struct IvfSearchMetrics {
336336
/// Centroids scored to route the query (the navigation cost):
337337
/// `num_centroids` on the exact path, the beam-visited count when routed

0 commit comments

Comments
 (0)