1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Query search options and extracted components for the query pipeline.
//!
//! Extracted from `query/mod.rs` to keep file NLOC under 500.
/// Maximum allowed LIMIT value to prevent overflow in over-fetch calculations.
pub(in crate::collection::search::query) const MAX_LIMIT: usize = 100_000;
/// Query-time search options extracted from the WITH clause.
///
/// Consolidates `mode`, `ef_search`, `rerank`, and `fusion_clause` into a single
/// struct that flows through all dispatch paths. When no WITH clause is present,
/// all fields are `None` and the default behavior is preserved.
#[derive(Debug, Clone, Default)]
pub(crate) struct QuerySearchOptions {
/// Search quality profile parsed from `WITH (mode='...')`.
pub quality: Option<crate::SearchQuality>,
/// Explicit ef_search override from `WITH (ef_search=N)`.
pub ef_search: Option<usize>,
/// Force reranking on (`true`) or off (`false`) from `WITH (rerank=...)`.
pub force_rerank: Option<bool>,
/// Fusion clause from `USING FUSION (...)`.
pub fusion_clause: Option<crate::velesql::FusionClause>,
/// EXPLAIN ANALYZE out-channel: the executor records which filter
/// strategy it actually ran into this cell (shared with the query's
/// [`QueryContext`](crate::guardrails::QueryContext)). `None` outside
/// the pipeline (raw search entry points) — recording is then a no-op.
/// A shared atomic cell, not a thread-local: the vector leg of the CBO
/// `Parallel` strategy runs on a rayon worker thread.
pub executed_strategy_probe: Option<std::sync::Arc<crate::guardrails::ExecutedStrategyCell>>,
}
impl QuerySearchOptions {
/// Extracts search options from an optional WITH clause and fusion clause.
///
/// Maps `mode` string to [`SearchQuality`](crate::SearchQuality) using the
/// same parsing logic as `mode_to_search_quality()`. Invalid mode strings
/// are silently ignored (quality remains `None`).
#[must_use]
pub(crate) fn from_with_clause(with: Option<&crate::velesql::WithClause>) -> Self {
let Some(with) = with else {
return Self::default();
};
let quality = with.get_mode().and_then(parse_mode_to_quality);
let ef_search = with.get_ef_search();
let force_rerank = with.get_rerank();
Self {
quality,
ef_search,
force_rerank,
fusion_clause: None,
executed_strategy_probe: None,
}
}
/// Attaches the query context's executed-strategy slot, so the filtered
/// vector-search dispatch can report which shape it ran to EXPLAIN
/// ANALYZE.
#[must_use]
pub(crate) fn with_executed_strategy_probe(
mut self,
ctx: &crate::guardrails::QueryContext,
) -> Self {
self.executed_strategy_probe = Some(ctx.executed_strategy_slot());
self
}
/// Records the filter strategy the executor is about to run. No-op when
/// no probe is attached (raw search entry points).
pub(crate) fn record_executed_strategy(&self, strategy: crate::velesql::FilterStrategy) {
if let Some(probe) = &self.executed_strategy_probe {
probe.record(strategy);
}
}
/// Creates options with a fusion clause attached.
#[must_use]
pub(crate) fn with_fusion(mut self, fusion: Option<crate::velesql::FusionClause>) -> Self {
self.fusion_clause = fusion;
self
}
/// Returns `true` when any quality-related override is set.
#[must_use]
pub(crate) fn has_quality_overrides(&self) -> bool {
self.quality.is_some() || self.ef_search.is_some() || self.force_rerank.is_some()
}
}
/// Maps a mode string from `WITH (mode='...')` to a [`SearchQuality`](crate::SearchQuality).
///
/// Delegates to [`crate::api_types::mode_to_search_quality`] which also handles
/// advanced modes (`custom:<ef>`, `adaptive:<min>:<max>`).
#[cfg(feature = "persistence")]
fn parse_mode_to_quality(mode: &str) -> Option<crate::SearchQuality> {
crate::api_types::mode_to_search_quality(mode)
}
/// Extracted query components from the WHERE clause.
pub(in crate::collection::search::query) struct ExtractedComponents {
pub(in crate::collection::search::query) vector_search: Option<Vec<f32>>,
pub(in crate::collection::search::query) similarity_conditions:
Vec<(String, Vec<f32>, crate::velesql::CompareOp, f64)>,
pub(in crate::collection::search::query) filter_condition: Option<crate::velesql::Condition>,
pub(in crate::collection::search::query) graph_match_predicates:
Vec<crate::velesql::GraphMatchPredicate>,
pub(in crate::collection::search::query) sparse_vector_search:
Option<crate::velesql::SparseVectorSearch>,
/// `NEAR_FUSED` multi-vector fusion: resolved query vectors + fusion config,
/// routed to `multi_query_search`. `None` for non-fused queries.
pub(in crate::collection::search::query) fused_search:
Option<(Vec<Vec<f32>>, crate::velesql::FusionConfig)>,
pub(in crate::collection::search::query) is_union_query: bool,
pub(in crate::collection::search::query) is_not_similarity_query: bool,
}
/// Bundles the parameters for [`Collection::finalize_query_results`] to stay
/// within the 8-parameter limit.
pub(in crate::collection::search::query) struct QueryFinalizationContext<'a> {
pub(in crate::collection::search::query) stmt: &'a crate::velesql::SelectStatement,
pub(in crate::collection::search::query) params:
&'a std::collections::HashMap<String, serde_json::Value>,
pub(in crate::collection::search::query) limit: usize,
pub(in crate::collection::search::query) extracted: &'a ExtractedComponents,
pub(in crate::collection::search::query) ctx: &'a crate::guardrails::QueryContext,
pub(in crate::collection::search::query) let_bindings: &'a [crate::velesql::LetBinding],
}