Skip to main content

tinyquant_core/corpus/
vector_entry.rs

1//! `VectorEntry` entity — one compressed vector stored in a [`Corpus`](super::Corpus).
2//!
3//! Identity is determined solely by [`VectorId`]; equality and hashing ignore
4//! the compressed payload and metadata. This matches Python's behaviour where
5//! `dict` keys are the vector identifiers.
6
7use alloc::{collections::BTreeMap, string::String};
8
9use crate::codec::CompressedVector;
10use crate::corpus::entry_meta_value::EntryMetaValue;
11use crate::types::{Timestamp, VectorId};
12
13/// A single compressed vector together with its insertion timestamp and
14/// arbitrary key-value metadata.
15///
16/// # Identity
17///
18/// `PartialEq`, `Eq`, and `Hash` are implemented **by `vector_id` only**.
19/// Two entries with the same id but different payloads compare as equal.
20/// This lets callers use `VectorEntry` as a set element or map key.
21#[derive(Clone, Debug)]
22pub struct VectorEntry {
23    vector_id: VectorId,
24    compressed: CompressedVector,
25    /// The actual f32 vector dimension (not the byte count stored in
26    /// `compressed.dimension()` for Passthrough/Fp16 policies).
27    declared_dimension: u32,
28    inserted_at: Timestamp,
29    metadata: BTreeMap<String, EntryMetaValue>,
30}
31
32impl VectorEntry {
33    /// Construct a new `VectorEntry`.
34    ///
35    /// `declared_dimension` is the number of f32 elements in the original
36    /// vector.  For `Compress` policy this equals `compressed.dimension()`;
37    /// for `Passthrough` and `Fp16` the compressed dimension is a byte count
38    /// that differs from the f32 dimension.
39    ///
40    /// `inserted_at` should be a nanosecond Unix timestamp from the caller's
41    /// time source.  `metadata` may be empty.
42    #[must_use]
43    pub const fn new(
44        vector_id: VectorId,
45        compressed: CompressedVector,
46        declared_dimension: u32,
47        inserted_at: Timestamp,
48        metadata: BTreeMap<String, EntryMetaValue>,
49    ) -> Self {
50        Self {
51            vector_id,
52            compressed,
53            declared_dimension,
54            inserted_at,
55            metadata,
56        }
57    }
58
59    /// The stable string identifier for this vector.
60    #[must_use]
61    pub const fn vector_id(&self) -> &VectorId {
62        &self.vector_id
63    }
64
65    /// Reference to the compressed payload.
66    #[must_use]
67    pub const fn compressed(&self) -> &CompressedVector {
68        &self.compressed
69    }
70
71    /// Nanosecond Unix timestamp recorded at insertion time.
72    #[must_use]
73    pub const fn inserted_at(&self) -> Timestamp {
74        self.inserted_at
75    }
76
77    /// Read-only view of the metadata map.
78    #[must_use]
79    pub const fn metadata(&self) -> &BTreeMap<String, EntryMetaValue> {
80        &self.metadata
81    }
82
83    /// Mutable access to the metadata map.
84    pub fn metadata_mut(&mut self) -> &mut BTreeMap<String, EntryMetaValue> {
85        &mut self.metadata
86    }
87
88    /// Convenience delegate: config hash of the underlying compressed vector.
89    #[must_use]
90    pub const fn config_hash(&self) -> &crate::types::ConfigHash {
91        self.compressed.config_hash()
92    }
93
94    /// The number of f32 dimensions in the original vector.
95    ///
96    /// For `Compress` policy this matches `compressed.dimension()`.  For
97    /// `Passthrough` and `Fp16` policies `compressed.dimension()` holds a
98    /// byte count; this method always returns the true f32 vector length.
99    #[must_use]
100    pub const fn dimension(&self) -> u32 {
101        self.declared_dimension
102    }
103
104    /// Convenience delegate: whether a residual buffer is present.
105    #[must_use]
106    pub fn has_residual(&self) -> bool {
107        self.compressed.has_residual()
108    }
109}
110
111impl PartialEq for VectorEntry {
112    /// Equality by `vector_id` only — payload and metadata are ignored.
113    fn eq(&self, other: &Self) -> bool {
114        self.vector_id == other.vector_id
115    }
116}
117
118impl Eq for VectorEntry {}
119
120impl core::hash::Hash for VectorEntry {
121    /// Hash by `vector_id` only — must be consistent with `PartialEq`.
122    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
123        self.vector_id.hash(state);
124    }
125}