Skip to main content

chroma_types/
metadata.rs

1use chroma_error::{ChromaError, ErrorCodes};
2use itertools::Itertools;
3use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
4use serde_json::{Number, Value};
5use sprs::CsVec;
6use std::{
7    cmp::Ordering,
8    collections::{HashMap, HashSet},
9    mem::size_of_val,
10    ops::{BitAnd, BitOr},
11};
12use thiserror::Error;
13
14use crate::chroma_proto;
15
16#[cfg(feature = "pyo3")]
17use pyo3::types::{PyAnyMethods, PyDictMethods};
18
19#[cfg(feature = "testing")]
20use proptest::prelude::*;
21
22#[derive(Serialize, Deserialize)]
23struct SparseVectorSerdeHelper {
24    #[serde(rename = "#type")]
25    type_tag: Option<String>,
26    indices: Vec<u32>,
27    values: Vec<f32>,
28    tokens: Option<Vec<String>>,
29}
30
31/// Represents a sparse vector using parallel arrays for indices and values.
32///
33/// On deserialization: accepts both old format `{"indices": [...], "values": [...]}`
34/// and new format `{"#type": "sparse_vector", "indices": [...], "values": [...]}`.
35///
36/// On serialization: always includes `#type` field with value `"sparse_vector"`.
37#[derive(Clone, Debug, PartialEq)]
38#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
39pub struct SparseVector {
40    /// Dimension indices
41    pub indices: Vec<u32>,
42    /// Values corresponding to each index
43    pub values: Vec<f32>,
44    /// Tokens corresponding to each index
45    pub tokens: Option<Vec<String>>,
46}
47
48// Custom deserializer: accept both old and new formats
49impl<'de> Deserialize<'de> for SparseVector {
50    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
51    where
52        D: Deserializer<'de>,
53    {
54        let helper = SparseVectorSerdeHelper::deserialize(deserializer)?;
55
56        // If #type is present, validate it
57        if let Some(type_tag) = &helper.type_tag {
58            if type_tag != "sparse_vector" {
59                return Err(serde::de::Error::custom(format!(
60                    "Expected #type='sparse_vector', got '{}'",
61                    type_tag
62                )));
63            }
64        }
65
66        Ok(SparseVector {
67            indices: helper.indices,
68            values: helper.values,
69            tokens: helper.tokens,
70        })
71    }
72}
73
74// Custom serializer: always include #type field
75impl Serialize for SparseVector {
76    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
77    where
78        S: Serializer,
79    {
80        let helper = SparseVectorSerdeHelper {
81            type_tag: Some("sparse_vector".to_string()),
82            indices: self.indices.clone(),
83            values: self.values.clone(),
84            tokens: self.tokens.clone(),
85        };
86        helper.serialize(serializer)
87    }
88}
89
90/// Length mismatch between indices, values, and tokens in a sparse vector.
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct SparseVectorLengthMismatch;
93
94impl std::fmt::Display for SparseVectorLengthMismatch {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96        write!(
97            f,
98            "Sparse vector indices, values, and tokens (when present) must have the same length"
99        )
100    }
101}
102
103impl std::error::Error for SparseVectorLengthMismatch {}
104
105impl ChromaError for SparseVectorLengthMismatch {
106    fn code(&self) -> ErrorCodes {
107        ErrorCodes::InvalidArgument
108    }
109}
110
111impl SparseVector {
112    /// Create a new sparse vector from parallel arrays.
113    pub fn new(indices: Vec<u32>, values: Vec<f32>) -> Result<Self, SparseVectorLengthMismatch> {
114        if indices.len() != values.len() {
115            return Err(SparseVectorLengthMismatch);
116        }
117        Ok(Self {
118            indices,
119            values,
120            tokens: None,
121        })
122    }
123
124    /// Create a new sparse vector from parallel arrays.
125    pub fn new_with_tokens(
126        indices: Vec<u32>,
127        values: Vec<f32>,
128        tokens: Vec<String>,
129    ) -> Result<Self, SparseVectorLengthMismatch> {
130        if indices.len() != values.len() {
131            return Err(SparseVectorLengthMismatch);
132        }
133        if tokens.len() != indices.len() {
134            return Err(SparseVectorLengthMismatch);
135        }
136        Ok(Self {
137            indices,
138            values,
139            tokens: Some(tokens),
140        })
141    }
142
143    /// Create a sparse vector from an iterator of (index, value) pairs.
144    pub fn from_pairs(pairs: impl IntoIterator<Item = (u32, f32)>) -> Self {
145        let mut indices = vec![];
146        let mut values = vec![];
147        for (index, value) in pairs {
148            indices.push(index);
149            values.push(value);
150        }
151        let tokens = None;
152        Self {
153            indices,
154            values,
155            tokens,
156        }
157    }
158
159    /// Create a sparse vector from an iterator of (string, index, value) pairs.
160    pub fn from_triples(triples: impl IntoIterator<Item = (String, u32, f32)>) -> Self {
161        let mut tokens = vec![];
162        let mut indices = vec![];
163        let mut values = vec![];
164        for (token, index, value) in triples {
165            tokens.push(token);
166            indices.push(index);
167            values.push(value);
168        }
169        let tokens = Some(tokens);
170        Self {
171            indices,
172            values,
173            tokens,
174        }
175    }
176
177    /// Iterate over (index, value) pairs.
178    pub fn iter(&self) -> impl Iterator<Item = (u32, f32)> + '_ {
179        self.indices
180            .iter()
181            .copied()
182            .zip(self.values.iter().copied())
183    }
184
185    /// Validate the sparse vector
186    pub fn validate(&self) -> Result<(), MetadataValueConversionError> {
187        // Check that indices and values have the same length
188        if self.indices.len() != self.values.len() {
189            return Err(MetadataValueConversionError::SparseVectorLengthMismatch);
190        }
191
192        // Check that tokens (if present) align with indices
193        if let Some(tokens) = self.tokens.as_ref() {
194            if tokens.len() != self.indices.len() {
195                return Err(MetadataValueConversionError::SparseVectorLengthMismatch);
196            }
197        }
198
199        // Check that indices are sorted in strictly ascending order (no duplicates)
200        for i in 1..self.indices.len() {
201            if self.indices[i] <= self.indices[i - 1] {
202                return Err(MetadataValueConversionError::SparseVectorIndicesNotSorted);
203            }
204        }
205
206        Ok(())
207    }
208}
209
210impl Eq for SparseVector {}
211
212impl Ord for SparseVector {
213    fn cmp(&self, other: &Self) -> Ordering {
214        self.indices.cmp(&other.indices).then_with(|| {
215            for (a, b) in self.values.iter().zip(other.values.iter()) {
216                match a.total_cmp(b) {
217                    Ordering::Equal => continue,
218                    other => return other,
219                }
220            }
221            self.values.len().cmp(&other.values.len())
222        })
223    }
224}
225
226impl PartialOrd for SparseVector {
227    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
228        Some(self.cmp(other))
229    }
230}
231
232impl TryFrom<chroma_proto::SparseVector> for SparseVector {
233    type Error = SparseVectorLengthMismatch;
234
235    fn try_from(proto: chroma_proto::SparseVector) -> Result<Self, Self::Error> {
236        if proto.tokens.is_empty() {
237            SparseVector::new(proto.indices, proto.values)
238        } else {
239            SparseVector::new_with_tokens(proto.indices, proto.values, proto.tokens)
240        }
241    }
242}
243
244impl From<SparseVector> for chroma_proto::SparseVector {
245    fn from(sparse: SparseVector) -> Self {
246        chroma_proto::SparseVector {
247            indices: sparse.indices,
248            values: sparse.values,
249            tokens: sparse.tokens.unwrap_or_default(),
250        }
251    }
252}
253
254/// Convert SparseVector to sprs::CsVec for efficient sparse operations
255impl From<&SparseVector> for CsVec<f32> {
256    fn from(sparse: &SparseVector) -> Self {
257        let (indices, values) = sparse
258            .iter()
259            .map(|(index, value)| (index as usize, value))
260            .unzip();
261        CsVec::new(u32::MAX as usize, indices, values)
262    }
263}
264
265impl From<SparseVector> for CsVec<f32> {
266    fn from(sparse: SparseVector) -> Self {
267        (&sparse).into()
268    }
269}
270
271#[cfg(feature = "pyo3")]
272impl<'py> pyo3::IntoPyObject<'py> for SparseVector {
273    type Target = pyo3::PyAny;
274    type Output = pyo3::Bound<'py, Self::Target>;
275    type Error = pyo3::PyErr;
276
277    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
278        use pyo3::types::PyDict;
279
280        let dict = PyDict::new(py);
281        dict.set_item("indices", self.indices)?;
282        dict.set_item("values", self.values)?;
283        dict.set_item("tokens", self.tokens)?;
284        Ok(dict.into_any())
285    }
286}
287
288#[cfg(feature = "pyo3")]
289impl<'py> pyo3::FromPyObject<'py> for SparseVector {
290    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
291        use pyo3::types::PyDict;
292
293        let dict = ob.downcast::<PyDict>()?;
294        let indices_obj = dict.get_item("indices")?;
295        if indices_obj.is_none() {
296            return Err(pyo3::exceptions::PyKeyError::new_err(
297                "missing 'indices' key",
298            ));
299        }
300        let indices: Vec<u32> = indices_obj.unwrap().extract()?;
301
302        let values_obj = dict.get_item("values")?;
303        if values_obj.is_none() {
304            return Err(pyo3::exceptions::PyKeyError::new_err(
305                "missing 'values' key",
306            ));
307        }
308        let values: Vec<f32> = values_obj.unwrap().extract()?;
309
310        let tokens_obj = dict.get_item("tokens")?;
311        let tokens = match tokens_obj {
312            Some(obj) if obj.is_none() => None,
313            Some(obj) => Some(obj.extract::<Vec<String>>()?),
314            None => None,
315        };
316
317        let result = match tokens {
318            Some(tokens) => SparseVector::new_with_tokens(indices, values, tokens),
319            None => SparseVector::new(indices, values),
320        };
321
322        result.map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))
323    }
324}
325
326#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
327#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
328#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
329#[serde(untagged)]
330pub enum UpdateMetadataValue {
331    Bool(bool),
332    Int(i64),
333    #[cfg_attr(
334        feature = "testing",
335        proptest(
336            strategy = "(-1e6..=1e6f32).prop_map(|v| UpdateMetadataValue::Float(v as f64)).boxed()"
337        )
338    )]
339    Float(f64),
340    Str(String),
341    #[cfg_attr(feature = "testing", proptest(skip))]
342    SparseVector(SparseVector),
343    // Array types for multi-valued metadata fields
344    // TODO: Add support for these in proptests
345    #[cfg_attr(feature = "testing", proptest(skip))]
346    BoolArray(Vec<bool>),
347    #[cfg_attr(feature = "testing", proptest(skip))]
348    IntArray(Vec<i64>),
349    #[cfg_attr(feature = "testing", proptest(skip))]
350    FloatArray(Vec<f64>),
351    #[cfg_attr(feature = "testing", proptest(skip))]
352    StringArray(Vec<String>),
353    None,
354}
355
356#[cfg(feature = "pyo3")]
357impl<'py> pyo3::FromPyObject<'py> for UpdateMetadataValue {
358    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
359        use pyo3::types::PyList;
360
361        if ob.is_none() {
362            Ok(UpdateMetadataValue::None)
363        } else if let Ok(value) = ob.extract::<bool>() {
364            Ok(UpdateMetadataValue::Bool(value))
365        } else if let Ok(value) = ob.extract::<i64>() {
366            Ok(UpdateMetadataValue::Int(value))
367        } else if let Ok(value) = ob.extract::<f64>() {
368            Ok(UpdateMetadataValue::Float(value))
369        } else if let Ok(value) = ob.extract::<String>() {
370            Ok(UpdateMetadataValue::Str(value))
371        } else if let Ok(value) = ob.extract::<SparseVector>() {
372            Ok(UpdateMetadataValue::SparseVector(value))
373        } else if let Ok(list) = ob.downcast::<PyList>() {
374            // Empty lists are not allowed
375            if list.is_empty()? {
376                return Err(pyo3::exceptions::PyValueError::new_err(
377                    "Empty lists are not allowed as metadata values",
378                ));
379            }
380            // Try to extract entire list as each type.
381            // We check all elements (not just the first) to handle mixed-numeric
382            // lists like [1, 2.5, 3] which should be inferred as FloatArray.
383            if let Ok(arr) = list.extract::<Vec<bool>>() {
384                Ok(UpdateMetadataValue::BoolArray(arr))
385            } else if let Ok(arr) = list.extract::<Vec<i64>>() {
386                Ok(UpdateMetadataValue::IntArray(arr))
387            } else if let Ok(arr) = list.extract::<Vec<f64>>() {
388                Ok(UpdateMetadataValue::FloatArray(arr))
389            } else if let Ok(arr) = list.extract::<Vec<String>>() {
390                Ok(UpdateMetadataValue::StringArray(arr))
391            } else {
392                Err(pyo3::exceptions::PyTypeError::new_err(
393                    "Cannot convert Python list to UpdateMetadataValue: mixed or unsupported element types",
394                ))
395            }
396        } else {
397            Err(pyo3::exceptions::PyTypeError::new_err(
398                "Cannot convert Python object to UpdateMetadataValue",
399            ))
400        }
401    }
402}
403
404impl From<bool> for UpdateMetadataValue {
405    fn from(b: bool) -> Self {
406        Self::Bool(b)
407    }
408}
409
410impl From<i64> for UpdateMetadataValue {
411    fn from(v: i64) -> Self {
412        Self::Int(v)
413    }
414}
415
416impl From<i32> for UpdateMetadataValue {
417    fn from(v: i32) -> Self {
418        Self::Int(v as i64)
419    }
420}
421
422impl From<f64> for UpdateMetadataValue {
423    fn from(v: f64) -> Self {
424        Self::Float(v)
425    }
426}
427
428impl From<f32> for UpdateMetadataValue {
429    fn from(v: f32) -> Self {
430        Self::Float(v as f64)
431    }
432}
433
434impl From<String> for UpdateMetadataValue {
435    fn from(v: String) -> Self {
436        Self::Str(v)
437    }
438}
439
440impl From<&str> for UpdateMetadataValue {
441    fn from(v: &str) -> Self {
442        Self::Str(v.to_string())
443    }
444}
445
446impl From<SparseVector> for UpdateMetadataValue {
447    fn from(v: SparseVector) -> Self {
448        Self::SparseVector(v)
449    }
450}
451
452impl From<Vec<bool>> for UpdateMetadataValue {
453    fn from(v: Vec<bool>) -> Self {
454        Self::BoolArray(v)
455    }
456}
457
458impl From<Vec<i64>> for UpdateMetadataValue {
459    fn from(v: Vec<i64>) -> Self {
460        Self::IntArray(v)
461    }
462}
463
464impl From<Vec<f64>> for UpdateMetadataValue {
465    fn from(v: Vec<f64>) -> Self {
466        Self::FloatArray(v)
467    }
468}
469
470impl From<Vec<String>> for UpdateMetadataValue {
471    fn from(v: Vec<String>) -> Self {
472        Self::StringArray(v)
473    }
474}
475
476#[derive(Error, Debug)]
477pub enum UpdateMetadataValueConversionError {
478    #[error("Invalid metadata value, valid values are: Int, Float, Str, Bool, None")]
479    InvalidValue,
480}
481
482impl ChromaError for UpdateMetadataValueConversionError {
483    fn code(&self) -> ErrorCodes {
484        match self {
485            UpdateMetadataValueConversionError::InvalidValue => ErrorCodes::InvalidArgument,
486        }
487    }
488}
489
490impl TryFrom<&chroma_proto::UpdateMetadataValue> for UpdateMetadataValue {
491    type Error = UpdateMetadataValueConversionError;
492
493    fn try_from(value: &chroma_proto::UpdateMetadataValue) -> Result<Self, Self::Error> {
494        match &value.value {
495            Some(chroma_proto::update_metadata_value::Value::BoolValue(value)) => {
496                Ok(UpdateMetadataValue::Bool(*value))
497            }
498            Some(chroma_proto::update_metadata_value::Value::IntValue(value)) => {
499                Ok(UpdateMetadataValue::Int(*value))
500            }
501            Some(chroma_proto::update_metadata_value::Value::FloatValue(value)) => {
502                if !value.is_finite() {
503                    return Err(UpdateMetadataValueConversionError::InvalidValue);
504                }
505                Ok(UpdateMetadataValue::Float(*value))
506            }
507            Some(chroma_proto::update_metadata_value::Value::StringValue(value)) => {
508                Ok(UpdateMetadataValue::Str(value.clone()))
509            }
510            Some(chroma_proto::update_metadata_value::Value::SparseVectorValue(value)) => {
511                let sparse = value
512                    .clone()
513                    .try_into()
514                    .map_err(|_| UpdateMetadataValueConversionError::InvalidValue)?;
515                Ok(UpdateMetadataValue::SparseVector(sparse))
516            }
517            Some(chroma_proto::update_metadata_value::Value::BoolListValue(value)) => {
518                Ok(UpdateMetadataValue::BoolArray(value.values.clone()))
519            }
520            Some(chroma_proto::update_metadata_value::Value::IntListValue(value)) => {
521                Ok(UpdateMetadataValue::IntArray(value.values.clone()))
522            }
523            Some(chroma_proto::update_metadata_value::Value::DoubleListValue(value)) => {
524                if value.values.iter().any(|v| !v.is_finite()) {
525                    return Err(UpdateMetadataValueConversionError::InvalidValue);
526                }
527                Ok(UpdateMetadataValue::FloatArray(value.values.clone()))
528            }
529            Some(chroma_proto::update_metadata_value::Value::StringListValue(value)) => {
530                Ok(UpdateMetadataValue::StringArray(value.values.clone()))
531            }
532            // Used to communicate that the user wants to delete this key.
533            None => Ok(UpdateMetadataValue::None),
534        }
535    }
536}
537
538impl From<UpdateMetadataValue> for chroma_proto::UpdateMetadataValue {
539    fn from(value: UpdateMetadataValue) -> Self {
540        match value {
541            UpdateMetadataValue::Bool(value) => chroma_proto::UpdateMetadataValue {
542                value: Some(chroma_proto::update_metadata_value::Value::BoolValue(value)),
543            },
544            UpdateMetadataValue::Int(value) => chroma_proto::UpdateMetadataValue {
545                value: Some(chroma_proto::update_metadata_value::Value::IntValue(value)),
546            },
547            UpdateMetadataValue::Float(value) => chroma_proto::UpdateMetadataValue {
548                value: Some(chroma_proto::update_metadata_value::Value::FloatValue(
549                    value,
550                )),
551            },
552            UpdateMetadataValue::Str(value) => chroma_proto::UpdateMetadataValue {
553                value: Some(chroma_proto::update_metadata_value::Value::StringValue(
554                    value,
555                )),
556            },
557            UpdateMetadataValue::SparseVector(sparse_vec) => chroma_proto::UpdateMetadataValue {
558                value: Some(
559                    chroma_proto::update_metadata_value::Value::SparseVectorValue(
560                        sparse_vec.into(),
561                    ),
562                ),
563            },
564            UpdateMetadataValue::BoolArray(values) => chroma_proto::UpdateMetadataValue {
565                value: Some(chroma_proto::update_metadata_value::Value::BoolListValue(
566                    chroma_proto::BoolListValue { values },
567                )),
568            },
569            UpdateMetadataValue::IntArray(values) => chroma_proto::UpdateMetadataValue {
570                value: Some(chroma_proto::update_metadata_value::Value::IntListValue(
571                    chroma_proto::IntListValue { values },
572                )),
573            },
574            UpdateMetadataValue::FloatArray(values) => chroma_proto::UpdateMetadataValue {
575                value: Some(chroma_proto::update_metadata_value::Value::DoubleListValue(
576                    chroma_proto::DoubleListValue { values },
577                )),
578            },
579            UpdateMetadataValue::StringArray(values) => chroma_proto::UpdateMetadataValue {
580                value: Some(chroma_proto::update_metadata_value::Value::StringListValue(
581                    chroma_proto::StringListValue { values },
582                )),
583            },
584            UpdateMetadataValue::None => chroma_proto::UpdateMetadataValue { value: None },
585        }
586    }
587}
588
589impl TryFrom<&UpdateMetadataValue> for MetadataValue {
590    type Error = MetadataValueConversionError;
591
592    fn try_from(value: &UpdateMetadataValue) -> Result<Self, Self::Error> {
593        match value {
594            UpdateMetadataValue::Bool(value) => Ok(MetadataValue::Bool(*value)),
595            UpdateMetadataValue::Int(value) => Ok(MetadataValue::Int(*value)),
596            UpdateMetadataValue::Float(value) => Ok(MetadataValue::Float(*value)),
597            UpdateMetadataValue::Str(value) => Ok(MetadataValue::Str(value.clone())),
598            UpdateMetadataValue::SparseVector(value) => {
599                Ok(MetadataValue::SparseVector(value.clone()))
600            }
601            UpdateMetadataValue::BoolArray(value) => Ok(MetadataValue::BoolArray(value.clone())),
602            UpdateMetadataValue::IntArray(value) => Ok(MetadataValue::IntArray(value.clone())),
603            UpdateMetadataValue::FloatArray(value) => Ok(MetadataValue::FloatArray(value.clone())),
604            UpdateMetadataValue::StringArray(value) => {
605                Ok(MetadataValue::StringArray(value.clone()))
606            }
607            UpdateMetadataValue::None => Err(MetadataValueConversionError::InvalidValue),
608        }
609    }
610}
611
612/*
613===========================================
614MetadataValue
615===========================================
616*/
617
618#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
619#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
620#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
621#[cfg_attr(feature = "pyo3", derive(pyo3::IntoPyObject))]
622#[serde(untagged)]
623pub enum MetadataValue {
624    Bool(bool),
625    Int(i64),
626    #[cfg_attr(
627        feature = "testing",
628        proptest(
629            strategy = "(-1e6..=1e6f32).prop_map(|v| MetadataValue::Float(v as f64)).boxed()"
630        )
631    )]
632    Float(f64),
633    Str(String),
634    #[cfg_attr(feature = "testing", proptest(skip))]
635    SparseVector(SparseVector),
636    // Array types for multi-valued metadata fields
637    // TODO: Add support for these in proptests
638    #[cfg_attr(feature = "testing", proptest(skip))]
639    BoolArray(Vec<bool>),
640    #[cfg_attr(feature = "testing", proptest(skip))]
641    IntArray(Vec<i64>),
642    #[cfg_attr(feature = "testing", proptest(skip))]
643    FloatArray(Vec<f64>),
644    #[cfg_attr(feature = "testing", proptest(skip))]
645    StringArray(Vec<String>),
646}
647
648#[cfg(feature = "pyo3")]
649impl<'py> pyo3::FromPyObject<'py> for MetadataValue {
650    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
651        use pyo3::types::PyList;
652
653        if let Ok(value) = ob.extract::<bool>() {
654            Ok(MetadataValue::Bool(value))
655        } else if let Ok(value) = ob.extract::<i64>() {
656            Ok(MetadataValue::Int(value))
657        } else if let Ok(value) = ob.extract::<f64>() {
658            Ok(MetadataValue::Float(value))
659        } else if let Ok(value) = ob.extract::<String>() {
660            Ok(MetadataValue::Str(value))
661        } else if let Ok(value) = ob.extract::<SparseVector>() {
662            Ok(MetadataValue::SparseVector(value))
663        } else if let Ok(list) = ob.downcast::<PyList>() {
664            // Empty lists are not allowed
665            if list.is_empty()? {
666                return Err(pyo3::exceptions::PyValueError::new_err(
667                    "Empty lists are not allowed as metadata values",
668                ));
669            }
670            // Try to extract entire list as each type.
671            // We check all elements (not just the first) to handle mixed-numeric
672            // lists like [1, 2.5, 3] which should be inferred as FloatArray.
673            if let Ok(arr) = list.extract::<Vec<bool>>() {
674                Ok(MetadataValue::BoolArray(arr))
675            } else if let Ok(arr) = list.extract::<Vec<i64>>() {
676                Ok(MetadataValue::IntArray(arr))
677            } else if let Ok(arr) = list.extract::<Vec<f64>>() {
678                Ok(MetadataValue::FloatArray(arr))
679            } else if let Ok(arr) = list.extract::<Vec<String>>() {
680                Ok(MetadataValue::StringArray(arr))
681            } else {
682                Err(pyo3::exceptions::PyTypeError::new_err(
683                    "Cannot convert Python list to MetadataValue: mixed or unsupported element types",
684                ))
685            }
686        } else {
687            Err(pyo3::exceptions::PyTypeError::new_err(
688                "Cannot convert Python object to MetadataValue",
689            ))
690        }
691    }
692}
693
694impl std::fmt::Display for MetadataValue {
695    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
696        match self {
697            MetadataValue::Bool(v) => write!(f, "{}", v),
698            MetadataValue::Int(v) => write!(f, "{}", v),
699            MetadataValue::Float(v) => write!(f, "{}", v),
700            MetadataValue::Str(v) => write!(f, "\"{}\"", v),
701            MetadataValue::SparseVector(v) => write!(f, "SparseVector(len={})", v.values.len()),
702            MetadataValue::BoolArray(v) => write!(f, "BoolArray(len={})", v.len()),
703            MetadataValue::IntArray(v) => write!(f, "IntArray(len={})", v.len()),
704            MetadataValue::FloatArray(v) => write!(f, "FloatArray(len={})", v.len()),
705            MetadataValue::StringArray(v) => write!(f, "StringArray(len={})", v.len()),
706        }
707    }
708}
709
710impl Eq for MetadataValue {}
711
712#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
713pub enum MetadataValueType {
714    Bool,
715    Int,
716    Float,
717    Str,
718    SparseVector,
719    BoolArray,
720    IntArray,
721    FloatArray,
722    StringArray,
723}
724
725impl MetadataValue {
726    pub fn value_type(&self) -> MetadataValueType {
727        match self {
728            MetadataValue::Bool(_) => MetadataValueType::Bool,
729            MetadataValue::Int(_) => MetadataValueType::Int,
730            MetadataValue::Float(_) => MetadataValueType::Float,
731            MetadataValue::Str(_) => MetadataValueType::Str,
732            MetadataValue::SparseVector(_) => MetadataValueType::SparseVector,
733            MetadataValue::BoolArray(_) => MetadataValueType::BoolArray,
734            MetadataValue::IntArray(_) => MetadataValueType::IntArray,
735            MetadataValue::FloatArray(_) => MetadataValueType::FloatArray,
736            MetadataValue::StringArray(_) => MetadataValueType::StringArray,
737        }
738    }
739}
740
741impl From<&MetadataValue> for MetadataValueType {
742    fn from(value: &MetadataValue) -> Self {
743        value.value_type()
744    }
745}
746
747impl From<bool> for MetadataValue {
748    fn from(v: bool) -> Self {
749        MetadataValue::Bool(v)
750    }
751}
752
753impl From<i64> for MetadataValue {
754    fn from(v: i64) -> Self {
755        MetadataValue::Int(v)
756    }
757}
758
759impl From<i32> for MetadataValue {
760    fn from(v: i32) -> Self {
761        MetadataValue::Int(v as i64)
762    }
763}
764
765impl From<f64> for MetadataValue {
766    fn from(v: f64) -> Self {
767        MetadataValue::Float(v)
768    }
769}
770
771impl From<f32> for MetadataValue {
772    fn from(v: f32) -> Self {
773        MetadataValue::Float(v as f64)
774    }
775}
776
777impl From<String> for MetadataValue {
778    fn from(v: String) -> Self {
779        MetadataValue::Str(v)
780    }
781}
782
783impl From<&str> for MetadataValue {
784    fn from(v: &str) -> Self {
785        MetadataValue::Str(v.to_string())
786    }
787}
788
789impl From<SparseVector> for MetadataValue {
790    fn from(v: SparseVector) -> Self {
791        MetadataValue::SparseVector(v)
792    }
793}
794
795impl From<Vec<bool>> for MetadataValue {
796    fn from(v: Vec<bool>) -> Self {
797        MetadataValue::BoolArray(v)
798    }
799}
800
801impl From<Vec<i64>> for MetadataValue {
802    fn from(v: Vec<i64>) -> Self {
803        MetadataValue::IntArray(v)
804    }
805}
806
807impl From<Vec<i32>> for MetadataValue {
808    fn from(v: Vec<i32>) -> Self {
809        MetadataValue::IntArray(v.into_iter().map(|x| x as i64).collect())
810    }
811}
812
813impl From<Vec<f64>> for MetadataValue {
814    fn from(v: Vec<f64>) -> Self {
815        MetadataValue::FloatArray(v)
816    }
817}
818
819impl From<Vec<f32>> for MetadataValue {
820    fn from(v: Vec<f32>) -> Self {
821        MetadataValue::FloatArray(v.into_iter().map(|x| x as f64).collect())
822    }
823}
824
825impl From<Vec<String>> for MetadataValue {
826    fn from(v: Vec<String>) -> Self {
827        MetadataValue::StringArray(v)
828    }
829}
830
831impl From<Vec<&str>> for MetadataValue {
832    fn from(v: Vec<&str>) -> Self {
833        MetadataValue::StringArray(v.into_iter().map(|s| s.to_string()).collect())
834    }
835}
836
837/// We need `Eq` and `Ord` since we want to use this as a key in `BTreeMap`
838///
839/// For cross-type comparisons, we define a consistent ordering based on variant position:
840/// Bool < Int < Float < Str < SparseVector < BoolArray < IntArray < FloatArray < StringArray
841#[allow(clippy::derive_ord_xor_partial_ord)]
842impl Ord for MetadataValue {
843    fn cmp(&self, other: &Self) -> Ordering {
844        // Define type ordering based on variant position
845        fn type_order(val: &MetadataValue) -> u8 {
846            match val {
847                MetadataValue::Bool(_) => 0,
848                MetadataValue::Int(_) => 1,
849                MetadataValue::Float(_) => 2,
850                MetadataValue::Str(_) => 3,
851                MetadataValue::SparseVector(_) => 4,
852                MetadataValue::BoolArray(_) => 5,
853                MetadataValue::IntArray(_) => 6,
854                MetadataValue::FloatArray(_) => 7,
855                MetadataValue::StringArray(_) => 8,
856            }
857        }
858
859        // Chain type ordering with value ordering
860        type_order(self).cmp(&type_order(other)).then_with(|| {
861            match (self, other) {
862                (MetadataValue::Bool(left), MetadataValue::Bool(right)) => left.cmp(right),
863                (MetadataValue::Int(left), MetadataValue::Int(right)) => left.cmp(right),
864                (MetadataValue::Float(left), MetadataValue::Float(right)) => left.total_cmp(right),
865                (MetadataValue::Str(left), MetadataValue::Str(right)) => left.cmp(right),
866                (MetadataValue::SparseVector(left), MetadataValue::SparseVector(right)) => {
867                    left.cmp(right)
868                }
869                (MetadataValue::BoolArray(left), MetadataValue::BoolArray(right)) => {
870                    left.cmp(right)
871                }
872                (MetadataValue::IntArray(left), MetadataValue::IntArray(right)) => left.cmp(right),
873                (MetadataValue::FloatArray(left), MetadataValue::FloatArray(right)) => {
874                    // Compare element by element using total_cmp for f64
875                    for (l, r) in left.iter().zip(right.iter()) {
876                        match l.total_cmp(r) {
877                            Ordering::Equal => continue,
878                            other => return other,
879                        }
880                    }
881                    left.len().cmp(&right.len())
882                }
883                (MetadataValue::StringArray(left), MetadataValue::StringArray(right)) => {
884                    left.cmp(right)
885                }
886                _ => Ordering::Equal, // Different types, but type_order already handled this
887            }
888        })
889    }
890}
891
892impl PartialOrd for MetadataValue {
893    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
894        Some(self.cmp(other))
895    }
896}
897
898impl TryFrom<&MetadataValue> for bool {
899    type Error = MetadataValueConversionError;
900
901    fn try_from(value: &MetadataValue) -> Result<Self, Self::Error> {
902        match value {
903            MetadataValue::Bool(value) => Ok(*value),
904            _ => Err(MetadataValueConversionError::InvalidValue),
905        }
906    }
907}
908
909impl TryFrom<&MetadataValue> for i64 {
910    type Error = MetadataValueConversionError;
911
912    fn try_from(value: &MetadataValue) -> Result<Self, Self::Error> {
913        match value {
914            MetadataValue::Int(value) => Ok(*value),
915            _ => Err(MetadataValueConversionError::InvalidValue),
916        }
917    }
918}
919
920impl TryFrom<&MetadataValue> for f64 {
921    type Error = MetadataValueConversionError;
922
923    fn try_from(value: &MetadataValue) -> Result<Self, Self::Error> {
924        match value {
925            MetadataValue::Float(value) => Ok(*value),
926            _ => Err(MetadataValueConversionError::InvalidValue),
927        }
928    }
929}
930
931impl TryFrom<&MetadataValue> for String {
932    type Error = MetadataValueConversionError;
933
934    fn try_from(value: &MetadataValue) -> Result<Self, Self::Error> {
935        match value {
936            MetadataValue::Str(value) => Ok(value.clone()),
937            _ => Err(MetadataValueConversionError::InvalidValue),
938        }
939    }
940}
941
942impl From<MetadataValue> for UpdateMetadataValue {
943    fn from(value: MetadataValue) -> Self {
944        match value {
945            MetadataValue::Bool(v) => UpdateMetadataValue::Bool(v),
946            MetadataValue::Int(v) => UpdateMetadataValue::Int(v),
947            MetadataValue::Float(v) => UpdateMetadataValue::Float(v),
948            MetadataValue::Str(v) => UpdateMetadataValue::Str(v),
949            MetadataValue::SparseVector(v) => UpdateMetadataValue::SparseVector(v),
950            MetadataValue::BoolArray(v) => UpdateMetadataValue::BoolArray(v),
951            MetadataValue::IntArray(v) => UpdateMetadataValue::IntArray(v),
952            MetadataValue::FloatArray(v) => UpdateMetadataValue::FloatArray(v),
953            MetadataValue::StringArray(v) => UpdateMetadataValue::StringArray(v),
954        }
955    }
956}
957
958impl From<MetadataValue> for Value {
959    fn from(value: MetadataValue) -> Self {
960        match value {
961            MetadataValue::Bool(val) => Self::Bool(val),
962            MetadataValue::Int(val) => Self::Number(
963                Number::from_i128(val as i128).expect("i64 should be representable in JSON"),
964            ),
965            MetadataValue::Float(val) => Self::Number(
966                Number::from_f64(val).expect("Inf and NaN should not be present in MetadataValue"),
967            ),
968            MetadataValue::Str(val) => Self::String(val),
969            MetadataValue::SparseVector(val) => {
970                let mut map = serde_json::Map::new();
971                map.insert(
972                    "indices".to_string(),
973                    Value::Array(
974                        val.indices
975                            .iter()
976                            .map(|&i| Value::Number(i.into()))
977                            .collect(),
978                    ),
979                );
980                map.insert(
981                    "values".to_string(),
982                    Value::Array(
983                        val.values
984                            .iter()
985                            .map(|&v| {
986                                Value::Number(
987                                    Number::from_f64(v as f64)
988                                        .expect("Float number should not be NaN or infinite"),
989                                )
990                            })
991                            .collect(),
992                    ),
993                );
994                Self::Object(map)
995            }
996            MetadataValue::BoolArray(vals) => {
997                Self::Array(vals.into_iter().map(Value::Bool).collect())
998            }
999            MetadataValue::IntArray(vals) => Self::Array(
1000                vals.into_iter()
1001                    .map(|v| {
1002                        Value::Number(
1003                            Number::from_i128(v as i128)
1004                                .expect("i64 should be representable in JSON"),
1005                        )
1006                    })
1007                    .collect(),
1008            ),
1009            MetadataValue::FloatArray(vals) => Self::Array(
1010                vals.into_iter()
1011                    .map(|v| {
1012                        Value::Number(
1013                            Number::from_f64(v)
1014                                .expect("Inf and NaN should not be present in MetadataValue"),
1015                        )
1016                    })
1017                    .collect(),
1018            ),
1019            MetadataValue::StringArray(vals) => {
1020                Self::Array(vals.into_iter().map(Value::String).collect())
1021            }
1022        }
1023    }
1024}
1025
1026#[derive(Error, Debug)]
1027pub enum MetadataValueConversionError {
1028    #[error("Invalid metadata value, valid values are: Int, Float, Str")]
1029    InvalidValue,
1030    #[error("Metadata key cannot start with '#' or '$': {0}")]
1031    InvalidKey(String),
1032    #[error("Sparse vector indices, values, and tokens (when present) must have the same length")]
1033    SparseVectorLengthMismatch,
1034    #[error("Sparse vector indices must be sorted in strictly ascending order (no duplicates)")]
1035    SparseVectorIndicesNotSorted,
1036}
1037
1038impl ChromaError for MetadataValueConversionError {
1039    fn code(&self) -> ErrorCodes {
1040        match self {
1041            MetadataValueConversionError::InvalidValue => ErrorCodes::InvalidArgument,
1042            MetadataValueConversionError::InvalidKey(_) => ErrorCodes::InvalidArgument,
1043            MetadataValueConversionError::SparseVectorLengthMismatch => ErrorCodes::InvalidArgument,
1044            MetadataValueConversionError::SparseVectorIndicesNotSorted => {
1045                ErrorCodes::InvalidArgument
1046            }
1047        }
1048    }
1049}
1050
1051impl TryFrom<&chroma_proto::UpdateMetadataValue> for MetadataValue {
1052    type Error = MetadataValueConversionError;
1053
1054    fn try_from(value: &chroma_proto::UpdateMetadataValue) -> Result<Self, Self::Error> {
1055        match &value.value {
1056            Some(chroma_proto::update_metadata_value::Value::BoolValue(value)) => {
1057                Ok(MetadataValue::Bool(*value))
1058            }
1059            Some(chroma_proto::update_metadata_value::Value::IntValue(value)) => {
1060                Ok(MetadataValue::Int(*value))
1061            }
1062            Some(chroma_proto::update_metadata_value::Value::FloatValue(value)) => {
1063                if !value.is_finite() {
1064                    return Err(MetadataValueConversionError::InvalidValue);
1065                }
1066                Ok(MetadataValue::Float(*value))
1067            }
1068            Some(chroma_proto::update_metadata_value::Value::StringValue(value)) => {
1069                Ok(MetadataValue::Str(value.clone()))
1070            }
1071            Some(chroma_proto::update_metadata_value::Value::SparseVectorValue(value)) => {
1072                let sparse = value
1073                    .clone()
1074                    .try_into()
1075                    .map_err(|_| MetadataValueConversionError::SparseVectorLengthMismatch)?;
1076                Ok(MetadataValue::SparseVector(sparse))
1077            }
1078            Some(chroma_proto::update_metadata_value::Value::BoolListValue(value)) => {
1079                Ok(MetadataValue::BoolArray(value.values.clone()))
1080            }
1081            Some(chroma_proto::update_metadata_value::Value::IntListValue(value)) => {
1082                Ok(MetadataValue::IntArray(value.values.clone()))
1083            }
1084            Some(chroma_proto::update_metadata_value::Value::DoubleListValue(value)) => {
1085                if value.values.iter().any(|v| !v.is_finite()) {
1086                    return Err(MetadataValueConversionError::InvalidValue);
1087                }
1088                Ok(MetadataValue::FloatArray(value.values.clone()))
1089            }
1090            Some(chroma_proto::update_metadata_value::Value::StringListValue(value)) => {
1091                Ok(MetadataValue::StringArray(value.values.clone()))
1092            }
1093            _ => Err(MetadataValueConversionError::InvalidValue),
1094        }
1095    }
1096}
1097
1098impl From<MetadataValue> for chroma_proto::UpdateMetadataValue {
1099    fn from(value: MetadataValue) -> Self {
1100        match value {
1101            MetadataValue::Int(value) => chroma_proto::UpdateMetadataValue {
1102                value: Some(chroma_proto::update_metadata_value::Value::IntValue(value)),
1103            },
1104            MetadataValue::Float(value) => chroma_proto::UpdateMetadataValue {
1105                value: Some(chroma_proto::update_metadata_value::Value::FloatValue(
1106                    value,
1107                )),
1108            },
1109            MetadataValue::Str(value) => chroma_proto::UpdateMetadataValue {
1110                value: Some(chroma_proto::update_metadata_value::Value::StringValue(
1111                    value,
1112                )),
1113            },
1114            MetadataValue::Bool(value) => chroma_proto::UpdateMetadataValue {
1115                value: Some(chroma_proto::update_metadata_value::Value::BoolValue(value)),
1116            },
1117            MetadataValue::SparseVector(sparse_vec) => chroma_proto::UpdateMetadataValue {
1118                value: Some(
1119                    chroma_proto::update_metadata_value::Value::SparseVectorValue(
1120                        sparse_vec.into(),
1121                    ),
1122                ),
1123            },
1124            MetadataValue::BoolArray(values) => chroma_proto::UpdateMetadataValue {
1125                value: Some(chroma_proto::update_metadata_value::Value::BoolListValue(
1126                    chroma_proto::BoolListValue { values },
1127                )),
1128            },
1129            MetadataValue::IntArray(values) => chroma_proto::UpdateMetadataValue {
1130                value: Some(chroma_proto::update_metadata_value::Value::IntListValue(
1131                    chroma_proto::IntListValue { values },
1132                )),
1133            },
1134            MetadataValue::FloatArray(values) => chroma_proto::UpdateMetadataValue {
1135                value: Some(chroma_proto::update_metadata_value::Value::DoubleListValue(
1136                    chroma_proto::DoubleListValue { values },
1137                )),
1138            },
1139            MetadataValue::StringArray(values) => chroma_proto::UpdateMetadataValue {
1140                value: Some(chroma_proto::update_metadata_value::Value::StringListValue(
1141                    chroma_proto::StringListValue { values },
1142                )),
1143            },
1144        }
1145    }
1146}
1147
1148/*
1149===========================================
1150UpdateMetadata
1151===========================================
1152*/
1153pub type UpdateMetadata = HashMap<String, UpdateMetadataValue>;
1154
1155/**
1156 * Check if two metadata are close to equal. Ignores small differences in float values.
1157 */
1158pub fn are_update_metadatas_close_to_equal(
1159    metadata1: &UpdateMetadata,
1160    metadata2: &UpdateMetadata,
1161) -> bool {
1162    assert_eq!(metadata1.len(), metadata2.len());
1163
1164    for (key, value) in metadata1.iter() {
1165        if !metadata2.contains_key(key) {
1166            return false;
1167        }
1168        let other_value = metadata2.get(key).unwrap();
1169
1170        if let (UpdateMetadataValue::Float(value), UpdateMetadataValue::Float(other_value)) =
1171            (value, other_value)
1172        {
1173            if (value - other_value).abs() > 1e-6 {
1174                return false;
1175            }
1176        } else if value != other_value {
1177            return false;
1178        }
1179    }
1180
1181    true
1182}
1183
1184pub fn are_metadatas_close_to_equal(metadata1: &Metadata, metadata2: &Metadata) -> bool {
1185    assert_eq!(metadata1.len(), metadata2.len());
1186
1187    for (key, value) in metadata1.iter() {
1188        if !metadata2.contains_key(key) {
1189            return false;
1190        }
1191        let other_value = metadata2.get(key).unwrap();
1192
1193        if let (MetadataValue::Float(value), MetadataValue::Float(other_value)) =
1194            (value, other_value)
1195        {
1196            if (value - other_value).abs() > 1e-6 {
1197                return false;
1198            }
1199        } else if value != other_value {
1200            return false;
1201        }
1202    }
1203
1204    true
1205}
1206
1207impl TryFrom<chroma_proto::UpdateMetadata> for UpdateMetadata {
1208    type Error = UpdateMetadataValueConversionError;
1209
1210    fn try_from(proto_metadata: chroma_proto::UpdateMetadata) -> Result<Self, Self::Error> {
1211        let mut metadata = UpdateMetadata::with_capacity(proto_metadata.metadata.len());
1212        for (key, value) in proto_metadata.metadata.into_iter() {
1213            let value = match (&value).try_into() {
1214                Ok(value) => value,
1215                Err(_) => return Err(UpdateMetadataValueConversionError::InvalidValue),
1216            };
1217            metadata.insert(key, value);
1218        }
1219        Ok(metadata)
1220    }
1221}
1222
1223impl From<UpdateMetadata> for chroma_proto::UpdateMetadata {
1224    fn from(metadata: UpdateMetadata) -> Self {
1225        let mut proto_metadata = chroma_proto::UpdateMetadata {
1226            metadata: HashMap::with_capacity(metadata.len()),
1227        };
1228        for (key, value) in metadata.into_iter() {
1229            let proto_value = value.into();
1230            proto_metadata.metadata.insert(key, proto_value);
1231        }
1232        proto_metadata
1233    }
1234}
1235
1236/*
1237===========================================
1238Metadata
1239===========================================
1240*/
1241
1242pub type Metadata = HashMap<String, MetadataValue>;
1243pub type DeletedMetadata = HashSet<String>;
1244
1245pub fn logical_size_of_metadata(metadata: &Metadata) -> usize {
1246    metadata
1247        .iter()
1248        .map(|(k, v)| {
1249            k.len()
1250                + match v {
1251                    MetadataValue::Bool(b) => size_of_val(b),
1252                    MetadataValue::Int(i) => size_of_val(i),
1253                    MetadataValue::Float(f) => size_of_val(f),
1254                    MetadataValue::Str(s) => s.len(),
1255                    MetadataValue::SparseVector(v) => {
1256                        size_of_val(&v.indices[..]) + size_of_val(&v.values[..])
1257                    }
1258                    MetadataValue::BoolArray(arr) => size_of_val(&arr[..]),
1259                    MetadataValue::IntArray(arr) => size_of_val(&arr[..]),
1260                    MetadataValue::FloatArray(arr) => size_of_val(&arr[..]),
1261                    MetadataValue::StringArray(arr) => arr.iter().map(|s| s.len()).sum::<usize>(),
1262                }
1263        })
1264        .sum()
1265}
1266
1267pub fn get_metadata_value_as<'a, T>(
1268    metadata: &'a Metadata,
1269    key: &str,
1270) -> Result<T, Box<MetadataValueConversionError>>
1271where
1272    T: TryFrom<&'a MetadataValue, Error = MetadataValueConversionError>,
1273{
1274    let res = match metadata.get(key) {
1275        Some(value) => T::try_from(value),
1276        None => return Err(Box::new(MetadataValueConversionError::InvalidValue)),
1277    };
1278    match res {
1279        Ok(value) => Ok(value),
1280        Err(_) => Err(Box::new(MetadataValueConversionError::InvalidValue)),
1281    }
1282}
1283
1284impl TryFrom<chroma_proto::UpdateMetadata> for Metadata {
1285    type Error = MetadataValueConversionError;
1286
1287    fn try_from(proto_metadata: chroma_proto::UpdateMetadata) -> Result<Self, Self::Error> {
1288        let mut metadata = Metadata::new();
1289        for (key, value) in proto_metadata.metadata.iter() {
1290            let maybe_value: Result<MetadataValue, Self::Error> = value.try_into();
1291            if maybe_value.is_err() {
1292                return Err(MetadataValueConversionError::InvalidValue);
1293            }
1294            let value = maybe_value.unwrap();
1295            metadata.insert(key.clone(), value);
1296        }
1297        Ok(metadata)
1298    }
1299}
1300
1301impl From<Metadata> for chroma_proto::UpdateMetadata {
1302    fn from(metadata: Metadata) -> Self {
1303        let mut metadata = metadata;
1304        let mut proto_metadata = chroma_proto::UpdateMetadata {
1305            metadata: HashMap::new(),
1306        };
1307        for (key, value) in metadata.drain() {
1308            let proto_value = value.into();
1309            proto_metadata.metadata.insert(key.clone(), proto_value);
1310        }
1311        proto_metadata
1312    }
1313}
1314
1315#[derive(Debug, Default)]
1316pub struct MetadataDelta<'referred_data> {
1317    pub metadata_to_update: HashMap<
1318        &'referred_data str,
1319        (&'referred_data MetadataValue, &'referred_data MetadataValue),
1320    >,
1321    pub metadata_to_delete: HashMap<&'referred_data str, &'referred_data MetadataValue>,
1322    pub metadata_to_insert: HashMap<&'referred_data str, &'referred_data MetadataValue>,
1323}
1324
1325impl MetadataDelta<'_> {
1326    pub fn new() -> Self {
1327        Self::default()
1328    }
1329}
1330
1331/*
1332===========================================
1333Metadata queries
1334===========================================
1335*/
1336
1337#[derive(Clone, Debug, Error, PartialEq)]
1338pub enum WhereConversionError {
1339    #[error("Error: {0}")]
1340    Cause(String),
1341    #[error("{0} -> {1}")]
1342    Trace(String, Box<Self>),
1343}
1344
1345impl WhereConversionError {
1346    pub fn cause(msg: impl ToString) -> Self {
1347        Self::Cause(msg.to_string())
1348    }
1349
1350    pub fn trace(self, context: impl ToString) -> Self {
1351        Self::Trace(context.to_string(), Box::new(self))
1352    }
1353}
1354
1355/// This `Where` enum serves as an unified representation for the `where` and `where_document` clauses.
1356/// Although this is not unified in the API level due to legacy design choices, in the future we will be
1357/// unifying them together, and the structure of the unified AST should be identical to the one here.
1358/// Currently both `where` and `where_document` clauses will be translated into `Where`, and if both are
1359/// present we simply create a conjunction of both clauses as the actual filter. This is consistent with
1360/// the semantics we used to have when the `where` and `where_document` clauses are treated seperately.
1361// TODO: Remove this note once the `where` clause and `where_document` clause is unified in the API level.
1362#[derive(Clone, Debug, PartialEq)]
1363#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1364pub enum Where {
1365    Composite(CompositeExpression),
1366    Document(DocumentExpression),
1367    Metadata(MetadataExpression),
1368}
1369
1370impl std::fmt::Display for Where {
1371    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1372        match self {
1373            Where::Composite(composite) => {
1374                let fragment = composite
1375                    .children
1376                    .iter()
1377                    .map(|child| format!("{}", child))
1378                    .collect::<Vec<_>>()
1379                    .join(match composite.operator {
1380                        BooleanOperator::And => " & ",
1381                        BooleanOperator::Or => " | ",
1382                    });
1383                write!(f, "({})", fragment)
1384            }
1385            Where::Metadata(expr) => write!(f, "{}", expr),
1386            Where::Document(expr) => write!(f, "{}", expr),
1387        }
1388    }
1389}
1390
1391impl serde::Serialize for Where {
1392    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1393    where
1394        S: Serializer,
1395    {
1396        match self {
1397            Where::Composite(composite) => {
1398                let mut map = serializer.serialize_map(Some(1))?;
1399                let op_key = match composite.operator {
1400                    BooleanOperator::And => "$and",
1401                    BooleanOperator::Or => "$or",
1402                };
1403                map.serialize_entry(op_key, &composite.children)?;
1404                map.end()
1405            }
1406            Where::Document(doc) => {
1407                let mut outer_map = serializer.serialize_map(Some(1))?;
1408                let mut inner_map = serde_json::Map::new();
1409                let op_key = match doc.operator {
1410                    DocumentOperator::Contains => "$contains",
1411                    DocumentOperator::NotContains => "$not_contains",
1412                    DocumentOperator::Regex => "$regex",
1413                    DocumentOperator::NotRegex => "$not_regex",
1414                };
1415                inner_map.insert(
1416                    op_key.to_string(),
1417                    serde_json::Value::String(doc.pattern.clone()),
1418                );
1419                outer_map.serialize_entry("#document", &inner_map)?;
1420                outer_map.end()
1421            }
1422            Where::Metadata(meta) => {
1423                let mut outer_map = serializer.serialize_map(Some(1))?;
1424                let mut inner_map = serde_json::Map::new();
1425
1426                match &meta.comparison {
1427                    MetadataComparison::Primitive(op, value) => {
1428                        let op_key = match op {
1429                            PrimitiveOperator::Equal => "$eq",
1430                            PrimitiveOperator::NotEqual => "$ne",
1431                            PrimitiveOperator::GreaterThan => "$gt",
1432                            PrimitiveOperator::GreaterThanOrEqual => "$gte",
1433                            PrimitiveOperator::LessThan => "$lt",
1434                            PrimitiveOperator::LessThanOrEqual => "$lte",
1435                        };
1436                        let value_json =
1437                            serde_json::to_value(value).map_err(serde::ser::Error::custom)?;
1438                        inner_map.insert(op_key.to_string(), value_json);
1439                    }
1440                    MetadataComparison::Set(op, set_value) => {
1441                        let op_key = match op {
1442                            SetOperator::In => "$in",
1443                            SetOperator::NotIn => "$nin",
1444                        };
1445                        let values_json = match set_value {
1446                            MetadataSetValue::Bool(v) => serde_json::to_value(v),
1447                            MetadataSetValue::Int(v) => serde_json::to_value(v),
1448                            MetadataSetValue::Float(v) => serde_json::to_value(v),
1449                            MetadataSetValue::Str(v) => serde_json::to_value(v),
1450                        }
1451                        .map_err(serde::ser::Error::custom)?;
1452                        inner_map.insert(op_key.to_string(), values_json);
1453                    }
1454                    MetadataComparison::ArrayContains(op, value) => {
1455                        let op_key = match op {
1456                            ContainsOperator::Contains => "$contains",
1457                            ContainsOperator::NotContains => "$not_contains",
1458                        };
1459                        let value_json =
1460                            serde_json::to_value(value).map_err(serde::ser::Error::custom)?;
1461                        inner_map.insert(op_key.to_string(), value_json);
1462                    }
1463                }
1464
1465                outer_map.serialize_entry(&meta.key, &inner_map)?;
1466                outer_map.end()
1467            }
1468        }
1469    }
1470}
1471
1472impl From<bool> for Where {
1473    fn from(value: bool) -> Self {
1474        if value {
1475            Where::conjunction(vec![])
1476        } else {
1477            Where::disjunction(vec![])
1478        }
1479    }
1480}
1481
1482impl Where {
1483    pub fn conjunction(children: impl IntoIterator<Item = Where>) -> Self {
1484        // If children.len() == 0, we will return a conjunction that is always true.
1485        // If children.len() == 1, we will return the single child.
1486        // Otherwise, we will return a conjunction of the children.
1487
1488        let mut children: Vec<_> = children
1489            .into_iter()
1490            .flat_map(|expr| {
1491                if let Where::Composite(CompositeExpression {
1492                    operator: BooleanOperator::And,
1493                    children,
1494                }) = expr
1495                {
1496                    return children;
1497                }
1498                vec![expr]
1499            })
1500            .dedup()
1501            .collect();
1502
1503        if children.len() == 1 {
1504            return children.pop().expect("just checked len is 1");
1505        }
1506
1507        Self::Composite(CompositeExpression {
1508            operator: BooleanOperator::And,
1509            children,
1510        })
1511    }
1512    pub fn disjunction(children: impl IntoIterator<Item = Where>) -> Self {
1513        // If children.len() == 0, we will return a disjunction that is always false.
1514        // If children.len() == 1, we will return the single child.
1515        // Otherwise, we will return a disjunction of the children.
1516
1517        let mut children: Vec<_> = children
1518            .into_iter()
1519            .flat_map(|expr| {
1520                if let Where::Composite(CompositeExpression {
1521                    operator: BooleanOperator::Or,
1522                    children,
1523                }) = expr
1524                {
1525                    return children;
1526                }
1527                vec![expr]
1528            })
1529            .dedup()
1530            .collect();
1531
1532        if children.len() == 1 {
1533            return children.pop().expect("just checked len is 1");
1534        }
1535
1536        Self::Composite(CompositeExpression {
1537            operator: BooleanOperator::Or,
1538            children,
1539        })
1540    }
1541
1542    pub fn fts_query_length(&self) -> u64 {
1543        match self {
1544            Where::Composite(composite_expression) => composite_expression
1545                .children
1546                .iter()
1547                .map(Where::fts_query_length)
1548                .sum(),
1549            // The query length is defined to be the number of trigram tokens
1550            Where::Document(document_expression) => {
1551                document_expression.pattern.len().max(3) as u64 - 2
1552            }
1553            Where::Metadata(_) => 0,
1554        }
1555    }
1556
1557    pub fn metadata_predicate_count(&self) -> u64 {
1558        match self {
1559            Where::Composite(composite_expression) => composite_expression
1560                .children
1561                .iter()
1562                .map(Where::metadata_predicate_count)
1563                .sum(),
1564            Where::Document(_) => 0,
1565            Where::Metadata(metadata_expression) => match &metadata_expression.comparison {
1566                MetadataComparison::Primitive(_, _) => 1,
1567                MetadataComparison::Set(_, metadata_set_value) => match metadata_set_value {
1568                    MetadataSetValue::Bool(items) => items.len() as u64,
1569                    MetadataSetValue::Int(items) => items.len() as u64,
1570                    MetadataSetValue::Float(items) => items.len() as u64,
1571                    MetadataSetValue::Str(items) => items.len() as u64,
1572                },
1573                MetadataComparison::ArrayContains(_, _) => 1,
1574            },
1575        }
1576    }
1577}
1578
1579impl BitAnd for Where {
1580    type Output = Where;
1581
1582    fn bitand(self, rhs: Self) -> Self::Output {
1583        Self::conjunction([self, rhs])
1584    }
1585}
1586
1587impl BitOr for Where {
1588    type Output = Where;
1589
1590    fn bitor(self, rhs: Self) -> Self::Output {
1591        Self::disjunction([self, rhs])
1592    }
1593}
1594
1595impl TryFrom<chroma_proto::Where> for Where {
1596    type Error = WhereConversionError;
1597
1598    fn try_from(proto_where: chroma_proto::Where) -> Result<Self, Self::Error> {
1599        let where_inner = proto_where
1600            .r#where
1601            .ok_or(WhereConversionError::cause("Invalid Where"))?;
1602        Ok(match where_inner {
1603            chroma_proto::r#where::Where::DirectComparison(direct_comparison) => {
1604                Self::Metadata(direct_comparison.try_into()?)
1605            }
1606            chroma_proto::r#where::Where::Children(where_children) => {
1607                Self::Composite(where_children.try_into()?)
1608            }
1609            chroma_proto::r#where::Where::DirectDocumentComparison(direct_where_document) => {
1610                Self::Document(direct_where_document.into())
1611            }
1612        })
1613    }
1614}
1615
1616impl TryFrom<Where> for chroma_proto::Where {
1617    type Error = WhereConversionError;
1618
1619    fn try_from(value: Where) -> Result<Self, Self::Error> {
1620        let proto_where = match value {
1621            Where::Composite(composite_expression) => {
1622                chroma_proto::r#where::Where::Children(composite_expression.try_into()?)
1623            }
1624            Where::Document(document_expression) => {
1625                chroma_proto::r#where::Where::DirectDocumentComparison(document_expression.into())
1626            }
1627            Where::Metadata(metadata_expression) => chroma_proto::r#where::Where::DirectComparison(
1628                chroma_proto::DirectComparison::try_from(metadata_expression)
1629                    .map_err(|err| err.trace("MetadataExpression"))?,
1630            ),
1631        };
1632        Ok(Self {
1633            r#where: Some(proto_where),
1634        })
1635    }
1636}
1637
1638#[derive(Clone, Debug, PartialEq)]
1639#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1640pub struct CompositeExpression {
1641    pub operator: BooleanOperator,
1642    pub children: Vec<Where>,
1643}
1644
1645impl TryFrom<chroma_proto::WhereChildren> for CompositeExpression {
1646    type Error = WhereConversionError;
1647
1648    fn try_from(proto_children: chroma_proto::WhereChildren) -> Result<Self, Self::Error> {
1649        let operator = proto_children.operator().into();
1650        let children = proto_children
1651            .children
1652            .into_iter()
1653            .map(Where::try_from)
1654            .collect::<Result<Vec<_>, _>>()
1655            .map_err(|err| err.trace("Child Where of CompositeExpression"))?;
1656        Ok(Self { operator, children })
1657    }
1658}
1659
1660impl TryFrom<CompositeExpression> for chroma_proto::WhereChildren {
1661    type Error = WhereConversionError;
1662
1663    fn try_from(value: CompositeExpression) -> Result<Self, Self::Error> {
1664        Ok(Self {
1665            operator: chroma_proto::BooleanOperator::from(value.operator) as i32,
1666            children: value
1667                .children
1668                .into_iter()
1669                .map(chroma_proto::Where::try_from)
1670                .collect::<Result<_, _>>()?,
1671        })
1672    }
1673}
1674
1675#[derive(Clone, Debug, PartialEq)]
1676#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1677pub enum BooleanOperator {
1678    And,
1679    Or,
1680}
1681
1682impl From<chroma_proto::BooleanOperator> for BooleanOperator {
1683    fn from(value: chroma_proto::BooleanOperator) -> Self {
1684        match value {
1685            chroma_proto::BooleanOperator::And => Self::And,
1686            chroma_proto::BooleanOperator::Or => Self::Or,
1687        }
1688    }
1689}
1690
1691impl From<BooleanOperator> for chroma_proto::BooleanOperator {
1692    fn from(value: BooleanOperator) -> Self {
1693        match value {
1694            BooleanOperator::And => Self::And,
1695            BooleanOperator::Or => Self::Or,
1696        }
1697    }
1698}
1699
1700#[derive(Clone, Debug, PartialEq)]
1701#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1702pub struct DocumentExpression {
1703    pub operator: DocumentOperator,
1704    pub pattern: String,
1705}
1706
1707impl std::fmt::Display for DocumentExpression {
1708    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1709        let op_str = match self.operator {
1710            DocumentOperator::Contains => "CONTAINS",
1711            DocumentOperator::NotContains => "NOT CONTAINS",
1712            DocumentOperator::Regex => "REGEX",
1713            DocumentOperator::NotRegex => "NOT REGEX",
1714        };
1715        write!(f, "#document {} \"{}\"", op_str, self.pattern)
1716    }
1717}
1718
1719impl From<chroma_proto::DirectWhereDocument> for DocumentExpression {
1720    fn from(value: chroma_proto::DirectWhereDocument) -> Self {
1721        Self {
1722            operator: value.operator().into(),
1723            pattern: value.pattern,
1724        }
1725    }
1726}
1727
1728impl From<DocumentExpression> for chroma_proto::DirectWhereDocument {
1729    fn from(value: DocumentExpression) -> Self {
1730        Self {
1731            pattern: value.pattern,
1732            operator: chroma_proto::WhereDocumentOperator::from(value.operator) as i32,
1733        }
1734    }
1735}
1736
1737#[derive(Clone, Debug, PartialEq)]
1738#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1739pub enum DocumentOperator {
1740    Contains,
1741    NotContains,
1742    Regex,
1743    NotRegex,
1744}
1745impl From<chroma_proto::WhereDocumentOperator> for DocumentOperator {
1746    fn from(value: chroma_proto::WhereDocumentOperator) -> Self {
1747        match value {
1748            chroma_proto::WhereDocumentOperator::Contains => Self::Contains,
1749            chroma_proto::WhereDocumentOperator::NotContains => Self::NotContains,
1750            chroma_proto::WhereDocumentOperator::Regex => Self::Regex,
1751            chroma_proto::WhereDocumentOperator::NotRegex => Self::NotRegex,
1752        }
1753    }
1754}
1755
1756impl From<DocumentOperator> for chroma_proto::WhereDocumentOperator {
1757    fn from(value: DocumentOperator) -> Self {
1758        match value {
1759            DocumentOperator::Contains => Self::Contains,
1760            DocumentOperator::NotContains => Self::NotContains,
1761            DocumentOperator::Regex => Self::Regex,
1762            DocumentOperator::NotRegex => Self::NotRegex,
1763        }
1764    }
1765}
1766
1767#[derive(Clone, Debug, PartialEq)]
1768#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1769pub struct MetadataExpression {
1770    pub key: String,
1771    pub comparison: MetadataComparison,
1772}
1773
1774impl std::fmt::Display for MetadataExpression {
1775    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1776        match &self.comparison {
1777            MetadataComparison::Primitive(op, value) => {
1778                write!(f, "{} {} {}", self.key, op, value)
1779            }
1780            MetadataComparison::Set(op, set_value) => {
1781                write!(f, "{} {} {}", self.key, op, set_value)
1782            }
1783            MetadataComparison::ArrayContains(op, value) => {
1784                write!(f, "{} {} {}", self.key, op, value)
1785            }
1786        }
1787    }
1788}
1789
1790/// Helper to convert a `GenericComparator` and a `MetadataValue` into either a
1791/// `MetadataComparison::Primitive` (for EQ/NE) or `MetadataComparison::Contains`
1792/// (for CONTAINS/NOT_CONTAINS).
1793fn generic_comparator_to_metadata_comparison(
1794    comparator: chroma_proto::GenericComparator,
1795    value: MetadataValue,
1796) -> MetadataComparison {
1797    match comparator {
1798        chroma_proto::GenericComparator::Eq | chroma_proto::GenericComparator::Ne => {
1799            // SAFETY: We just matched Eq | Ne, so try_into() a
1800            // PrimitiveOperator will always succeed.
1801            MetadataComparison::Primitive(comparator.try_into().unwrap(), value)
1802        }
1803        chroma_proto::GenericComparator::ArrayContains => {
1804            MetadataComparison::ArrayContains(ContainsOperator::Contains, value)
1805        }
1806        chroma_proto::GenericComparator::ArrayNotContains => {
1807            MetadataComparison::ArrayContains(ContainsOperator::NotContains, value)
1808        }
1809    }
1810}
1811
1812impl TryFrom<chroma_proto::DirectComparison> for MetadataExpression {
1813    type Error = WhereConversionError;
1814
1815    fn try_from(value: chroma_proto::DirectComparison) -> Result<Self, Self::Error> {
1816        let proto_comparison = value
1817            .comparison
1818            .ok_or(WhereConversionError::cause("Invalid MetadataExpression"))?;
1819        let comparison = match proto_comparison {
1820            chroma_proto::direct_comparison::Comparison::SingleStringOperand(
1821                single_string_comparison,
1822            ) => generic_comparator_to_metadata_comparison(
1823                single_string_comparison.comparator(),
1824                MetadataValue::Str(single_string_comparison.value),
1825            ),
1826            chroma_proto::direct_comparison::Comparison::StringListOperand(
1827                string_list_comparison,
1828            ) => MetadataComparison::Set(
1829                string_list_comparison.list_operator().into(),
1830                MetadataSetValue::Str(string_list_comparison.values),
1831            ),
1832            chroma_proto::direct_comparison::Comparison::SingleIntOperand(
1833                single_int_comparison,
1834            ) => {
1835                let comparator =
1836                    single_int_comparison
1837                        .comparator
1838                        .ok_or(WhereConversionError::cause(
1839                            "Invalid scalar integer operator",
1840                        ))?;
1841                let value = MetadataValue::Int(single_int_comparison.value);
1842                match comparator {
1843                    chroma_proto::single_int_comparison::Comparator::GenericComparator(op) => {
1844                        let generic = chroma_proto::GenericComparator::try_from(op)
1845                            .map_err(WhereConversionError::cause)?;
1846                        generic_comparator_to_metadata_comparison(generic, value)
1847                    }
1848                    chroma_proto::single_int_comparison::Comparator::NumberComparator(op) => {
1849                        MetadataComparison::Primitive(
1850                            chroma_proto::NumberComparator::try_from(op)
1851                                .map_err(WhereConversionError::cause)?
1852                                .into(),
1853                            value,
1854                        )
1855                    }
1856                }
1857            }
1858            chroma_proto::direct_comparison::Comparison::IntListOperand(int_list_comparison) => {
1859                MetadataComparison::Set(
1860                    int_list_comparison.list_operator().into(),
1861                    MetadataSetValue::Int(int_list_comparison.values),
1862                )
1863            }
1864            chroma_proto::direct_comparison::Comparison::SingleDoubleOperand(
1865                single_double_comparison,
1866            ) => {
1867                let comparator = single_double_comparison
1868                    .comparator
1869                    .ok_or(WhereConversionError::cause("Invalid scalar float operator"))?;
1870                let value = MetadataValue::Float(single_double_comparison.value);
1871                match comparator {
1872                    chroma_proto::single_double_comparison::Comparator::GenericComparator(op) => {
1873                        let generic = chroma_proto::GenericComparator::try_from(op)
1874                            .map_err(WhereConversionError::cause)?;
1875                        generic_comparator_to_metadata_comparison(generic, value)
1876                    }
1877                    chroma_proto::single_double_comparison::Comparator::NumberComparator(op) => {
1878                        MetadataComparison::Primitive(
1879                            chroma_proto::NumberComparator::try_from(op)
1880                                .map_err(WhereConversionError::cause)?
1881                                .into(),
1882                            value,
1883                        )
1884                    }
1885                }
1886            }
1887            chroma_proto::direct_comparison::Comparison::DoubleListOperand(
1888                double_list_comparison,
1889            ) => MetadataComparison::Set(
1890                double_list_comparison.list_operator().into(),
1891                MetadataSetValue::Float(double_list_comparison.values),
1892            ),
1893            chroma_proto::direct_comparison::Comparison::BoolListOperand(bool_list_comparison) => {
1894                MetadataComparison::Set(
1895                    bool_list_comparison.list_operator().into(),
1896                    MetadataSetValue::Bool(bool_list_comparison.values),
1897                )
1898            }
1899            chroma_proto::direct_comparison::Comparison::SingleBoolOperand(
1900                single_bool_comparison,
1901            ) => generic_comparator_to_metadata_comparison(
1902                single_bool_comparison.comparator(),
1903                MetadataValue::Bool(single_bool_comparison.value),
1904            ),
1905        };
1906        Ok(Self {
1907            key: value.key,
1908            comparison,
1909        })
1910    }
1911}
1912
1913impl TryFrom<MetadataExpression> for chroma_proto::DirectComparison {
1914    type Error = WhereConversionError;
1915
1916    fn try_from(value: MetadataExpression) -> Result<Self, Self::Error> {
1917        let comparison = match value.comparison {
1918            MetadataComparison::Primitive(primitive_operator, metadata_value) => match metadata_value {
1919                MetadataValue::Bool(value) => chroma_proto::direct_comparison::Comparison::SingleBoolOperand(chroma_proto::SingleBoolComparison { value, comparator: chroma_proto::GenericComparator::try_from(primitive_operator)? as i32 }),
1920                MetadataValue::Int(value) => chroma_proto::direct_comparison::Comparison::SingleIntOperand(chroma_proto::SingleIntComparison { value, comparator: Some(match primitive_operator {
1921                                generic_operator @ PrimitiveOperator::Equal | generic_operator @ PrimitiveOperator::NotEqual => chroma_proto::single_int_comparison::Comparator::GenericComparator(chroma_proto::GenericComparator::try_from(generic_operator)? as i32),
1922                                numeric => chroma_proto::single_int_comparison::Comparator::NumberComparator(chroma_proto::NumberComparator::try_from(numeric)? as i32) }),
1923                            }),
1924                MetadataValue::Float(value) => chroma_proto::direct_comparison::Comparison::SingleDoubleOperand(chroma_proto::SingleDoubleComparison { value, comparator: Some(match primitive_operator {
1925                                generic_operator @ PrimitiveOperator::Equal | generic_operator @ PrimitiveOperator::NotEqual => chroma_proto::single_double_comparison::Comparator::GenericComparator(chroma_proto::GenericComparator::try_from(generic_operator)? as i32),
1926                                numeric => chroma_proto::single_double_comparison::Comparator::NumberComparator(chroma_proto::NumberComparator::try_from(numeric)? as i32) }),
1927                            }),
1928                MetadataValue::Str(value) => chroma_proto::direct_comparison::Comparison::SingleStringOperand(chroma_proto::SingleStringComparison { value, comparator: chroma_proto::GenericComparator::try_from(primitive_operator)? as i32 }),
1929                MetadataValue::SparseVector(_) => return Err(WhereConversionError::Cause("Comparison with sparse vector is not supported".to_string())),
1930                MetadataValue::BoolArray(_) | MetadataValue::IntArray(_) | MetadataValue::FloatArray(_) | MetadataValue::StringArray(_) => {
1931                    return Err(WhereConversionError::Cause("Primitive comparison with array metadata values is not supported".to_string()))
1932                }
1933            },
1934            MetadataComparison::Set(set_operator, metadata_set_value) => match metadata_set_value {
1935                MetadataSetValue::Bool(vec) => chroma_proto::direct_comparison::Comparison::BoolListOperand(chroma_proto::BoolListComparison { values: vec, list_operator: chroma_proto::ListOperator::from(set_operator) as i32 }),
1936                MetadataSetValue::Int(vec) => chroma_proto::direct_comparison::Comparison::IntListOperand(chroma_proto::IntListComparison { values: vec, list_operator: chroma_proto::ListOperator::from(set_operator) as i32 }),
1937                MetadataSetValue::Float(vec) => chroma_proto::direct_comparison::Comparison::DoubleListOperand(chroma_proto::DoubleListComparison { values: vec, list_operator: chroma_proto::ListOperator::from(set_operator) as i32 }),
1938                MetadataSetValue::Str(vec) => chroma_proto::direct_comparison::Comparison::StringListOperand(chroma_proto::StringListComparison { values: vec, list_operator: chroma_proto::ListOperator::from(set_operator) as i32 }),
1939            },
1940            MetadataComparison::ArrayContains(contains_operator, metadata_value) => {
1941                let comparator = chroma_proto::GenericComparator::from(contains_operator) as i32;
1942                match metadata_value {
1943                    MetadataValue::Bool(value) => chroma_proto::direct_comparison::Comparison::SingleBoolOperand(chroma_proto::SingleBoolComparison { value, comparator }),
1944                    MetadataValue::Int(value) => chroma_proto::direct_comparison::Comparison::SingleIntOperand(chroma_proto::SingleIntComparison { value, comparator: Some(chroma_proto::single_int_comparison::Comparator::GenericComparator(comparator)) }),
1945                    MetadataValue::Float(value) => chroma_proto::direct_comparison::Comparison::SingleDoubleOperand(chroma_proto::SingleDoubleComparison { value, comparator: Some(chroma_proto::single_double_comparison::Comparator::GenericComparator(comparator)) }),
1946                    MetadataValue::Str(value) => chroma_proto::direct_comparison::Comparison::SingleStringOperand(chroma_proto::SingleStringComparison { value, comparator }),
1947                    MetadataValue::SparseVector(_) => return Err(WhereConversionError::Cause("Contains comparison with sparse vector is not supported".to_string())),
1948                    MetadataValue::BoolArray(_) | MetadataValue::IntArray(_) | MetadataValue::FloatArray(_) | MetadataValue::StringArray(_) => {
1949                        return Err(WhereConversionError::Cause("Contains comparison value must be a scalar, not an array".to_string()))
1950                    }
1951                }
1952            },
1953        };
1954        Ok(Self {
1955            key: value.key,
1956            comparison: Some(comparison),
1957        })
1958    }
1959}
1960
1961#[derive(Clone, Debug, PartialEq)]
1962#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1963pub enum MetadataComparison {
1964    Primitive(PrimitiveOperator, MetadataValue),
1965    Set(SetOperator, MetadataSetValue),
1966    /// Array contains: check if an array metadata field contains (or does not
1967    /// contain) a specific scalar value.
1968    ArrayContains(ContainsOperator, MetadataValue),
1969}
1970
1971impl std::fmt::Display for MetadataComparison {
1972    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1973        match self {
1974            MetadataComparison::Primitive(op, val) => {
1975                let type_name = match val {
1976                    MetadataValue::Bool(_) => "Bool",
1977                    MetadataValue::Int(_) => "Int",
1978                    MetadataValue::Float(_) => "Float",
1979                    MetadataValue::Str(_) => "Str",
1980                    MetadataValue::SparseVector(_) => "SparseVector",
1981                    MetadataValue::BoolArray(_) => "BoolArray",
1982                    MetadataValue::IntArray(_) => "IntArray",
1983                    MetadataValue::FloatArray(_) => "FloatArray",
1984                    MetadataValue::StringArray(_) => "StringArray",
1985                };
1986                write!(f, "Primitive({}, {})", op, type_name)
1987            }
1988            MetadataComparison::Set(op, val) => {
1989                let type_name = match val {
1990                    MetadataSetValue::Bool(_) => "Bool",
1991                    MetadataSetValue::Int(_) => "Int",
1992                    MetadataSetValue::Float(_) => "Float",
1993                    MetadataSetValue::Str(_) => "Str",
1994                };
1995                write!(f, "Set({}, {})", op, type_name)
1996            }
1997            MetadataComparison::ArrayContains(op, val) => {
1998                let type_name = match val {
1999                    MetadataValue::Bool(_) => "Bool",
2000                    MetadataValue::Int(_) => "Int",
2001                    MetadataValue::Float(_) => "Float",
2002                    MetadataValue::Str(_) => "Str",
2003                    MetadataValue::SparseVector(_) => "SparseVector",
2004                    MetadataValue::BoolArray(_) => "BoolArray",
2005                    MetadataValue::IntArray(_) => "IntArray",
2006                    MetadataValue::FloatArray(_) => "FloatArray",
2007                    MetadataValue::StringArray(_) => "StringArray",
2008                };
2009                write!(f, "ArrayContains({}, {})", op, type_name)
2010            }
2011        }
2012    }
2013}
2014
2015#[derive(Clone, Debug, PartialEq)]
2016#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
2017pub enum PrimitiveOperator {
2018    Equal,
2019    NotEqual,
2020    GreaterThan,
2021    GreaterThanOrEqual,
2022    LessThan,
2023    LessThanOrEqual,
2024}
2025
2026impl std::fmt::Display for PrimitiveOperator {
2027    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2028        let op_str = match self {
2029            PrimitiveOperator::Equal => "=",
2030            PrimitiveOperator::NotEqual => "≠",
2031            PrimitiveOperator::GreaterThan => ">",
2032            PrimitiveOperator::GreaterThanOrEqual => "≥",
2033            PrimitiveOperator::LessThan => "<",
2034            PrimitiveOperator::LessThanOrEqual => "≤",
2035        };
2036        write!(f, "{}", op_str)
2037    }
2038}
2039
2040impl TryFrom<chroma_proto::GenericComparator> for PrimitiveOperator {
2041    type Error = WhereConversionError;
2042
2043    fn try_from(value: chroma_proto::GenericComparator) -> Result<Self, Self::Error> {
2044        match value {
2045            chroma_proto::GenericComparator::Eq => Ok(Self::Equal),
2046            chroma_proto::GenericComparator::Ne => Ok(Self::NotEqual),
2047            chroma_proto::GenericComparator::ArrayContains
2048            | chroma_proto::GenericComparator::ArrayNotContains => {
2049                Err(WhereConversionError::cause(
2050                    "ArrayContains/ArrayNotContains cannot be converted to PrimitiveOperator",
2051                ))
2052            }
2053        }
2054    }
2055}
2056
2057impl TryFrom<PrimitiveOperator> for chroma_proto::GenericComparator {
2058    type Error = WhereConversionError;
2059
2060    fn try_from(value: PrimitiveOperator) -> Result<Self, Self::Error> {
2061        match value {
2062            PrimitiveOperator::Equal => Ok(Self::Eq),
2063            PrimitiveOperator::NotEqual => Ok(Self::Ne),
2064            op => Err(WhereConversionError::cause(format!("{op:?} ∉ [=, ≠]"))),
2065        }
2066    }
2067}
2068
2069impl From<chroma_proto::NumberComparator> for PrimitiveOperator {
2070    fn from(value: chroma_proto::NumberComparator) -> Self {
2071        match value {
2072            chroma_proto::NumberComparator::Gt => Self::GreaterThan,
2073            chroma_proto::NumberComparator::Gte => Self::GreaterThanOrEqual,
2074            chroma_proto::NumberComparator::Lt => Self::LessThan,
2075            chroma_proto::NumberComparator::Lte => Self::LessThanOrEqual,
2076        }
2077    }
2078}
2079
2080impl TryFrom<PrimitiveOperator> for chroma_proto::NumberComparator {
2081    type Error = WhereConversionError;
2082
2083    fn try_from(value: PrimitiveOperator) -> Result<Self, Self::Error> {
2084        match value {
2085            PrimitiveOperator::GreaterThan => Ok(Self::Gt),
2086            PrimitiveOperator::GreaterThanOrEqual => Ok(Self::Gte),
2087            PrimitiveOperator::LessThan => Ok(Self::Lt),
2088            PrimitiveOperator::LessThanOrEqual => Ok(Self::Lte),
2089            op => Err(WhereConversionError::cause(format!(
2090                "{op:?} ∉ [≤, <, >, ≥]"
2091            ))),
2092        }
2093    }
2094}
2095
2096#[derive(Clone, Debug, PartialEq, Eq)]
2097#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
2098pub enum SetOperator {
2099    In,
2100    NotIn,
2101}
2102
2103impl std::fmt::Display for SetOperator {
2104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2105        let op_str = match self {
2106            SetOperator::In => "∈",
2107            SetOperator::NotIn => "∉",
2108        };
2109        write!(f, "{}", op_str)
2110    }
2111}
2112
2113impl From<chroma_proto::ListOperator> for SetOperator {
2114    fn from(value: chroma_proto::ListOperator) -> Self {
2115        match value {
2116            chroma_proto::ListOperator::In => Self::In,
2117            chroma_proto::ListOperator::Nin => Self::NotIn,
2118        }
2119    }
2120}
2121
2122impl From<SetOperator> for chroma_proto::ListOperator {
2123    fn from(value: SetOperator) -> Self {
2124        match value {
2125            SetOperator::In => Self::In,
2126            SetOperator::NotIn => Self::Nin,
2127        }
2128    }
2129}
2130
2131#[derive(Clone, Debug, PartialEq, Eq)]
2132#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
2133pub enum ContainsOperator {
2134    Contains,
2135    NotContains,
2136}
2137
2138impl std::fmt::Display for ContainsOperator {
2139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2140        let op_str = match self {
2141            ContainsOperator::Contains => "contains",
2142            ContainsOperator::NotContains => "not_contains",
2143        };
2144        write!(f, "{}", op_str)
2145    }
2146}
2147
2148impl From<ContainsOperator> for chroma_proto::GenericComparator {
2149    fn from(value: ContainsOperator) -> Self {
2150        match value {
2151            ContainsOperator::Contains => Self::ArrayContains,
2152            ContainsOperator::NotContains => Self::ArrayNotContains,
2153        }
2154    }
2155}
2156
2157#[derive(Clone, Debug, PartialEq)]
2158#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
2159pub enum MetadataSetValue {
2160    Bool(Vec<bool>),
2161    Int(Vec<i64>),
2162    Float(Vec<f64>),
2163    Str(Vec<String>),
2164}
2165
2166impl std::fmt::Display for MetadataSetValue {
2167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2168        match self {
2169            MetadataSetValue::Bool(values) => {
2170                let values_str = values
2171                    .iter()
2172                    .map(|v| format!("\"{}\"", v))
2173                    .collect::<Vec<_>>()
2174                    .join(", ");
2175                write!(f, "[{}]", values_str)
2176            }
2177            MetadataSetValue::Int(values) => {
2178                let values_str = values
2179                    .iter()
2180                    .map(|v| v.to_string())
2181                    .collect::<Vec<_>>()
2182                    .join(", ");
2183                write!(f, "[{}]", values_str)
2184            }
2185            MetadataSetValue::Float(values) => {
2186                let values_str = values
2187                    .iter()
2188                    .map(|v| v.to_string())
2189                    .collect::<Vec<_>>()
2190                    .join(", ");
2191                write!(f, "[{}]", values_str)
2192            }
2193            MetadataSetValue::Str(values) => {
2194                let values_str = values
2195                    .iter()
2196                    .map(|v| format!("\"{}\"", v))
2197                    .collect::<Vec<_>>()
2198                    .join(", ");
2199                write!(f, "[{}]", values_str)
2200            }
2201        }
2202    }
2203}
2204
2205impl MetadataSetValue {
2206    pub fn value_type(&self) -> MetadataValueType {
2207        match self {
2208            MetadataSetValue::Bool(_) => MetadataValueType::Bool,
2209            MetadataSetValue::Int(_) => MetadataValueType::Int,
2210            MetadataSetValue::Float(_) => MetadataValueType::Float,
2211            MetadataSetValue::Str(_) => MetadataValueType::Str,
2212        }
2213    }
2214}
2215
2216impl From<Vec<bool>> for MetadataSetValue {
2217    fn from(values: Vec<bool>) -> Self {
2218        MetadataSetValue::Bool(values)
2219    }
2220}
2221
2222impl From<Vec<i64>> for MetadataSetValue {
2223    fn from(values: Vec<i64>) -> Self {
2224        MetadataSetValue::Int(values)
2225    }
2226}
2227
2228impl From<Vec<i32>> for MetadataSetValue {
2229    fn from(values: Vec<i32>) -> Self {
2230        MetadataSetValue::Int(values.into_iter().map(|v| v as i64).collect())
2231    }
2232}
2233
2234impl From<Vec<f64>> for MetadataSetValue {
2235    fn from(values: Vec<f64>) -> Self {
2236        MetadataSetValue::Float(values)
2237    }
2238}
2239
2240impl From<Vec<f32>> for MetadataSetValue {
2241    fn from(values: Vec<f32>) -> Self {
2242        MetadataSetValue::Float(values.into_iter().map(|v| v as f64).collect())
2243    }
2244}
2245
2246impl From<Vec<String>> for MetadataSetValue {
2247    fn from(values: Vec<String>) -> Self {
2248        MetadataSetValue::Str(values)
2249    }
2250}
2251
2252impl From<Vec<&str>> for MetadataSetValue {
2253    fn from(values: Vec<&str>) -> Self {
2254        MetadataSetValue::Str(values.into_iter().map(|s| s.to_string()).collect())
2255    }
2256}
2257
2258// TODO: Deprecate where_document
2259impl TryFrom<chroma_proto::WhereDocument> for Where {
2260    type Error = WhereConversionError;
2261
2262    fn try_from(proto_document: chroma_proto::WhereDocument) -> Result<Self, Self::Error> {
2263        match proto_document.r#where_document {
2264            Some(chroma_proto::where_document::WhereDocument::Direct(proto_comparison)) => {
2265                let operator = match TryInto::<chroma_proto::WhereDocumentOperator>::try_into(
2266                    proto_comparison.operator,
2267                ) {
2268                    Ok(operator) => operator,
2269                    Err(_) => {
2270                        return Err(WhereConversionError::cause(
2271                            "[Deprecated] Invalid where document operator",
2272                        ))
2273                    }
2274                };
2275                let comparison = DocumentExpression {
2276                    pattern: proto_comparison.pattern,
2277                    operator: operator.into(),
2278                };
2279                Ok(Where::Document(comparison))
2280            }
2281            Some(chroma_proto::where_document::WhereDocument::Children(proto_children)) => {
2282                let operator = match TryInto::<chroma_proto::BooleanOperator>::try_into(
2283                    proto_children.operator,
2284                ) {
2285                    Ok(operator) => operator,
2286                    Err(_) => {
2287                        return Err(WhereConversionError::cause(
2288                            "[Deprecated] Invalid boolean operator",
2289                        ))
2290                    }
2291                };
2292                let children = CompositeExpression {
2293                    children: proto_children
2294                        .children
2295                        .into_iter()
2296                        .map(|child| child.try_into())
2297                        .collect::<Result<_, _>>()?,
2298                    operator: operator.into(),
2299                };
2300                Ok(Where::Composite(children))
2301            }
2302            None => Err(WhereConversionError::cause("[Deprecated] Invalid where")),
2303        }
2304    }
2305}
2306
2307#[cfg(test)]
2308mod tests {
2309    use crate::operator::Key;
2310
2311    use super::*;
2312
2313    // This is needed for the tests that round trip to the python world.
2314    #[cfg(feature = "pyo3")]
2315    fn ensure_python_interpreter() {
2316        static PYTHON_INIT: std::sync::Once = std::sync::Once::new();
2317        PYTHON_INIT.call_once(|| {
2318            pyo3::prepare_freethreaded_python();
2319        });
2320    }
2321
2322    #[test]
2323    fn test_update_metadata_try_from() {
2324        let mut proto_metadata = chroma_proto::UpdateMetadata {
2325            metadata: HashMap::new(),
2326        };
2327        proto_metadata.metadata.insert(
2328            "foo".to_string(),
2329            chroma_proto::UpdateMetadataValue {
2330                value: Some(chroma_proto::update_metadata_value::Value::IntValue(42)),
2331            },
2332        );
2333        proto_metadata.metadata.insert(
2334            "bar".to_string(),
2335            chroma_proto::UpdateMetadataValue {
2336                value: Some(chroma_proto::update_metadata_value::Value::FloatValue(42.0)),
2337            },
2338        );
2339        proto_metadata.metadata.insert(
2340            "baz".to_string(),
2341            chroma_proto::UpdateMetadataValue {
2342                value: Some(chroma_proto::update_metadata_value::Value::StringValue(
2343                    "42".to_string(),
2344                )),
2345            },
2346        );
2347        // Add sparse vector test
2348        proto_metadata.metadata.insert(
2349            "sparse".to_string(),
2350            chroma_proto::UpdateMetadataValue {
2351                value: Some(
2352                    chroma_proto::update_metadata_value::Value::SparseVectorValue(
2353                        chroma_proto::SparseVector {
2354                            indices: vec![0, 5, 10],
2355                            values: vec![0.1, 0.5, 0.9],
2356                            tokens: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()],
2357                        },
2358                    ),
2359                ),
2360            },
2361        );
2362        let converted_metadata: UpdateMetadata = proto_metadata.try_into().unwrap();
2363        assert_eq!(converted_metadata.len(), 4);
2364        assert_eq!(
2365            converted_metadata.get("foo").unwrap(),
2366            &UpdateMetadataValue::Int(42)
2367        );
2368        assert_eq!(
2369            converted_metadata.get("bar").unwrap(),
2370            &UpdateMetadataValue::Float(42.0)
2371        );
2372        assert_eq!(
2373            converted_metadata.get("baz").unwrap(),
2374            &UpdateMetadataValue::Str("42".to_string())
2375        );
2376        assert_eq!(
2377            converted_metadata.get("sparse").unwrap(),
2378            &UpdateMetadataValue::SparseVector(
2379                SparseVector::new_with_tokens(
2380                    vec![0, 5, 10],
2381                    vec![0.1, 0.5, 0.9],
2382                    vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),],
2383                )
2384                .unwrap()
2385            )
2386        );
2387    }
2388
2389    #[test]
2390    fn test_metadata_try_from() {
2391        let mut proto_metadata = chroma_proto::UpdateMetadata {
2392            metadata: HashMap::new(),
2393        };
2394        proto_metadata.metadata.insert(
2395            "foo".to_string(),
2396            chroma_proto::UpdateMetadataValue {
2397                value: Some(chroma_proto::update_metadata_value::Value::IntValue(42)),
2398            },
2399        );
2400        proto_metadata.metadata.insert(
2401            "bar".to_string(),
2402            chroma_proto::UpdateMetadataValue {
2403                value: Some(chroma_proto::update_metadata_value::Value::FloatValue(42.0)),
2404            },
2405        );
2406        proto_metadata.metadata.insert(
2407            "baz".to_string(),
2408            chroma_proto::UpdateMetadataValue {
2409                value: Some(chroma_proto::update_metadata_value::Value::StringValue(
2410                    "42".to_string(),
2411                )),
2412            },
2413        );
2414        // Add sparse vector test
2415        proto_metadata.metadata.insert(
2416            "sparse".to_string(),
2417            chroma_proto::UpdateMetadataValue {
2418                value: Some(
2419                    chroma_proto::update_metadata_value::Value::SparseVectorValue(
2420                        chroma_proto::SparseVector {
2421                            indices: vec![1, 10, 100],
2422                            values: vec![0.2, 0.4, 0.6],
2423                            tokens: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()],
2424                        },
2425                    ),
2426                ),
2427            },
2428        );
2429        let converted_metadata: Metadata = proto_metadata.try_into().unwrap();
2430        assert_eq!(converted_metadata.len(), 4);
2431        assert_eq!(
2432            converted_metadata.get("foo").unwrap(),
2433            &MetadataValue::Int(42)
2434        );
2435        assert_eq!(
2436            converted_metadata.get("bar").unwrap(),
2437            &MetadataValue::Float(42.0)
2438        );
2439        assert_eq!(
2440            converted_metadata.get("baz").unwrap(),
2441            &MetadataValue::Str("42".to_string())
2442        );
2443        assert_eq!(
2444            converted_metadata.get("sparse").unwrap(),
2445            &MetadataValue::SparseVector(
2446                SparseVector::new_with_tokens(
2447                    vec![1, 10, 100],
2448                    vec![0.2, 0.4, 0.6],
2449                    vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),],
2450                )
2451                .unwrap()
2452            )
2453        );
2454    }
2455
2456    #[test]
2457    fn test_where_clause_simple_from() {
2458        let proto_where = chroma_proto::Where {
2459            r#where: Some(chroma_proto::r#where::Where::DirectComparison(
2460                chroma_proto::DirectComparison {
2461                    key: "foo".to_string(),
2462                    comparison: Some(
2463                        chroma_proto::direct_comparison::Comparison::SingleIntOperand(
2464                            chroma_proto::SingleIntComparison {
2465                                value: 42,
2466                                comparator: Some(chroma_proto::single_int_comparison::Comparator::GenericComparator(chroma_proto::GenericComparator::Eq as i32)),
2467                            },
2468                        ),
2469                    ),
2470                },
2471            )),
2472        };
2473        let where_clause: Where = proto_where.try_into().unwrap();
2474        match where_clause {
2475            Where::Metadata(comparison) => {
2476                assert_eq!(comparison.key, "foo");
2477                match comparison.comparison {
2478                    MetadataComparison::Primitive(_, value) => {
2479                        assert_eq!(value, MetadataValue::Int(42));
2480                    }
2481                    _ => panic!("Invalid comparison type"),
2482                }
2483            }
2484            _ => panic!("Invalid where type"),
2485        }
2486    }
2487
2488    #[test]
2489    fn test_where_clause_with_children() {
2490        let proto_where = chroma_proto::Where {
2491            r#where: Some(chroma_proto::r#where::Where::Children(
2492                chroma_proto::WhereChildren {
2493                    children: vec![
2494                        chroma_proto::Where {
2495                            r#where: Some(chroma_proto::r#where::Where::DirectComparison(
2496                                chroma_proto::DirectComparison {
2497                                    key: "foo".to_string(),
2498                                    comparison: Some(
2499                                        chroma_proto::direct_comparison::Comparison::SingleIntOperand(
2500                                            chroma_proto::SingleIntComparison {
2501                                                value: 42,
2502                                                comparator: Some(chroma_proto::single_int_comparison::Comparator::GenericComparator(chroma_proto::GenericComparator::Eq as i32)),
2503                                            },
2504                                        ),
2505                                    ),
2506                                },
2507                            )),
2508                        },
2509                        chroma_proto::Where {
2510                            r#where: Some(chroma_proto::r#where::Where::DirectComparison(
2511                                chroma_proto::DirectComparison {
2512                                    key: "bar".to_string(),
2513                                    comparison: Some(
2514                                        chroma_proto::direct_comparison::Comparison::SingleIntOperand(
2515                                            chroma_proto::SingleIntComparison {
2516                                                value: 42,
2517                                                comparator: Some(chroma_proto::single_int_comparison::Comparator::GenericComparator(chroma_proto::GenericComparator::Eq as i32)),
2518                                            },
2519                                        ),
2520                                    ),
2521                                },
2522                            )),
2523                        },
2524                    ],
2525                    operator: chroma_proto::BooleanOperator::And.into(),
2526                },
2527            )),
2528        };
2529        let where_clause: Where = proto_where.try_into().unwrap();
2530        match where_clause {
2531            Where::Composite(children) => {
2532                assert_eq!(children.children.len(), 2);
2533                assert_eq!(children.operator, BooleanOperator::And);
2534            }
2535            _ => panic!("Invalid where type"),
2536        }
2537    }
2538
2539    #[test]
2540    fn test_where_document_simple() {
2541        let proto_where = chroma_proto::WhereDocument {
2542            r#where_document: Some(chroma_proto::where_document::WhereDocument::Direct(
2543                chroma_proto::DirectWhereDocument {
2544                    pattern: "foo".to_string(),
2545                    operator: chroma_proto::WhereDocumentOperator::Contains.into(),
2546                },
2547            )),
2548        };
2549        let where_document: Where = proto_where.try_into().unwrap();
2550        match where_document {
2551            Where::Document(comparison) => {
2552                assert_eq!(comparison.pattern, "foo");
2553                assert_eq!(comparison.operator, DocumentOperator::Contains);
2554            }
2555            _ => panic!("Invalid where document type"),
2556        }
2557    }
2558
2559    #[test]
2560    fn test_where_document_with_children() {
2561        let proto_where = chroma_proto::WhereDocument {
2562            r#where_document: Some(chroma_proto::where_document::WhereDocument::Children(
2563                chroma_proto::WhereDocumentChildren {
2564                    children: vec![
2565                        chroma_proto::WhereDocument {
2566                            r#where_document: Some(
2567                                chroma_proto::where_document::WhereDocument::Direct(
2568                                    chroma_proto::DirectWhereDocument {
2569                                        pattern: "foo".to_string(),
2570                                        operator: chroma_proto::WhereDocumentOperator::Contains
2571                                            .into(),
2572                                    },
2573                                ),
2574                            ),
2575                        },
2576                        chroma_proto::WhereDocument {
2577                            r#where_document: Some(
2578                                chroma_proto::where_document::WhereDocument::Direct(
2579                                    chroma_proto::DirectWhereDocument {
2580                                        pattern: "bar".to_string(),
2581                                        operator: chroma_proto::WhereDocumentOperator::Contains
2582                                            .into(),
2583                                    },
2584                                ),
2585                            ),
2586                        },
2587                    ],
2588                    operator: chroma_proto::BooleanOperator::And.into(),
2589                },
2590            )),
2591        };
2592        let where_document: Where = proto_where.try_into().unwrap();
2593        match where_document {
2594            Where::Composite(children) => {
2595                assert_eq!(children.children.len(), 2);
2596                assert_eq!(children.operator, BooleanOperator::And);
2597            }
2598            _ => panic!("Invalid where document type"),
2599        }
2600    }
2601
2602    #[test]
2603    fn test_sparse_vector_new() {
2604        let indices = vec![0, 5, 10];
2605        let values = vec![0.1, 0.5, 0.9];
2606        let sparse = SparseVector::new(indices.clone(), values.clone()).unwrap();
2607        assert_eq!(sparse.indices, indices);
2608        assert_eq!(sparse.values, values);
2609    }
2610
2611    #[test]
2612    fn test_sparse_vector_from_pairs() {
2613        let pairs = vec![(0, 0.1), (5, 0.5), (10, 0.9)];
2614        let sparse = SparseVector::from_pairs(pairs.clone());
2615        assert_eq!(sparse.indices, vec![0, 5, 10]);
2616        assert_eq!(sparse.values, vec![0.1, 0.5, 0.9]);
2617    }
2618
2619    #[test]
2620    fn test_sparse_vector_from_triples() {
2621        let triples = vec![
2622            ("foo".to_string(), 0, 0.1),
2623            ("bar".to_string(), 5, 0.5),
2624            ("baz".to_string(), 10, 0.9),
2625        ];
2626        let sparse = SparseVector::from_triples(triples.clone());
2627        assert_eq!(sparse.indices, vec![0, 5, 10]);
2628        assert_eq!(sparse.values, vec![0.1, 0.5, 0.9]);
2629    }
2630
2631    #[test]
2632    fn test_sparse_vector_iter() {
2633        let sparse = SparseVector::new(vec![0, 5, 10], vec![0.1, 0.5, 0.9]).unwrap();
2634        let collected: Vec<(u32, f32)> = sparse.iter().collect();
2635        assert_eq!(collected, vec![(0, 0.1), (5, 0.5), (10, 0.9)]);
2636    }
2637
2638    #[test]
2639    fn test_sparse_vector_ordering() {
2640        let sparse1 = SparseVector::new(vec![0, 5], vec![0.1, 0.5]).unwrap();
2641        let sparse2 = SparseVector::new(vec![0, 5], vec![0.1, 0.5]).unwrap();
2642        let sparse3 = SparseVector::new(vec![0, 6], vec![0.1, 0.5]).unwrap();
2643        let sparse4 = SparseVector::new(vec![0, 5], vec![0.1, 0.6]).unwrap();
2644
2645        assert_eq!(sparse1, sparse2);
2646        assert!(sparse1 < sparse3);
2647        assert!(sparse1 < sparse4);
2648    }
2649
2650    #[test]
2651    fn test_sparse_vector_proto_conversion() {
2652        let tokens = vec![
2653            "token1".to_string(),
2654            "token2".to_string(),
2655            "token3".to_string(),
2656        ];
2657        let sparse =
2658            SparseVector::new_with_tokens(vec![1, 10, 100], vec![0.2, 0.4, 0.6], tokens.clone())
2659                .unwrap();
2660        let proto: chroma_proto::SparseVector = sparse.clone().into();
2661        assert_eq!(proto.indices, vec![1, 10, 100]);
2662        assert_eq!(proto.values, vec![0.2, 0.4, 0.6]);
2663        assert_eq!(proto.tokens, tokens.clone());
2664
2665        let converted: SparseVector = proto.try_into().unwrap();
2666        assert_eq!(converted, sparse);
2667        assert_eq!(converted.tokens, Some(tokens));
2668    }
2669
2670    #[test]
2671    fn test_sparse_vector_proto_conversion_empty_tokens() {
2672        let sparse = SparseVector::new(vec![0, 5, 10], vec![0.1, 0.5, 0.9]).unwrap();
2673        let proto: chroma_proto::SparseVector = sparse.clone().into();
2674        assert_eq!(proto.indices, vec![0, 5, 10]);
2675        assert_eq!(proto.values, vec![0.1, 0.5, 0.9]);
2676        assert_eq!(proto.tokens, Vec::<String>::new());
2677
2678        let converted: SparseVector = proto.try_into().unwrap();
2679        assert_eq!(converted, sparse);
2680        assert_eq!(converted.tokens, None);
2681    }
2682
2683    #[test]
2684    fn test_sparse_vector_logical_size() {
2685        let metadata = Metadata::from([(
2686            "sparse".to_string(),
2687            MetadataValue::SparseVector(
2688                SparseVector::new(vec![0, 1, 2, 3, 4], vec![0.1, 0.2, 0.3, 0.4, 0.5]).unwrap(),
2689            ),
2690        )]);
2691
2692        let size = logical_size_of_metadata(&metadata);
2693        // Size should include the key string length and the sparse vector data
2694        // "sparse" = 6 bytes + 5 * 4 bytes (u32 indices) + 5 * 4 bytes (f32 values) = 46 bytes
2695        assert_eq!(size, 46);
2696    }
2697
2698    #[test]
2699    fn test_sparse_vector_validation() {
2700        // Valid sparse vector
2701        let sparse = SparseVector::new(vec![1, 2, 3], vec![0.1, 0.2, 0.3]).unwrap();
2702        assert!(sparse.validate().is_ok());
2703
2704        // Length mismatch
2705        let sparse = SparseVector::new(vec![1, 2, 3], vec![0.1, 0.2]);
2706        assert!(sparse.is_err());
2707        let result = SparseVector::new(vec![1, 2, 3], vec![0.1, 0.2, 0.3])
2708            .unwrap()
2709            .validate();
2710        assert!(result.is_ok());
2711
2712        // Tokens length mismatch with indices/values
2713        let sparse = SparseVector::new_with_tokens(
2714            vec![1, 2, 3],
2715            vec![0.1, 0.2, 0.3],
2716            vec!["a".to_string(), "b".to_string()],
2717        );
2718        assert!(sparse.is_err());
2719
2720        // Unsorted indices (descending order)
2721        let sparse = SparseVector::new(vec![3, 1, 2], vec![0.3, 0.1, 0.2]).unwrap();
2722        let result = sparse.validate();
2723        assert!(result.is_err());
2724        assert!(matches!(
2725            result.unwrap_err(),
2726            MetadataValueConversionError::SparseVectorIndicesNotSorted
2727        ));
2728
2729        // Duplicate indices (not strictly ascending)
2730        let sparse = SparseVector::new(vec![1, 2, 2, 3], vec![0.1, 0.2, 0.3, 0.4]).unwrap();
2731        let result = sparse.validate();
2732        assert!(result.is_err());
2733        assert!(matches!(
2734            result.unwrap_err(),
2735            MetadataValueConversionError::SparseVectorIndicesNotSorted
2736        ));
2737
2738        // Descending at one point
2739        let sparse = SparseVector::new(vec![1, 3, 2], vec![0.1, 0.3, 0.2]).unwrap();
2740        let result = sparse.validate();
2741        assert!(result.is_err());
2742        assert!(matches!(
2743            result.unwrap_err(),
2744            MetadataValueConversionError::SparseVectorIndicesNotSorted
2745        ));
2746    }
2747
2748    #[test]
2749    fn test_sparse_vector_deserialize_old_format() {
2750        // Old format without #type field (backward compatibility)
2751        let json = r#"{"indices": [0, 1, 2], "values": [1.0, 2.0, 3.0]}"#;
2752        let sv: SparseVector = serde_json::from_str(json).unwrap();
2753        assert_eq!(sv.indices, vec![0, 1, 2]);
2754        assert_eq!(sv.values, vec![1.0, 2.0, 3.0]);
2755    }
2756
2757    #[test]
2758    fn test_sparse_vector_deserialize_new_format() {
2759        // New format with #type field
2760        let json =
2761            "{\"#type\": \"sparse_vector\", \"indices\": [0, 1, 2], \"values\": [1.0, 2.0, 3.0]}";
2762        let sv: SparseVector = serde_json::from_str(json).unwrap();
2763        assert_eq!(sv.indices, vec![0, 1, 2]);
2764        assert_eq!(sv.values, vec![1.0, 2.0, 3.0]);
2765    }
2766
2767    #[test]
2768    fn test_sparse_vector_deserialize_new_format_field_order() {
2769        // New format with different field order (should still work)
2770        let json = "{\"indices\": [5, 10], \"#type\": \"sparse_vector\", \"values\": [0.5, 1.0]}";
2771        let sv: SparseVector = serde_json::from_str(json).unwrap();
2772        assert_eq!(sv.indices, vec![5, 10]);
2773        assert_eq!(sv.values, vec![0.5, 1.0]);
2774    }
2775
2776    #[test]
2777    fn test_sparse_vector_deserialize_wrong_type_tag() {
2778        // Wrong #type field value should fail
2779        let json = "{\"#type\": \"dense_vector\", \"indices\": [0, 1], \"values\": [1.0, 2.0]}";
2780        let result: Result<SparseVector, _> = serde_json::from_str(json);
2781        assert!(result.is_err());
2782        let err_msg = result.unwrap_err().to_string();
2783        assert!(err_msg.contains("sparse_vector"));
2784    }
2785
2786    #[test]
2787    fn test_sparse_vector_serialize_always_has_type() {
2788        // Serialization should always include #type field
2789        let sv = SparseVector::new(vec![0, 1, 2], vec![1.0, 2.0, 3.0]).unwrap();
2790        let json = serde_json::to_value(&sv).unwrap();
2791
2792        assert_eq!(json["#type"], "sparse_vector");
2793        assert_eq!(json["indices"], serde_json::json!([0, 1, 2]));
2794        assert_eq!(json["values"], serde_json::json!([1.0, 2.0, 3.0]));
2795    }
2796
2797    #[test]
2798    fn test_sparse_vector_roundtrip_with_type() {
2799        // Test that serialize -> deserialize preserves the data
2800        let original = SparseVector::new(vec![0, 5, 10, 15], vec![0.1, 0.5, 1.0, 1.5]).unwrap();
2801        let json = serde_json::to_string(&original).unwrap();
2802
2803        // Verify the serialized JSON contains #type
2804        assert!(json.contains("\"#type\":\"sparse_vector\""));
2805
2806        let deserialized: SparseVector = serde_json::from_str(&json).unwrap();
2807        assert_eq!(original, deserialized);
2808    }
2809
2810    #[test]
2811    fn test_sparse_vector_in_metadata_old_format() {
2812        // Test that old format works when sparse vector is in metadata
2813        let json = r#"{"key": "value", "sparse": {"indices": [0, 1], "values": [1.0, 2.0]}}"#;
2814        let map: HashMap<String, serde_json::Value> = serde_json::from_str(json).unwrap();
2815
2816        let sparse_value = &map["sparse"];
2817        let sv: SparseVector = serde_json::from_value(sparse_value.clone()).unwrap();
2818        assert_eq!(sv.indices, vec![0, 1]);
2819        assert_eq!(sv.values, vec![1.0, 2.0]);
2820    }
2821
2822    #[test]
2823    fn test_sparse_vector_in_metadata_new_format() {
2824        // Test that new format works when sparse vector is in metadata
2825        let json = "{\"key\": \"value\", \"sparse\": {\"#type\": \"sparse_vector\", \"indices\": [0, 1], \"values\": [1.0, 2.0]}}";
2826        let map: HashMap<String, serde_json::Value> = serde_json::from_str(json).unwrap();
2827
2828        let sparse_value = &map["sparse"];
2829        let sv: SparseVector = serde_json::from_value(sparse_value.clone()).unwrap();
2830        assert_eq!(sv.indices, vec![0, 1]);
2831        assert_eq!(sv.values, vec![1.0, 2.0]);
2832    }
2833
2834    #[test]
2835    fn test_sparse_vector_tokens_roundtrip_old_to_new() {
2836        // Old format without tokens field should deserialize with tokens=None
2837        let json = r#"{"indices": [0, 1, 2], "values": [1.0, 2.0, 3.0]}"#;
2838        let sv: SparseVector = serde_json::from_str(json).unwrap();
2839        assert_eq!(sv.indices, vec![0, 1, 2]);
2840        assert_eq!(sv.values, vec![1.0, 2.0, 3.0]);
2841        assert_eq!(sv.tokens, None);
2842
2843        // Serialize and verify it includes #type but no tokens field when None
2844        let serialized = serde_json::to_value(&sv).unwrap();
2845        assert_eq!(serialized["#type"], "sparse_vector");
2846        assert_eq!(serialized["indices"], serde_json::json!([0, 1, 2]));
2847        assert_eq!(serialized["values"], serde_json::json!([1.0, 2.0, 3.0]));
2848        assert_eq!(serialized["tokens"], serde_json::Value::Null);
2849    }
2850
2851    #[test]
2852    fn test_sparse_vector_tokens_roundtrip_new_to_new() {
2853        // New format with tokens field
2854        let sv_with_tokens = SparseVector::new_with_tokens(
2855            vec![0, 1, 2],
2856            vec![1.0, 2.0, 3.0],
2857            vec!["foo".to_string(), "bar".to_string(), "baz".to_string()],
2858        )
2859        .unwrap();
2860
2861        // Serialize
2862        let serialized = serde_json::to_string(&sv_with_tokens).unwrap();
2863        assert!(serialized.contains("\"#type\":\"sparse_vector\""));
2864        assert!(serialized.contains("\"tokens\""));
2865
2866        // Deserialize and verify tokens are preserved
2867        let deserialized: SparseVector = serde_json::from_str(&serialized).unwrap();
2868        assert_eq!(deserialized.indices, vec![0, 1, 2]);
2869        assert_eq!(deserialized.values, vec![1.0, 2.0, 3.0]);
2870        assert_eq!(
2871            deserialized.tokens,
2872            Some(vec![
2873                "foo".to_string(),
2874                "bar".to_string(),
2875                "baz".to_string()
2876            ])
2877        );
2878    }
2879
2880    #[test]
2881    fn test_sparse_vector_tokens_deserialize_with_tokens_field() {
2882        // Test deserializing JSON that explicitly includes tokens field
2883        let json = r##"{"#type": "sparse_vector", "indices": [5, 10], "values": [0.5, 1.0], "tokens": ["token1", "token2"]}"##;
2884        let sv: SparseVector = serde_json::from_str(json).unwrap();
2885        assert_eq!(sv.indices, vec![5, 10]);
2886        assert_eq!(sv.values, vec![0.5, 1.0]);
2887        assert_eq!(
2888            sv.tokens,
2889            Some(vec!["token1".to_string(), "token2".to_string()])
2890        );
2891    }
2892
2893    #[test]
2894    fn test_sparse_vector_tokens_backward_compatibility() {
2895        // Verify old format (no tokens, no #type) deserializes correctly
2896        let old_json = r#"{"indices": [1, 2], "values": [0.1, 0.2]}"#;
2897        let old_sv: SparseVector = serde_json::from_str(old_json).unwrap();
2898
2899        // Verify new format (with #type, with tokens) deserializes correctly
2900        let new_json = r##"{"#type": "sparse_vector", "indices": [1, 2], "values": [0.1, 0.2], "tokens": ["a", "b"]}"##;
2901        let new_sv: SparseVector = serde_json::from_str(new_json).unwrap();
2902
2903        // Both should have same indices and values
2904        assert_eq!(old_sv.indices, new_sv.indices);
2905        assert_eq!(old_sv.values, new_sv.values);
2906
2907        // Old should have None tokens, new should have Some tokens
2908        assert_eq!(old_sv.tokens, None);
2909        assert_eq!(new_sv.tokens, Some(vec!["a".to_string(), "b".to_string()]));
2910    }
2911
2912    #[test]
2913    fn test_sparse_vector_from_triples_preserves_tokens() {
2914        let triples = vec![
2915            ("apple".to_string(), 10, 0.5),
2916            ("banana".to_string(), 20, 0.7),
2917            ("cherry".to_string(), 30, 0.9),
2918        ];
2919        let sv = SparseVector::from_triples(triples.clone());
2920
2921        assert_eq!(sv.indices, vec![10, 20, 30]);
2922        assert_eq!(sv.values, vec![0.5, 0.7, 0.9]);
2923        assert_eq!(
2924            sv.tokens,
2925            Some(vec![
2926                "apple".to_string(),
2927                "banana".to_string(),
2928                "cherry".to_string()
2929            ])
2930        );
2931
2932        // Roundtrip through serialization
2933        let serialized = serde_json::to_string(&sv).unwrap();
2934        let deserialized: SparseVector = serde_json::from_str(&serialized).unwrap();
2935
2936        assert_eq!(deserialized.indices, sv.indices);
2937        assert_eq!(deserialized.values, sv.values);
2938        assert_eq!(deserialized.tokens, sv.tokens);
2939    }
2940
2941    #[cfg(feature = "pyo3")]
2942    #[test]
2943    fn test_sparse_vector_pyo3_roundtrip_with_tokens() {
2944        ensure_python_interpreter();
2945
2946        pyo3::Python::with_gil(|py| {
2947            use pyo3::types::PyDict;
2948            use pyo3::IntoPyObject;
2949
2950            let dict_in = PyDict::new(py);
2951            dict_in.set_item("indices", vec![0u32, 1, 2]).unwrap();
2952            dict_in
2953                .set_item("values", vec![0.1f32, 0.2f32, 0.3f32])
2954                .unwrap();
2955            dict_in
2956                .set_item("tokens", vec!["foo", "bar", "baz"])
2957                .unwrap();
2958
2959            let sparse: SparseVector = dict_in.clone().into_any().extract().unwrap();
2960            assert_eq!(sparse.indices, vec![0, 1, 2]);
2961            assert_eq!(sparse.values, vec![0.1, 0.2, 0.3]);
2962            assert_eq!(
2963                sparse.tokens,
2964                Some(vec![
2965                    "foo".to_string(),
2966                    "bar".to_string(),
2967                    "baz".to_string()
2968                ])
2969            );
2970
2971            let py_obj = sparse.clone().into_pyobject(py).unwrap();
2972            let dict_out = py_obj.downcast::<PyDict>().unwrap();
2973            let tokens_obj = dict_out.get_item("tokens").unwrap();
2974            let tokens: Vec<String> = tokens_obj
2975                .expect("expected tokens key in Python dict")
2976                .extract()
2977                .unwrap();
2978            assert_eq!(
2979                tokens,
2980                vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]
2981            );
2982        });
2983    }
2984
2985    #[cfg(feature = "pyo3")]
2986    #[test]
2987    fn test_sparse_vector_pyo3_roundtrip_without_tokens() {
2988        ensure_python_interpreter();
2989
2990        pyo3::Python::with_gil(|py| {
2991            use pyo3::types::PyDict;
2992            use pyo3::IntoPyObject;
2993
2994            let dict_in = PyDict::new(py);
2995            dict_in.set_item("indices", vec![5u32]).unwrap();
2996            dict_in.set_item("values", vec![1.5f32]).unwrap();
2997
2998            let sparse: SparseVector = dict_in.clone().into_any().extract().unwrap();
2999            assert_eq!(sparse.indices, vec![5]);
3000            assert_eq!(sparse.values, vec![1.5]);
3001            assert!(sparse.tokens.is_none());
3002
3003            let py_obj = sparse.into_pyobject(py).unwrap();
3004            let dict_out = py_obj.downcast::<PyDict>().unwrap();
3005            let tokens_obj = dict_out.get_item("tokens").unwrap();
3006            let tokens_value = tokens_obj.expect("expected tokens key in Python dict");
3007            assert!(
3008                tokens_value.is_none(),
3009                "expected tokens value in Python dict to be None"
3010            );
3011        });
3012    }
3013
3014    #[test]
3015    fn test_simplifies_identities() {
3016        let all: Where = true.into();
3017        assert_eq!(all.clone() & all.clone(), true.into());
3018        assert_eq!(all.clone() | all.clone(), true.into());
3019
3020        let foo = Key::field("foo").eq("bar");
3021        assert_eq!(foo.clone() & all.clone(), foo.clone());
3022        assert_eq!(all.clone() & foo.clone(), foo.clone());
3023
3024        let none: Where = false.into();
3025        assert_eq!(foo.clone() | none.clone(), foo.clone());
3026        assert_eq!(none | foo.clone(), foo);
3027    }
3028
3029    #[test]
3030    fn test_flattens() {
3031        let foo = Key::field("foo").eq("bar");
3032        let baz = Key::field("baz").eq("quux");
3033
3034        let and_nested = foo.clone() & (baz.clone() & foo.clone());
3035        assert_eq!(
3036            and_nested,
3037            Where::Composite(CompositeExpression {
3038                operator: BooleanOperator::And,
3039                children: vec![foo.clone(), baz.clone(), foo.clone()]
3040            })
3041        );
3042
3043        let or_nested = foo.clone() | (baz.clone() | foo.clone());
3044        assert_eq!(
3045            or_nested,
3046            Where::Composite(CompositeExpression {
3047                operator: BooleanOperator::Or,
3048                children: vec![foo.clone(), baz.clone(), foo.clone()]
3049            })
3050        );
3051    }
3052
3053    #[test]
3054    fn test_reject_nan_metadata_float_via_grpc() {
3055        for bad_value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
3056            let proto = chroma_proto::UpdateMetadataValue {
3057                value: Some(chroma_proto::update_metadata_value::Value::FloatValue(
3058                    bad_value,
3059                )),
3060            };
3061
3062            let result = UpdateMetadataValue::try_from(&proto);
3063            assert!(
3064                result.is_err(),
3065                "should reject {bad_value} in UpdateMetadataValue"
3066            );
3067
3068            let result = MetadataValue::try_from(&proto);
3069            assert!(
3070                result.is_err(),
3071                "should reject {bad_value} in MetadataValue"
3072            );
3073        }
3074
3075        // Valid floats should still work.
3076        let proto = chroma_proto::UpdateMetadataValue {
3077            value: Some(chroma_proto::update_metadata_value::Value::FloatValue(1.5)),
3078        };
3079        assert!(UpdateMetadataValue::try_from(&proto).is_ok());
3080        assert!(MetadataValue::try_from(&proto).is_ok());
3081    }
3082
3083    #[test]
3084    fn test_reject_nan_metadata_float_array_via_grpc() {
3085        for bad_value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
3086            let proto = chroma_proto::UpdateMetadataValue {
3087                value: Some(chroma_proto::update_metadata_value::Value::DoubleListValue(
3088                    chroma_proto::DoubleListValue {
3089                        values: vec![1.0, bad_value, 3.0],
3090                    },
3091                )),
3092            };
3093
3094            let result = UpdateMetadataValue::try_from(&proto);
3095            assert!(
3096                result.is_err(),
3097                "should reject {bad_value} in UpdateMetadataValue float array"
3098            );
3099
3100            let result = MetadataValue::try_from(&proto);
3101            assert!(
3102                result.is_err(),
3103                "should reject {bad_value} in MetadataValue float array"
3104            );
3105        }
3106
3107        // Valid float arrays should still work.
3108        let proto = chroma_proto::UpdateMetadataValue {
3109            value: Some(chroma_proto::update_metadata_value::Value::DoubleListValue(
3110                chroma_proto::DoubleListValue {
3111                    values: vec![1.0, 2.0, 3.0],
3112                },
3113            )),
3114        };
3115        assert!(UpdateMetadataValue::try_from(&proto).is_ok());
3116        assert!(MetadataValue::try_from(&proto).is_ok());
3117    }
3118}