Skip to main content

hermes_core/query/vector/
binary_dense.rs

1//! Binary dense vector query for Hamming distance search
2
3use crate::dsl::Field;
4use crate::segment::SegmentReader;
5use std::sync::{Arc, Mutex};
6
7use super::VectorResultScorer;
8use super::combiner::MultiValueCombiner;
9use crate::query::traits::{CountFuture, Query, Scorer, ScorerFuture};
10
11/// Binary dense vector query for Hamming distance similarity search
12///
13/// Uses global IVF routing when built and a brute-force fallback while the
14/// field is accumulating. Leaf scoring remains exact XOR + popcount.
15#[derive(Debug, Clone)]
16pub struct BinaryDenseVectorQuery {
17    /// Field containing the binary dense vectors
18    pub field: Field,
19    /// Query vector (packed bits, ceil(dim/8) bytes)
20    pub vector: Vec<u8>,
21    /// How to combine scores for multi-valued documents
22    pub combiner: MultiValueCombiner,
23    probe_cache: Arc<Mutex<Option<crate::structures::IvfProbePlan>>>,
24}
25
26impl std::fmt::Display for BinaryDenseVectorQuery {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        write!(
29            f,
30            "BinaryDense({}, bytes={})",
31            self.field.0,
32            self.vector.len(),
33        )
34    }
35}
36
37impl BinaryDenseVectorQuery {
38    pub fn new(field: Field, vector: Vec<u8>) -> Self {
39        Self {
40            field,
41            vector,
42            combiner: MultiValueCombiner::Max,
43            probe_cache: Arc::new(Mutex::new(None)),
44        }
45    }
46
47    pub fn with_combiner(mut self, combiner: MultiValueCombiner) -> Self {
48        self.combiner = combiner;
49        self
50    }
51}
52
53impl Query for BinaryDenseVectorQuery {
54    fn scorer<'a>(&self, reader: &'a SegmentReader, limit: usize) -> ScorerFuture<'a> {
55        let field = self.field;
56        let vector = self.vector.clone();
57        let combiner = self.combiner;
58        let probe_cache = Arc::clone(&self.probe_cache);
59        Box::pin(async move {
60            let results = reader
61                .search_binary_dense_vector_with_probe_cache(
62                    field,
63                    &vector,
64                    limit,
65                    combiner,
66                    &probe_cache,
67                )
68                .await?;
69
70            Ok(Box::new(VectorResultScorer::new(results, field.0)) as Box<dyn Scorer>)
71        })
72    }
73
74    #[cfg(feature = "sync")]
75    fn scorer_sync<'a>(
76        &self,
77        reader: &'a SegmentReader,
78        limit: usize,
79    ) -> crate::Result<Box<dyn Scorer + 'a>> {
80        let results = reader.search_binary_dense_vector_sync_with_probe_cache(
81            self.field,
82            &self.vector,
83            limit,
84            self.combiner,
85            &self.probe_cache,
86        )?;
87        Ok(Box::new(VectorResultScorer::new(results, self.field.0)) as Box<dyn Scorer>)
88    }
89
90    fn count_estimate<'a>(&self, _reader: &'a SegmentReader) -> CountFuture<'a> {
91        Box::pin(async move { Ok(u32::MAX) })
92    }
93}