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;
5
6use super::VectorResultScorer;
7use super::combiner::MultiValueCombiner;
8use crate::query::traits::{CountFuture, Query, Scorer, ScorerFuture};
9
10/// Binary dense vector query for Hamming distance similarity search
11///
12/// Uses brute-force XOR + popcount scoring. Score = 1.0 - hamming/dim_bits.
13#[derive(Debug, Clone)]
14pub struct BinaryDenseVectorQuery {
15    /// Field containing the binary dense vectors
16    pub field: Field,
17    /// Query vector (packed bits, ceil(dim/8) bytes)
18    pub vector: Vec<u8>,
19    /// How to combine scores for multi-valued documents
20    pub combiner: MultiValueCombiner,
21}
22
23impl std::fmt::Display for BinaryDenseVectorQuery {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        write!(
26            f,
27            "BinaryDense({}, bytes={})",
28            self.field.0,
29            self.vector.len(),
30        )
31    }
32}
33
34impl BinaryDenseVectorQuery {
35    pub fn new(field: Field, vector: Vec<u8>) -> Self {
36        Self {
37            field,
38            vector,
39            combiner: MultiValueCombiner::Max,
40        }
41    }
42
43    pub fn with_combiner(mut self, combiner: MultiValueCombiner) -> Self {
44        self.combiner = combiner;
45        self
46    }
47}
48
49impl Query for BinaryDenseVectorQuery {
50    fn scorer<'a>(&self, reader: &'a SegmentReader, limit: usize) -> ScorerFuture<'a> {
51        let field = self.field;
52        let vector = self.vector.clone();
53        let combiner = self.combiner;
54        Box::pin(async move {
55            let results = reader
56                .search_binary_dense_vector(field, &vector, limit, combiner)
57                .await?;
58
59            Ok(Box::new(VectorResultScorer::new(results, field.0)) as Box<dyn Scorer>)
60        })
61    }
62
63    fn count_estimate<'a>(&self, _reader: &'a SegmentReader) -> CountFuture<'a> {
64        Box::pin(async move { Ok(u32::MAX) })
65    }
66}