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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! 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 the `mode` option, or its alias `quality`, to a
/// [`SearchQuality`](crate::SearchQuality) through
/// [`WithClause::search_quality`](crate::velesql::WithClause::search_quality),
/// and `ef_search` through
/// [`WithClause::ef_search`](crate::velesql::WithClause::ef_search): a
/// mode that is not a string or does not parse, or an `ef_search` outside
/// the documented range, is a query error, not a silently ignored
/// override (#2267, #2274). The query validator rejects both first on
/// every query path; this keeps the conversion honest for a direct
/// caller.
pub(crate) fn from_with_clause(
with: Option<&crate::velesql::WithClause>,
) -> crate::error::Result<Self> {
let Some(with) = with else {
return Ok(Self::default());
};
let quality = with.search_quality().map_err(crate::error::Error::Query)?;
let ef_search = with.ef_search().map_err(crate::error::Error::Query)?;
let force_rerank = with.get_rerank();
Ok(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()
}
/// The quality a search with these options runs at: an explicit
/// `ef_search`, which wins over `mode` as `docs/VELESQL_SPEC.md` documents
/// and REST resolves (#2274), else `mode`, else `default` — the
/// collection's runtime quality, so options that name no quality (e.g.
/// only `rerank`) keep the configured `[search]` default (#2399). The one
/// resolution the plain and the filtered vector path share.
#[must_use]
pub(crate) fn resolved_quality(&self, default: crate::SearchQuality) -> crate::SearchQuality {
self.ef_search
.map(crate::collection::search::vector_filter::ef_to_quality)
.or(self.quality)
.unwrap_or(default)
}
}
/// 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.
///
/// [`Collection::finalize_query_results`]: crate::collection::Collection::finalize_query_results
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],
}