summa_core/query/vector/
dense.rs1use crate::dsl::Field;
4use crate::segment::SegmentReader;
5use std::sync::Arc;
6
7use super::VectorResultScorer;
8use super::combiner::MultiValueCombiner;
9use crate::query::traits::{CountFuture, Query, Scorer, ScorerFuture};
10
11pub const MAX_DENSE_NPROBE: usize = 65_536;
16
17pub const MAX_DENSE_RERANK_FACTOR: f32 = crate::query::MAX_CANDIDATE_OVERSUBSCRIPTION as f32;
19
20pub const DEFAULT_DENSE_RERANK_FACTOR: f32 = MAX_DENSE_RERANK_FACTOR;
22
23#[derive(Debug, Clone)]
25pub struct DenseVectorQuery {
26 pub field: Field,
28 pub vector: Vec<f32>,
30 pub nprobe: usize,
32 pub rerank_factor: f32,
34 pub combiner: MultiValueCombiner,
36 plan_cache: Arc<crate::segment::DensePlanCache>,
40 shared_vector: std::sync::OnceLock<Arc<[f32]>>,
44}
45
46impl std::fmt::Display for DenseVectorQuery {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 write!(
49 f,
50 "Dense({}, dim={}, nprobe={}, rerank={})",
51 self.field.0,
52 self.vector.len(),
53 self.nprobe,
54 self.rerank_factor
55 )
56 }
57}
58
59impl DenseVectorQuery {
60 pub fn new(field: Field, vector: Vec<f32>) -> Self {
62 Self {
63 field,
64 vector,
65 nprobe: 64,
66 rerank_factor: DEFAULT_DENSE_RERANK_FACTOR,
67 combiner: MultiValueCombiner::Max,
68 plan_cache: Arc::new(Default::default()),
69 shared_vector: std::sync::OnceLock::new(),
70 }
71 }
72
73 fn shared_vector(&self) -> Arc<[f32]> {
77 let shared = self
78 .shared_vector
79 .get_or_init(|| Arc::from(self.vector.as_slice()));
80 if shared.as_ref() == self.vector.as_slice() {
81 Arc::clone(shared)
82 } else {
83 Arc::from(self.vector.as_slice())
84 }
85 }
86
87 pub fn with_nprobe(mut self, nprobe: usize) -> Self {
92 self.nprobe = nprobe;
93 self
94 }
95
96 pub fn with_rerank_factor(mut self, factor: f32) -> Self {
101 self.rerank_factor = factor;
102 self
103 }
104
105 pub fn with_combiner(mut self, combiner: MultiValueCombiner) -> Self {
107 self.combiner = combiner;
108 self
109 }
110}
111
112impl Query for DenseVectorQuery {
113 fn candidate_query(&self) -> crate::Result<crate::query::CandidateQuery> {
114 Ok(crate::query::CandidateQuery::new(
115 self.field,
116 crate::query::candidate_scoring::ScoreComponent::Dense(self.vector.clone()),
117 )
118 .with_combiner(self.combiner))
119 }
120
121 fn scorer<'a>(&self, reader: &'a SegmentReader, limit: usize) -> ScorerFuture<'a> {
122 let field = self.field;
123 let vector = self.shared_vector();
124 let nprobe = self.nprobe;
125 let rerank_factor = self.rerank_factor;
126 let combiner = self.combiner;
127 let plan_cache = Arc::clone(&self.plan_cache);
128 Box::pin(async move {
129 let results = reader
130 .search_dense_vector_with_probe_cache(
131 field,
132 &vector,
133 limit,
134 nprobe,
135 rerank_factor,
136 combiner,
137 &plan_cache,
138 )
139 .await?;
140
141 Ok(Box::new(VectorResultScorer::new(results, field.0)) as Box<dyn Scorer>)
142 })
143 }
144
145 #[cfg(feature = "sync")]
146 fn scorer_sync<'a>(
147 &self,
148 reader: &'a SegmentReader,
149 limit: usize,
150 ) -> crate::Result<Box<dyn Scorer + 'a>> {
151 let results = reader.search_dense_vector_sync_with_probe_cache(
152 self.field,
153 &self.vector,
154 limit,
155 self.nprobe,
156 self.rerank_factor,
157 self.combiner,
158 &self.plan_cache,
159 )?;
160 Ok(Box::new(VectorResultScorer::new(results, self.field.0)) as Box<dyn Scorer>)
161 }
162
163 fn count_estimate<'a>(&self, _reader: &'a SegmentReader) -> CountFuture<'a> {
164 Box::pin(async move { Ok(u32::MAX) })
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_dense_vector_query_builder() {
174 let query = DenseVectorQuery::new(Field(0), vec![1.0, 2.0, 3.0]).with_nprobe(64);
175
176 assert_eq!(query.field, Field(0));
177 assert_eq!(query.vector.len(), 3);
178 assert_eq!(query.nprobe, 64);
179 assert_eq!(query.rerank_factor, 2.0);
180 }
181}