Skip to main content

qdrant_edge/segment/
types.rs

1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::collections::{BTreeMap, HashMap, HashSet};
4use std::fmt::{self, Display, Formatter};
5use std::hash::{self, Hash, Hasher};
6use std::mem;
7use std::ops::Deref;
8use std::rc::Rc;
9use std::str::FromStr;
10use std::sync::Arc;
11
12use ahash::AHashSet;
13use bytemuck::{Pod, Zeroable};
14use crate::common::stable_hash::StableHash;
15use crate::common::types::{PointOffsetType, ScoreType};
16use ecow::EcoString;
17use fnv::FnvBuildHasher;
18use geo::{Contains, Coord, Distance as GeoDistance, Haversine, LineString, Point, Polygon};
19use indexmap::IndexSet;
20use itertools::Itertools;
21use num_derive::FromPrimitive;
22use ordered_float::OrderedFloat;
23use schemars::JsonSchema;
24use serde::{Deserialize, Deserializer, Serialize};
25use serde_json::{Map, Value};
26use strum::{EnumIter, EnumString};
27use uuid::Uuid;
28use validator::{Validate, ValidationError, ValidationErrors};
29use zerocopy::native_endian::U64;
30
31use crate::segment::common::anonymize::Anonymize;
32use crate::segment::common::operation_error::{OperationError, OperationResult};
33use crate::segment::common::utils::{self, MaybeOneOrMany, MultiValue};
34use crate::segment::data_types::index::{
35    BoolIndexParams, DatetimeIndexParams, FloatIndexParams, GeoIndexParams, IntegerIndexParams,
36    KeywordIndexParams, TextIndexParams, UuidIndexParams,
37};
38use crate::segment::data_types::modifier::Modifier;
39use crate::segment::data_types::order_by::OrderValue;
40use crate::segment::data_types::primitive::PrimitiveVectorElement;
41use crate::segment::data_types::tiny_map::TinyMap;
42use crate::segment::data_types::vectors::{DenseVector, VectorStructInternal};
43use crate::segment::index::field_index::CardinalityEstimation;
44use crate::segment::index::sparse_index::sparse_index_config::SparseIndexConfig;
45use crate::segment::json_path::JsonPath;
46use crate::segment::spaces::metric::{Metric, MetricPostProcessing};
47use crate::segment::spaces::simple::{CosineMetric, DotProductMetric, EuclidMetric, ManhattanMetric};
48use crate::segment::types::utils::unordered_hash_unique;
49use crate::segment::utils::maybe_arc::MaybeArc;
50
51pub type PayloadKeyType = JsonPath;
52pub type PayloadKeyTypeRef<'a> = &'a JsonPath;
53/// Sequential number of modification, applied to segment
54pub type SeqNumberType = u64;
55/// Type of float point payload
56pub type FloatPayloadType = f64;
57/// Type of integer point payload
58pub type IntPayloadType = i64;
59/// Type of datetime point payload
60pub type DateTimePayloadType = DateTimeWrapper;
61/// Type of Uuid point payload
62pub type UuidPayloadType = Uuid;
63/// Type of Uuid point payload key
64pub type UuidIntType = u128;
65/// Name of a vector
66pub type VectorName = str;
67/// Name of a vector (owned variant)
68pub type VectorNameBuf = String;
69
70/// Wraps `DateTime<Utc>` to allow more flexible deserialization
71#[derive(Clone, Copy, Serialize, JsonSchema, Debug, PartialEq, Eq, PartialOrd, Hash)]
72#[serde(transparent)]
73pub struct DateTimeWrapper(pub chrono::DateTime<chrono::Utc>);
74
75impl DateTimeWrapper {
76    /// Qdrant's representation of datetime as timestamp is an i64 of microseconds
77    pub fn timestamp(&self) -> i64 {
78        self.0.timestamp_micros()
79    }
80
81    pub fn from_timestamp(ts: i64) -> Option<Self> {
82        Some(Self(chrono::DateTime::from_timestamp_micros(ts)?))
83    }
84}
85
86impl<'de> Deserialize<'de> for DateTimePayloadType {
87    /// Parses RFC3339 datetime strings used in REST/JSON `datetime_range` filters.
88    /// Returns a clear user-facing error when the format is invalid.
89    /// Example accepted value: `2014-01-01T00:00:00Z`.
90    ///
91    /// Binary formats (CBOR/MessagePack/WAL) also serialize as RFC3339 strings, so we reuse
92    /// the same parsing path everywhere.
93    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
94    where
95        D: Deserializer<'de>,
96    {
97        let str_datetime: Cow<'de, str> = Cow::deserialize(deserializer)?;
98
99        match DateTimePayloadType::from_str(str_datetime.as_ref()) {
100            Ok(datetime) => Ok(datetime),
101            Err(_) => Err(serde::de::Error::custom(format!(
102                "'{str_datetime}' does not match accepted datetime format (RFC3339). Example: 2014-01-01T00:00:00Z"
103            ))),
104        }
105    }
106}
107
108impl FromStr for DateTimePayloadType {
109    type Err = chrono::ParseError;
110
111    fn from_str(s: &str) -> Result<Self, Self::Err> {
112        // Attempt to parse the input string in RFC 3339 format
113        if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(s)
114            // Attempt to parse default to-string format
115            .or_else(|_| chrono::DateTime::from_str(s))
116            // Attempt to parse the input string in the specified formats:
117            // - YYYY-MM-DD'T'HH:MM:SS-HHMM (timezone without colon)
118            // - YYYY-MM-DD HH:MM:SS-HHMM (timezone without colon)
119            .or_else(|_| chrono::DateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f%#z"))
120            .or_else(|_| chrono::DateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f%#z"))
121            .map(|dt| chrono::DateTime::<chrono::Utc>::from(dt).into())
122        {
123            return Ok(datetime);
124        }
125
126        // Attempt to parse the input string in the specified formats:
127        // - YYYY-MM-DD'T'HH:MM:SS (without timezone or Z)
128        // - YYYY-MM-DD HH:MM:SS (without timezone or Z)
129        // - YYYY-MM-DD'T'HH:MM (without timezone and seconds)
130        // - YYYY-MM-DD HH:MM (without timezone and seconds)
131        // - YYYY-MM-DD
132        // See: <https://github.com/qdrant/qdrant/issues/3529>
133        // See: <https://github.com/qdrant/qdrant/issues/8718>
134        let datetime = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")
135            .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f"))
136            .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M"))
137            .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M"))
138            .or_else(|_| chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d").map(Into::into))?;
139
140        // Convert the parsed NaiveDateTime to a DateTime<Utc>
141        let datetime_utc = datetime.and_utc().into();
142        Ok(datetime_utc)
143    }
144}
145
146impl Display for DateTimePayloadType {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        write!(f, "{}", self.0)
149    }
150}
151
152impl From<chrono::DateTime<chrono::Utc>> for DateTimePayloadType {
153    fn from(dt: chrono::DateTime<chrono::Utc>) -> Self {
154        DateTimeWrapper(dt)
155    }
156}
157
158fn id_num_example() -> u64 {
159    42
160}
161
162fn id_uuid_example() -> String {
163    "550e8400-e29b-41d4-a716-446655440000".to_string()
164}
165
166/// Type, used for specifying point ID in user interface
167#[derive(Debug, Serialize, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, JsonSchema)]
168#[serde(untagged)]
169pub enum ExtendedPointId {
170    #[schemars(example = "id_num_example")]
171    NumId(u64),
172    #[schemars(example = "id_uuid_example")]
173    Uuid(Uuid),
174}
175
176impl StableHash for ExtendedPointId {
177    fn stable_hash<W: FnMut(&[u8])>(&self, write: &mut W) {
178        match self {
179            ExtendedPointId::NumId(num) => {
180                0u64.stable_hash(write); // discriminant for NumId
181                num.stable_hash(write);
182            }
183            ExtendedPointId::Uuid(uuid) => {
184                1u64.stable_hash(write); // discriminant for Uuid
185
186                uuid.as_bytes().len().stable_hash(write); // compatibility with uuid <= v1.16.0
187                write(uuid.as_bytes());
188            }
189        }
190    }
191}
192
193impl ExtendedPointId {
194    #[cfg(any(test, feature = "testing"))]
195    pub fn as_u64(&self) -> u64 {
196        match self {
197            ExtendedPointId::NumId(num) => *num,
198            ExtendedPointId::Uuid(_) => panic!("Cannot convert UUID to u64"),
199        }
200    }
201
202    pub fn is_uuid(&self) -> bool {
203        matches!(self, ExtendedPointId::Uuid(..))
204    }
205}
206
207impl std::fmt::Display for ExtendedPointId {
208    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
209        match self {
210            ExtendedPointId::NumId(idx) => write!(f, "{idx}"),
211            ExtendedPointId::Uuid(uuid) => write!(f, "{uuid}"),
212        }
213    }
214}
215
216impl From<u64> for ExtendedPointId {
217    fn from(idx: u64) -> Self {
218        ExtendedPointId::NumId(idx)
219    }
220}
221
222impl FromStr for ExtendedPointId {
223    type Err = ();
224
225    fn from_str(s: &str) -> Result<Self, Self::Err> {
226        let try_num: Result<u64, _> = s.parse();
227        if let Ok(num) = try_num {
228            return Ok(Self::NumId(num));
229        }
230        let try_uuid = Uuid::from_str(s);
231        if let Ok(uuid) = try_uuid {
232            return Ok(Self::Uuid(uuid));
233        }
234        Err(())
235    }
236}
237
238impl<'de> serde::Deserialize<'de> for ExtendedPointId {
239    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
240    where
241        D: serde::Deserializer<'de>,
242    {
243        let value = serde_value::Value::deserialize(deserializer)?;
244
245        if let Ok(num) = value.clone().deserialize_into() {
246            return Ok(ExtendedPointId::NumId(num));
247        }
248
249        if let Ok(uuid) = value.clone().deserialize_into() {
250            return Ok(ExtendedPointId::Uuid(uuid));
251        }
252
253        let value = crate::segment::utils::fmt::SerdeValue(&value);
254
255        Err(serde::de::Error::custom(format!(
256            "value {value} is not a valid point ID, \
257                 valid values are either an unsigned integer or a UUID",
258        )))
259    }
260}
261
262/// Type of point index across all segments
263pub type PointIdType = ExtendedPointId;
264
265/// Compact representation of [`ExtendedPointId`].
266/// Unlike [`ExtendedPointId`], this type is 17 bytes long vs 24 bytes.
267#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
268pub enum CompactExtendedPointId {
269    NumId(U64),
270    Uuid(Uuid),
271}
272
273impl From<ExtendedPointId> for CompactExtendedPointId {
274    fn from(id: ExtendedPointId) -> Self {
275        match id {
276            ExtendedPointId::NumId(num) => CompactExtendedPointId::NumId(U64::new(num)),
277            ExtendedPointId::Uuid(uuid) => CompactExtendedPointId::Uuid(uuid),
278        }
279    }
280}
281
282impl From<CompactExtendedPointId> for ExtendedPointId {
283    fn from(id: CompactExtendedPointId) -> Self {
284        match id {
285            CompactExtendedPointId::NumId(num) => ExtendedPointId::NumId(num.get()),
286            CompactExtendedPointId::Uuid(uuid) => ExtendedPointId::Uuid(uuid),
287        }
288    }
289}
290
291/// Type of internal tags, build from payload
292#[derive(
293    Debug,
294    Deserialize,
295    Serialize,
296    JsonSchema,
297    
298    Clone,
299    Copy,
300    FromPrimitive,
301    PartialEq,
302    Eq,
303    Hash,
304    EnumString,
305    EnumIter,
306)]
307/// Distance function types used to compare vectors
308pub enum Distance {
309    // <https://en.wikipedia.org/wiki/Cosine_similarity>
310    Cosine,
311    // <https://en.wikipedia.org/wiki/Euclidean_distance>
312    Euclid,
313    // <https://en.wikipedia.org/wiki/Dot_product>
314    Dot,
315    // <https://simple.wikipedia.org/wiki/Manhattan_distance>
316    Manhattan,
317}
318
319impl Distance {
320    pub fn postprocess_score(&self, score: ScoreType) -> ScoreType {
321        match self {
322            Distance::Cosine => CosineMetric::postprocess(score),
323            Distance::Euclid => EuclidMetric::postprocess(score),
324            Distance::Dot => DotProductMetric::postprocess(score),
325            Distance::Manhattan => ManhattanMetric::postprocess(score),
326        }
327    }
328
329    pub fn preprocess_vector<T: PrimitiveVectorElement>(&self, vector: DenseVector) -> DenseVector
330    where
331        CosineMetric: Metric<T>,
332        EuclidMetric: Metric<T>,
333        DotProductMetric: Metric<T>,
334        ManhattanMetric: Metric<T>,
335    {
336        match self {
337            Distance::Cosine => CosineMetric::preprocess(vector),
338            Distance::Euclid => EuclidMetric::preprocess(vector),
339            Distance::Dot => DotProductMetric::preprocess(vector),
340            Distance::Manhattan => ManhattanMetric::preprocess(vector),
341        }
342    }
343
344    pub fn distance_order(&self) -> Order {
345        match self {
346            Distance::Cosine | Distance::Dot => Order::LargeBetter,
347            Distance::Euclid | Distance::Manhattan => Order::SmallBetter,
348        }
349    }
350
351    pub fn is_ordered(&self, left: ScoreType, right: ScoreType) -> bool {
352        match self.distance_order() {
353            Order::LargeBetter => left >= right,
354            Order::SmallBetter => left <= right,
355        }
356    }
357
358    /// Checks if score satisfies threshold condition
359    pub fn check_threshold(&self, score: ScoreType, threshold: ScoreType) -> bool {
360        match self.distance_order() {
361            Order::LargeBetter => score > threshold,
362            Order::SmallBetter => score < threshold,
363        }
364    }
365}
366
367#[derive(Debug, PartialEq, Clone, Copy)]
368pub enum Order {
369    LargeBetter,
370    SmallBetter,
371}
372
373/// Search result
374#[derive(Clone, Debug)]
375pub struct ScoredPoint {
376    /// Point id
377    pub id: PointIdType,
378    /// Point version
379    pub version: SeqNumberType,
380    /// Points vector distance to the query vector
381    pub score: ScoreType,
382    /// Payload - values assigned to the point
383    pub payload: Option<Payload>,
384    /// Vector of the point
385    pub vector: Option<VectorStructInternal>,
386    /// Shard Key
387    pub shard_key: Option<ShardKey>,
388    /// Order-by value
389    pub order_value: Option<OrderValue>,
390}
391
392impl Eq for ScoredPoint {}
393
394impl Ord for ScoredPoint {
395    /// Compare two scored points by score, unless they have `order_value`, in that case compare by `order_value`.
396    fn cmp(&self, other: &Self) -> Ordering {
397        match (&self.order_value, &other.order_value) {
398            (None, None) => OrderedFloat(self.score).cmp(&OrderedFloat(other.score)),
399            (Some(_), None) => Ordering::Greater,
400            (None, Some(_)) => Ordering::Less,
401            (Some(self_order), Some(other_order)) => self_order.cmp(other_order),
402        }
403    }
404}
405
406impl PartialOrd for ScoredPoint {
407    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
408        Some(self.cmp(other))
409    }
410}
411
412impl PartialEq for ScoredPoint {
413    fn eq(&self, other: &Self) -> bool {
414        (self.id, &self.score) == (other.id, &other.score)
415    }
416}
417
418/// Type of segment
419#[derive(Debug, Serialize, JsonSchema,  Clone, Copy, PartialEq, Eq)]
420#[serde(rename_all = "snake_case")]
421pub enum SegmentType {
422    // There are no index built for the segment, all operations are available
423    Plain,
424    // Segment with some sort of index built. Optimized for search, appending new points will require reindexing
425    Indexed,
426    // Some index which you better don't touch
427    Special,
428}
429
430/// Display payload field type & index information
431#[derive(Debug, Serialize, JsonSchema,  Clone, PartialEq, Eq)]
432#[serde(rename_all = "snake_case")]
433pub struct PayloadIndexInfo {
434    pub data_type: PayloadSchemaType,
435    #[serde(skip_serializing_if = "Option::is_none")]
436    pub params: Option<PayloadSchemaParams>,
437    /// Number of points indexed with this index
438    pub points: usize,
439}
440
441impl PayloadIndexInfo {
442    pub fn new(field_type: PayloadFieldSchema, points_count: usize) -> Self {
443        match field_type {
444            PayloadFieldSchema::FieldType(data_type) => PayloadIndexInfo {
445                data_type,
446                params: None,
447                points: points_count,
448            },
449            PayloadFieldSchema::FieldParams(schema_params) => PayloadIndexInfo {
450                data_type: schema_params.kind(),
451                params: Some(schema_params),
452                points: points_count,
453            },
454        }
455    }
456}
457
458#[derive(Debug, Serialize, JsonSchema,  Clone, PartialEq, Eq)]
459#[serde(rename_all = "snake_case")]
460pub struct VectorDataInfo {
461    pub num_vectors: usize,
462    pub num_indexed_vectors: usize,
463    pub num_deleted_vectors: usize,
464}
465
466/// Aggregated information about segment
467#[derive(Debug, Serialize, JsonSchema,  Clone, PartialEq, Eq)]
468#[serde(rename_all = "snake_case")]
469pub struct SegmentInfo {
470    pub uuid: Uuid,
471    pub segment_type: SegmentType,
472    pub num_vectors: usize,
473    pub num_points: usize,
474    pub num_deferred_points: Option<usize>,
475    pub num_deleted_deferred_points: Option<usize>,
476    pub num_indexed_vectors: usize,
477    pub num_deleted_vectors: usize,
478    /// An ESTIMATION of effective amount of bytes used for vectors
479    /// Do NOT rely on this number unless you know what you are doing
480    pub vectors_size_bytes: usize,
481    /// An estimation of the effective amount of bytes used for payloads
482    pub payloads_size_bytes: usize,
483    pub ram_usage_bytes: usize,
484    pub disk_usage_bytes: usize,
485    pub is_appendable: bool,
486    pub index_schema: HashMap<PayloadKeyType, PayloadIndexInfo>,
487    pub vector_data: HashMap<String, VectorDataInfo>,
488    /// Internal ID from which points are deferred (hidden from reads).
489    /// Only set for appendable segments.
490    #[serde(skip_serializing_if = "Option::is_none")]
491    
492    pub deferred_internal_id: Option<PointOffsetType>,
493}
494
495#[derive(Debug, Default)]
496pub struct SizeStats {
497    pub num_vectors: usize,
498    pub num_vectors_by_name: TinyMap<VectorNameBuf, usize>,
499    pub vectors_size_bytes: usize,
500    pub payloads_size_bytes: usize,
501    pub num_points: usize,
502}
503
504/// Additional parameters of the search
505#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, Copy, PartialEq, Default)]
506#[serde(rename_all = "snake_case")]
507pub struct QuantizationSearchParams {
508    /// If true, quantized vectors are ignored. Default is false.
509    #[serde(default = "default_quantization_ignore_value")]
510    pub ignore: bool,
511
512    /// If true, use original vectors to re-score top-k results.
513    /// Might require more time in case if original vectors are stored on disk.
514    /// If not set, qdrant decides automatically apply rescoring or not.
515    #[serde(default)]
516    #[serde(skip_serializing_if = "Option::is_none")]
517    pub rescore: Option<bool>,
518
519    /// Oversampling factor for quantization. Default is 1.0.
520    ///
521    /// Defines how many extra vectors should be preselected using quantized index,
522    /// and then re-scored using original vectors.
523    ///
524    /// For example, if `oversampling` is 2.4 and `limit` is 100, then 240 vectors will be preselected using quantized index,
525    /// and then top-100 will be returned after re-scoring.
526    #[serde(default = "default_quantization_oversampling_value")]
527    #[validate(range(min = 1.0))]
528    #[serde(skip_serializing_if = "Option::is_none")]
529    pub oversampling: Option<f64>,
530}
531
532impl Hash for QuantizationSearchParams {
533    fn hash<H: Hasher>(&self, state: &mut H) {
534        let Self {
535            ignore,
536            rescore,
537            oversampling,
538        } = self;
539        ignore.hash(state);
540        rescore.hash(state);
541        oversampling.map(OrderedFloat).hash(state);
542    }
543}
544
545pub const fn default_quantization_ignore_value() -> bool {
546    false
547}
548
549pub const fn default_quantization_oversampling_value() -> Option<f64> {
550    None
551}
552
553/// Default value for [`AcornSearchParams::max_selectivity`].
554///
555/// After change, update docs for GRPC and REST API.
556pub const ACORN_MAX_SELECTIVITY_DEFAULT: f64 = 0.4;
557
558/// ACORN-related search parameters
559#[derive(
560    Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, Copy, PartialEq, Default, Hash,
561)]
562#[serde(rename_all = "snake_case")]
563pub struct AcornSearchParams {
564    /// If true, then ACORN may be used for the HNSW search based on filters
565    /// selectivity.
566
567    /// Improves search recall for searches with multiple low-selectivity
568    /// payload filters, at cost of performance.
569    #[serde(default)]
570    pub enable: bool,
571
572    /// Maximum selectivity of filters to enable ACORN.
573    ///
574    /// If estimated filters selectivity is higher than this value,
575    /// ACORN will not be used. Selectivity is estimated as:
576    /// `estimated number of points satisfying the filters / total number of points`.
577    ///
578    /// 0.0 for never, 1.0 for always. Default is 0.4.
579    #[serde(default)]
580    #[serde(skip_serializing_if = "Option::is_none")]
581    #[validate(range(min = 0.0, max = 1.0))]
582    pub max_selectivity: Option<OrderedFloat<f64>>,
583}
584
585/// Additional parameters of the search
586#[derive(
587    Debug, Deserialize, Serialize, JsonSchema, Validate, Copy, Clone, PartialEq, Default, Hash,
588)]
589#[serde(rename_all = "snake_case")]
590pub struct SearchParams {
591    /// Params relevant to HNSW index
592    /// Size of the beam in a beam-search. Larger the value - more accurate the result, more time required for search.
593    #[serde(skip_serializing_if = "Option::is_none")]
594    pub hnsw_ef: Option<usize>,
595
596    /// Search without approximation. If set to true, search may run long but with exact results.
597    #[serde(default)]
598    pub exact: bool,
599
600    /// Quantization params
601    #[serde(default)]
602    #[validate(nested)]
603    #[serde(skip_serializing_if = "Option::is_none")]
604    pub quantization: Option<QuantizationSearchParams>,
605
606    /// If enabled, the engine will only perform search among indexed or small segments.
607    /// Using this option prevents slow searches in case of delayed index, but does not
608    /// guarantee that all uploaded vectors will be included in search results
609    #[serde(default)]
610    pub indexed_only: bool,
611
612    /// ACORN search params
613    #[serde(default)]
614    #[validate(nested)]
615    #[serde(skip_serializing_if = "Option::is_none")]
616    pub acorn: Option<AcornSearchParams>,
617}
618
619/// Configuration for vectors.
620#[derive(Debug, Deserialize, Validate, Clone, PartialEq, Eq)]
621pub struct VectorsConfigDefaults {
622    #[serde(default)]
623    pub on_disk: Option<bool>,
624}
625
626/// Vector index configuration
627#[derive(Debug, Deserialize, Serialize, JsonSchema,  Clone, PartialEq, Eq)]
628#[serde(rename_all = "snake_case")]
629#[serde(tag = "type", content = "options")]
630pub enum Indexes {
631    /// Do not use any index, scan whole vector collection during search.
632    /// Guarantee 100% precision, but may be time consuming on large collections.
633    Plain {},
634    /// Use filterable HNSW index for approximate search. Is very fast even on a very huge collections,
635    /// but require additional space to store index and additional time to build it.
636    Hnsw(HnswConfig),
637}
638
639impl Indexes {
640    pub fn is_indexed(&self) -> bool {
641        match self {
642            Indexes::Plain {} => false,
643            Indexes::Hnsw(_) => true,
644        }
645    }
646
647    pub fn is_on_disk(&self) -> bool {
648        match self {
649            Indexes::Plain {} => false,
650            Indexes::Hnsw(config) => config.on_disk.unwrap_or_default(),
651        }
652    }
653}
654
655/// Config of HNSW index
656#[derive(
657    Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize, JsonSchema, Validate, 
658)]
659#[serde(rename_all = "snake_case")]
660
661pub struct HnswConfig {
662    /// Number of edges per node in the index graph. Larger the value - more accurate the search, more space required.
663    pub m: usize,
664    /// Number of neighbours to consider during the index building. Larger the value - more accurate the search, more time required to build index.
665    #[validate(range(min = 4))]
666    pub ef_construct: usize,
667    /// Minimal size threshold (in KiloBytes) below which full-scan is preferred over HNSW search.
668    /// This measures the total size of vectors being queried against.
669    /// When the maximum estimated amount of points that a condition satisfies is smaller than
670    /// `full_scan_threshold_kb`, the query planner will use full-scan search instead of HNSW index
671    /// traversal for better performance.
672    /// Note: 1Kb = 1 vector of size 256
673    #[serde(alias = "full_scan_threshold_kb")]
674    pub full_scan_threshold: usize,
675    /// Number of parallel threads used for background index building.
676    /// If 0 - automatically select from 8 to 16.
677    /// Best to keep between 8 and 16 to prevent likelihood of slow building or broken/inefficient HNSW graphs.
678    /// On small CPUs, less threads are used.
679    #[serde(default = "default_max_indexing_threads")]
680    pub max_indexing_threads: usize,
681    /// Store HNSW index on disk. If set to false, index will be stored in RAM. Default: false
682    #[serde(default, skip_serializing_if = "Option::is_none")] // Better backward compatibility
683    pub on_disk: Option<bool>,
684    /// Custom M param for hnsw graph built for payload index. If not set, default M will be used.
685    #[serde(default, skip_serializing_if = "Option::is_none")] // Better backward compatibility
686    pub payload_m: Option<usize>,
687    /// Store copies of original and quantized vectors within the HNSW index file. Default: false.
688    /// Enabling this option will trade the search speed for disk usage by reducing amount of
689    /// random seeks during the search.
690    /// Requires quantized vectors to be enabled. Multi-vectors are not supported.
691    #[serde(default, skip_serializing_if = "Option::is_none")]
692    pub inline_storage: Option<bool>,
693}
694
695impl HnswConfig {
696    /// Detect configuration mismatch against `other` that requires rebuilding
697    ///
698    /// Returns true only if both conditions are met:
699    /// - this configuration does not match `other`
700    /// - to effectively change the configuration, a HNSW rebuild is required
701    ///
702    /// For example, a change in `max_indexing_threads` will not require rebuilding because it
703    /// doesn't affect the final index, and thus this would return false.
704    pub fn mismatch_requires_rebuild(&self, other: &Self) -> bool {
705        let HnswConfig {
706            m,
707            ef_construct,
708            full_scan_threshold,
709            max_indexing_threads: _,
710            payload_m,
711            on_disk,
712            inline_storage,
713        } = *self;
714
715        m != other.m
716            || ef_construct != other.ef_construct
717            || full_scan_threshold != other.full_scan_threshold
718            || payload_m != other.payload_m
719            // Data on disk is the same, we have a unit test for that. We can eventually optimize
720            // this to just reload the collection rather than optimizing it again as a whole just
721            // to flip this flag
722            || on_disk != other.on_disk
723            || inline_storage != other.inline_storage
724    }
725}
726
727#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate,  Clone)]
728#[serde(rename_all = "snake_case", default)]
729
730pub struct HnswGlobalConfig {
731    /// Enable HNSW healing if the ratio of missing points is no more than this value.
732    /// To disable healing completely, set this value to `0.0`.
733    #[validate(range(min = 0.0, max = 1.0))]
734    pub healing_threshold: f64,
735}
736
737impl Default for HnswGlobalConfig {
738    fn default() -> Self {
739        Self {
740            healing_threshold: 0.3,
741        }
742    }
743}
744
745const fn default_max_indexing_threads() -> usize {
746    0
747}
748
749#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash)]
750#[serde(rename_all = "lowercase")]
751pub enum CompressionRatio {
752    X4,
753    X8,
754    X16,
755    X32,
756    X64,
757}
758
759#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema)]
760#[serde(rename_all = "lowercase")]
761pub enum ScalarType {
762    #[default]
763    Int8,
764}
765
766#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Validate)]
767#[serde(rename_all = "snake_case")]
768pub struct ScalarQuantizationConfig {
769    /// Type of quantization to use
770    /// If `int8` - 8 bit quantization will be used
771    pub r#type: ScalarType,
772    /// Quantile for quantization. Expected value range in [0.5, 1.0]. If not set - use the whole range of values
773    #[serde(skip_serializing_if = "Option::is_none")]
774    #[validate(range(min = 0.5, max = 1.0))]
775    pub quantile: Option<f32>,
776    /// If true - quantized vectors always will be stored in RAM, ignoring the config of main storage
777    #[serde(skip_serializing_if = "Option::is_none")]
778    pub always_ram: Option<bool>,
779}
780
781impl ScalarQuantizationConfig {
782    /// Detect configuration mismatch against `other` that requires rebuilding
783    ///
784    /// Returns true only if both conditions are met:
785    /// - this configuration does not match `other`
786    /// - to effectively change the configuration, a quantization rebuild is required
787    pub fn mismatch_requires_rebuild(&self, other: &Self) -> bool {
788        self != other
789    }
790}
791
792#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
793pub struct ScalarQuantization {
794    #[validate(nested)]
795    pub scalar: ScalarQuantizationConfig,
796}
797
798#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
799#[serde(rename_all = "snake_case")]
800pub struct ProductQuantizationConfig {
801    pub compression: CompressionRatio,
802
803    #[serde(skip_serializing_if = "Option::is_none")]
804    pub always_ram: Option<bool>,
805}
806
807impl ProductQuantizationConfig {
808    /// Detect configuration mismatch against `other` that requires rebuilding
809    ///
810    /// Returns true only if both conditions are met:
811    /// - this configuration does not match `other`
812    /// - to effectively change the configuration, a quantization rebuild is required
813    pub fn mismatch_requires_rebuild(&self, other: &Self) -> bool {
814        self != other
815    }
816}
817
818#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
819pub struct ProductQuantization {
820    #[validate(nested)]
821    pub product: ProductQuantizationConfig,
822}
823
824impl Hash for ScalarQuantizationConfig {
825    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
826        self.always_ram.hash(state);
827        self.r#type.hash(state);
828    }
829}
830
831impl Eq for ScalarQuantizationConfig {}
832
833#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash, Default)]
834#[serde(rename_all = "snake_case")]
835pub enum BinaryQuantizationEncoding {
836    #[default]
837    OneBit,
838    TwoBits,
839    OneAndHalfBits,
840}
841
842#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
843#[serde(rename_all = "snake_case")]
844pub struct BinaryQuantizationConfig {
845    #[serde(skip_serializing_if = "Option::is_none")]
846    pub always_ram: Option<bool>,
847    #[serde(default)]
848    #[serde(skip_serializing_if = "Option::is_none")]
849    pub encoding: Option<BinaryQuantizationEncoding>,
850
851    /// Asymmetric quantization configuration allows a query to have different quantization than stored vectors.
852    /// It can increase the accuracy of search at the cost of performance.
853    #[serde(default)]
854    #[serde(skip_serializing_if = "Option::is_none")]
855    pub query_encoding: Option<BinaryQuantizationQueryEncoding>,
856}
857
858#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
859pub struct BinaryQuantization {
860    #[validate(nested)]
861    pub binary: BinaryQuantizationConfig,
862}
863
864#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash, Default)]
865#[serde(rename_all = "snake_case")]
866pub enum TurboQuantBitSize {
867    Bits1,
868    Bits1_5,
869    Bits2,
870    #[default]
871    Bits4,
872}
873
874#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
875#[serde(rename_all = "snake_case")]
876pub struct TurboQuantQuantizationConfig {
877    #[serde(skip_serializing_if = "Option::is_none")]
878    pub always_ram: Option<bool>,
879
880    #[serde(default)]
881    #[serde(skip_serializing_if = "Option::is_none")]
882    pub bits: Option<TurboQuantBitSize>,
883}
884
885#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
886pub struct TurboQuantization {
887    #[validate(nested)]
888    pub turbo: TurboQuantQuantizationConfig,
889}
890
891#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, )]
892#[serde(untagged, rename_all = "snake_case")]
893
894pub enum QuantizationConfig {
895    Scalar(ScalarQuantization),
896    Product(ProductQuantization),
897    Binary(BinaryQuantization),
898    Turbo(TurboQuantization),
899}
900
901impl QuantizationConfig {
902    /// If appendable_quantization feature is enabled and config supports appendable segments,
903    /// returns the config for use in appendable segment; otherwise `None`.
904    pub fn for_appendable_segment(opt: Option<&Self>) -> Option<Self> {
905        let appendable = crate::common::flags::feature_flags().appendable_quantization;
906        opt.filter(|q| appendable && q.supports_appendable())
907            .cloned()
908    }
909
910    /// Detect configuration mismatch against `other` that requires rebuilding
911    ///
912    /// Returns true only if both conditions are met:
913    /// - this configuration does not match `other`
914    /// - to effectively change the configuration, a quantization rebuild is required
915    pub fn mismatch_requires_rebuild(&self, other: &Self) -> bool {
916        self != other
917    }
918
919    pub fn supports_appendable(&self) -> bool {
920        matches!(
921            self,
922            QuantizationConfig::Binary(_) | QuantizationConfig::Turbo(_)
923        )
924    }
925
926    pub fn always_ram(&self) -> bool {
927        match self {
928            QuantizationConfig::Scalar(s) => s.scalar.always_ram == Some(true),
929            QuantizationConfig::Product(p) => p.product.always_ram == Some(true),
930            QuantizationConfig::Binary(b) => b.binary.always_ram == Some(true),
931            QuantizationConfig::Turbo(t) => t.turbo.always_ram == Some(true),
932        }
933    }
934}
935
936impl Validate for QuantizationConfig {
937    fn validate(&self) -> Result<(), ValidationErrors> {
938        match self {
939            QuantizationConfig::Scalar(scalar) => scalar.validate(),
940            QuantizationConfig::Product(product) => product.validate(),
941            QuantizationConfig::Binary(binary) => binary.validate(),
942            QuantizationConfig::Turbo(turbo) => turbo.validate(),
943        }
944    }
945}
946
947#[derive(
948    Default, Debug, Deserialize, Serialize, JsonSchema,  Clone, Copy, PartialEq, Eq, Hash,
949)]
950#[serde(rename_all = "lowercase")]
951
952pub enum BinaryQuantizationQueryEncoding {
953    #[default]
954    Default,
955    Binary,
956    Scalar4Bits,
957    Scalar8Bits,
958}
959
960impl From<ScalarQuantizationConfig> for QuantizationConfig {
961    fn from(config: ScalarQuantizationConfig) -> Self {
962        QuantizationConfig::Scalar(ScalarQuantization { scalar: config })
963    }
964}
965
966impl From<ProductQuantizationConfig> for QuantizationConfig {
967    fn from(config: ProductQuantizationConfig) -> Self {
968        QuantizationConfig::Product(ProductQuantization { product: config })
969    }
970}
971
972impl From<BinaryQuantizationConfig> for QuantizationConfig {
973    fn from(config: BinaryQuantizationConfig) -> Self {
974        QuantizationConfig::Binary(BinaryQuantization { binary: config })
975    }
976}
977
978#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
979pub struct StrictModeSparse {
980    /// Max length of sparse vector
981    #[serde(skip_serializing_if = "Option::is_none")]
982    #[validate(range(min = 1))]
983    pub max_length: Option<usize>,
984}
985
986#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
987#[schemars(deny_unknown_fields)]
988pub struct StrictModeSparseConfig {
989    #[validate(nested)]
990    #[serde(flatten)]
991    pub config: BTreeMap<VectorNameBuf, StrictModeSparse>,
992}
993
994#[derive(Debug, Deserialize, Serialize, JsonSchema,  Clone, PartialEq, Default)]
995#[schemars(deny_unknown_fields)]
996pub struct StrictModeSparseConfigOutput {
997    #[serde(flatten)]
998    pub config: BTreeMap<VectorNameBuf, StrictModeSparseOutput>,
999}
1000
1001#[derive(Debug, Deserialize, Serialize, JsonSchema,  Clone, PartialEq, Default)]
1002pub struct StrictModeSparseOutput {
1003    /// Max length of sparse vector
1004    #[serde(skip_serializing_if = "Option::is_none")]
1005    
1006    pub max_length: Option<usize>,
1007}
1008
1009impl From<StrictModeSparseConfig> for StrictModeSparseConfigOutput {
1010    fn from(config: StrictModeSparseConfig) -> Self {
1011        let StrictModeSparseConfig { config } = config;
1012        let mut new_config = StrictModeSparseConfigOutput::default();
1013        for (key, value) in config {
1014            new_config
1015                .config
1016                .insert(key, StrictModeSparseOutput::from(value));
1017        }
1018        new_config
1019    }
1020}
1021
1022impl From<StrictModeSparse> for StrictModeSparseOutput {
1023    fn from(config: StrictModeSparse) -> Self {
1024        let StrictModeSparse { max_length } = config;
1025        StrictModeSparseOutput { max_length }
1026    }
1027}
1028
1029#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
1030pub struct StrictModeMultivector {
1031    /// Max number of vectors in a multivector
1032    #[serde(skip_serializing_if = "Option::is_none")]
1033    #[validate(range(min = 1))]
1034    pub max_vectors: Option<usize>,
1035}
1036
1037#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
1038#[schemars(deny_unknown_fields)]
1039pub struct StrictModeMultivectorConfig {
1040    #[validate(nested)]
1041    #[serde(flatten)]
1042    pub config: BTreeMap<VectorNameBuf, StrictModeMultivector>,
1043}
1044
1045#[derive(Debug, Deserialize, Serialize, JsonSchema,  Clone, PartialEq, Default)]
1046#[schemars(deny_unknown_fields)]
1047pub struct StrictModeMultivectorConfigOutput {
1048    #[serde(flatten)]
1049    pub config: BTreeMap<VectorNameBuf, StrictModeMultivectorOutput>,
1050}
1051
1052impl From<StrictModeMultivectorConfig> for StrictModeMultivectorConfigOutput {
1053    fn from(config: StrictModeMultivectorConfig) -> Self {
1054        let StrictModeMultivectorConfig { config } = config;
1055        let mut new_config = StrictModeMultivectorConfigOutput::default();
1056        for (key, value) in config {
1057            new_config
1058                .config
1059                .insert(key, StrictModeMultivectorOutput::from(value));
1060        }
1061        new_config
1062    }
1063}
1064
1065#[derive(Debug, Deserialize, Serialize, JsonSchema,  Clone, PartialEq, Default)]
1066pub struct StrictModeMultivectorOutput {
1067    /// Max number of vectors in a multivector
1068    #[serde(skip_serializing_if = "Option::is_none")]
1069    
1070    pub max_vectors: Option<usize>,
1071}
1072
1073impl From<StrictModeMultivector> for StrictModeMultivectorOutput {
1074    fn from(config: StrictModeMultivector) -> Self {
1075        let StrictModeMultivector { max_vectors } = config;
1076        StrictModeMultivectorOutput { max_vectors }
1077    }
1078}
1079
1080#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default)]
1081pub struct StrictModeConfig {
1082    // Global
1083    /// Whether strict mode is enabled for a collection or not.
1084    #[serde(skip_serializing_if = "Option::is_none")]
1085    pub enabled: Option<bool>,
1086
1087    /// Max allowed `limit` parameter for all APIs that don't have their own max limit.
1088    #[serde(skip_serializing_if = "Option::is_none")]
1089    #[validate(range(min = 1))]
1090    pub max_query_limit: Option<usize>,
1091
1092    /// Max allowed `timeout` parameter.
1093    #[serde(skip_serializing_if = "Option::is_none")]
1094    #[validate(range(min = 1))]
1095    pub max_timeout: Option<usize>,
1096
1097    /// Allow usage of unindexed fields in retrieval based (e.g. search) filters.
1098    #[serde(skip_serializing_if = "Option::is_none")]
1099    pub unindexed_filtering_retrieve: Option<bool>,
1100
1101    /// Allow usage of unindexed fields in filtered updates (e.g. delete by payload).
1102    #[serde(skip_serializing_if = "Option::is_none")]
1103    pub unindexed_filtering_update: Option<bool>,
1104
1105    // Search
1106    /// Max HNSW ef value allowed in search parameters.
1107    #[serde(skip_serializing_if = "Option::is_none")]
1108    pub search_max_hnsw_ef: Option<usize>,
1109
1110    /// Whether exact search is allowed.
1111    #[serde(skip_serializing_if = "Option::is_none")]
1112    pub search_allow_exact: Option<bool>,
1113
1114    /// Max oversampling value allowed in search.
1115    #[serde(skip_serializing_if = "Option::is_none")]
1116    pub search_max_oversampling: Option<f64>,
1117
1118    /// Max batchsize when upserting
1119    #[serde(skip_serializing_if = "Option::is_none")]
1120    pub upsert_max_batchsize: Option<usize>,
1121
1122    /// Max batchsize when searching
1123    #[serde(skip_serializing_if = "Option::is_none")]
1124    pub search_max_batchsize: Option<usize>,
1125
1126    /// Max size of a collections vector storage in bytes, ignoring replicas.
1127    #[serde(skip_serializing_if = "Option::is_none")]
1128    pub max_collection_vector_size_bytes: Option<usize>,
1129
1130    /// Max number of read operations per minute per replica
1131    #[serde(skip_serializing_if = "Option::is_none")]
1132    #[validate(range(min = 1))]
1133    pub read_rate_limit: Option<usize>,
1134
1135    /// Max number of write operations per minute per replica
1136    #[serde(skip_serializing_if = "Option::is_none")]
1137    #[validate(range(min = 1))]
1138    pub write_rate_limit: Option<usize>,
1139
1140    /// Max size of a collections payload storage in bytes
1141    #[serde(skip_serializing_if = "Option::is_none")]
1142    pub max_collection_payload_size_bytes: Option<usize>,
1143
1144    /// Max number of points estimated in a collection
1145    #[serde(skip_serializing_if = "Option::is_none")]
1146    #[validate(range(min = 1))]
1147    pub max_points_count: Option<usize>,
1148
1149    /// Max conditions a filter can have.
1150    #[serde(skip_serializing_if = "Option::is_none")]
1151    pub filter_max_conditions: Option<usize>,
1152
1153    /// Max size of a condition, eg. items in `MatchAny`.
1154    #[serde(skip_serializing_if = "Option::is_none")]
1155    pub condition_max_size: Option<usize>,
1156
1157    /// Multivector strict mode configuration
1158    #[serde(skip_serializing_if = "Option::is_none")]
1159    #[validate(nested)]
1160    pub multivector_config: Option<StrictModeMultivectorConfig>,
1161
1162    /// Sparse vector strict mode configuration
1163    #[serde(skip_serializing_if = "Option::is_none")]
1164    #[validate(nested)]
1165    pub sparse_config: Option<StrictModeSparseConfig>,
1166
1167    /// Max number of payload indexes in a collection
1168    #[serde(skip_serializing_if = "Option::is_none")]
1169    #[validate(range(min = 0))]
1170    pub max_payload_index_count: Option<usize>,
1171
1172    /// Reject memory-consuming update operations (e.g. upsert, set payload)
1173    /// when the process resident memory exceeds this percentage of total system
1174    /// memory (or cgroup limit). Value in [1, 100]. Applied uniformly to external
1175    /// and internal (replication) traffic — rejection is deterministic so it does
1176    /// not cause replica divergence. Delete operations are not affected, so
1177    /// callers can still free memory.
1178    #[serde(skip_serializing_if = "Option::is_none")]
1179    #[validate(range(min = 1, max = 100))]
1180    pub max_resident_memory_percent: Option<u8>,
1181
1182    /// Reject disk-consuming update operations (e.g. upsert, set payload) when
1183    /// the filesystem hosting Qdrant storage is filled above this percentage
1184    /// of its total capacity. Value in [1, 100]. Applied uniformly to external
1185    /// and internal (replication) traffic — rejection is deterministic so it
1186    /// does not cause replica divergence. Delete operations are not affected,
1187    /// so callers can still free disk space. Free space is sampled with a
1188    /// small TTL cache; the gate may take a few seconds to react.
1189    #[serde(skip_serializing_if = "Option::is_none")]
1190    #[validate(range(min = 1, max = 100))]
1191    pub max_disk_usage_percent: Option<u8>,
1192}
1193
1194impl Eq for StrictModeConfig {}
1195
1196impl Hash for StrictModeConfig {
1197    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1198        let Self {
1199            enabled,
1200            max_query_limit,
1201            max_timeout,
1202            unindexed_filtering_retrieve,
1203            unindexed_filtering_update,
1204            search_max_hnsw_ef,
1205            search_allow_exact,
1206            // We skip hashing this field because we cannot reliably hash a float
1207            search_max_oversampling: _,
1208            upsert_max_batchsize,
1209            search_max_batchsize,
1210            max_collection_vector_size_bytes,
1211            read_rate_limit,
1212            write_rate_limit,
1213            max_collection_payload_size_bytes,
1214            max_points_count,
1215            filter_max_conditions,
1216            condition_max_size,
1217            multivector_config,
1218            sparse_config,
1219            max_payload_index_count,
1220            max_resident_memory_percent,
1221            max_disk_usage_percent,
1222        } = self;
1223        enabled.hash(state);
1224        max_query_limit.hash(state);
1225        max_timeout.hash(state);
1226        unindexed_filtering_retrieve.hash(state);
1227        unindexed_filtering_update.hash(state);
1228        search_max_hnsw_ef.hash(state);
1229        search_allow_exact.hash(state);
1230        upsert_max_batchsize.hash(state);
1231        search_max_batchsize.hash(state);
1232        max_collection_vector_size_bytes.hash(state);
1233        read_rate_limit.hash(state);
1234        write_rate_limit.hash(state);
1235        max_collection_payload_size_bytes.hash(state);
1236        max_points_count.hash(state);
1237        filter_max_conditions.hash(state);
1238        condition_max_size.hash(state);
1239        multivector_config.hash(state);
1240        sparse_config.hash(state);
1241        max_payload_index_count.hash(state);
1242        max_resident_memory_percent.hash(state);
1243        max_disk_usage_percent.hash(state);
1244    }
1245}
1246
1247// Version of the strict mode config we can present to the user
1248#[derive(Debug, Deserialize, Serialize, JsonSchema,  Clone, PartialEq, Default)]
1249pub struct StrictModeConfigOutput {
1250    // Global
1251    /// Whether strict mode is enabled for a collection or not.
1252    #[serde(skip_serializing_if = "Option::is_none")]
1253    pub enabled: Option<bool>,
1254
1255    /// Max allowed `limit` parameter for all APIs that don't have their own max limit.
1256    #[serde(skip_serializing_if = "Option::is_none")]
1257    
1258    pub max_query_limit: Option<usize>,
1259
1260    /// Max allowed `timeout` parameter.
1261    #[serde(skip_serializing_if = "Option::is_none")]
1262    
1263    pub max_timeout: Option<usize>,
1264
1265    /// Allow usage of unindexed fields in retrieval based (e.g. search) filters.
1266    #[serde(skip_serializing_if = "Option::is_none")]
1267    pub unindexed_filtering_retrieve: Option<bool>,
1268
1269    /// Allow usage of unindexed fields in filtered updates (e.g. delete by payload).
1270    #[serde(skip_serializing_if = "Option::is_none")]
1271    pub unindexed_filtering_update: Option<bool>,
1272
1273    // Search
1274    /// Max HNSW value allowed in search parameters.
1275    #[serde(skip_serializing_if = "Option::is_none")]
1276    
1277    pub search_max_hnsw_ef: Option<usize>,
1278
1279    /// Whether exact search is allowed or not.
1280    #[serde(skip_serializing_if = "Option::is_none")]
1281    pub search_allow_exact: Option<bool>,
1282
1283    /// Max oversampling value allowed in search.
1284    #[serde(skip_serializing_if = "Option::is_none")]
1285    
1286    pub search_max_oversampling: Option<f64>,
1287
1288    /// Max batchsize when upserting
1289    #[serde(skip_serializing_if = "Option::is_none")]
1290    
1291    pub upsert_max_batchsize: Option<usize>,
1292    /// Max batchsize when searching
1293    #[serde(skip_serializing_if = "Option::is_none")]
1294    
1295    pub search_max_batchsize: Option<usize>,
1296
1297    /// Max size of a collections vector storage in bytes, ignoring replicas.
1298    #[serde(skip_serializing_if = "Option::is_none")]
1299    
1300    pub max_collection_vector_size_bytes: Option<usize>,
1301
1302    /// Max number of read operations per minute per replica
1303    #[serde(skip_serializing_if = "Option::is_none")]
1304    
1305    pub read_rate_limit: Option<usize>,
1306
1307    /// Max number of write operations per minute per replica
1308    #[serde(skip_serializing_if = "Option::is_none")]
1309    
1310    pub write_rate_limit: Option<usize>,
1311
1312    /// Max size of a collections payload storage in bytes
1313    #[serde(skip_serializing_if = "Option::is_none")]
1314    
1315    pub max_collection_payload_size_bytes: Option<usize>,
1316
1317    /// Max number of points estimated in a collection
1318    #[serde(skip_serializing_if = "Option::is_none")]
1319    
1320    pub max_points_count: Option<usize>,
1321
1322    /// Max conditions a filter can have.
1323    #[serde(skip_serializing_if = "Option::is_none")]
1324    
1325    pub filter_max_conditions: Option<usize>,
1326
1327    /// Max size of a condition, eg. items in `MatchAny`.
1328    #[serde(skip_serializing_if = "Option::is_none")]
1329    
1330    pub condition_max_size: Option<usize>,
1331
1332    /// Multivector configuration
1333    #[serde(skip_serializing_if = "Option::is_none")]
1334    pub multivector_config: Option<StrictModeMultivectorConfigOutput>,
1335
1336    /// Sparse vector configuration
1337    #[serde(skip_serializing_if = "Option::is_none")]
1338    pub sparse_config: Option<StrictModeSparseConfigOutput>,
1339
1340    /// Max number of payload indexes in a collection
1341    #[serde(skip_serializing_if = "Option::is_none")]
1342    pub max_payload_index_count: Option<usize>,
1343
1344    /// Reject memory-consuming update operations when resident memory exceeds this percentage of total RAM (1-100)
1345    #[serde(skip_serializing_if = "Option::is_none")]
1346    
1347    pub max_resident_memory_percent: Option<u8>,
1348
1349    /// Reject disk-consuming update operations when the storage filesystem exceeds this percentage of total capacity (1-100)
1350    #[serde(skip_serializing_if = "Option::is_none")]
1351    
1352    pub max_disk_usage_percent: Option<u8>,
1353}
1354
1355impl From<StrictModeConfig> for StrictModeConfigOutput {
1356    fn from(config: StrictModeConfig) -> Self {
1357        let StrictModeConfig {
1358            enabled,
1359            max_query_limit,
1360            max_timeout,
1361            unindexed_filtering_retrieve,
1362            unindexed_filtering_update,
1363            search_max_hnsw_ef,
1364            search_allow_exact,
1365            search_max_oversampling,
1366            upsert_max_batchsize,
1367            search_max_batchsize,
1368            max_collection_vector_size_bytes,
1369            read_rate_limit,
1370            write_rate_limit,
1371            max_collection_payload_size_bytes,
1372            max_points_count,
1373            filter_max_conditions,
1374            condition_max_size,
1375            multivector_config,
1376            sparse_config,
1377            max_payload_index_count,
1378            max_resident_memory_percent,
1379            max_disk_usage_percent,
1380        } = config;
1381
1382        Self {
1383            enabled,
1384            max_query_limit,
1385            max_timeout,
1386            unindexed_filtering_retrieve,
1387            unindexed_filtering_update,
1388            search_max_hnsw_ef,
1389            search_allow_exact,
1390            search_max_oversampling,
1391            upsert_max_batchsize,
1392            search_max_batchsize,
1393            max_collection_vector_size_bytes,
1394            read_rate_limit,
1395            write_rate_limit,
1396            max_collection_payload_size_bytes,
1397            max_points_count,
1398            filter_max_conditions,
1399            condition_max_size,
1400            multivector_config: multivector_config.map(StrictModeMultivectorConfigOutput::from),
1401            sparse_config: sparse_config.map(StrictModeSparseConfigOutput::from),
1402            max_payload_index_count,
1403            max_resident_memory_percent,
1404            max_disk_usage_percent,
1405        }
1406    }
1407}
1408
1409pub const DEFAULT_HNSW_EF_CONSTRUCT: usize = 100;
1410
1411impl Default for HnswConfig {
1412    fn default() -> Self {
1413        HnswConfig {
1414            m: 16,
1415            ef_construct: DEFAULT_HNSW_EF_CONSTRUCT,
1416            full_scan_threshold: DEFAULT_FULL_SCAN_THRESHOLD,
1417            max_indexing_threads: 0,
1418            on_disk: Some(false),
1419            payload_m: None,
1420            inline_storage: None,
1421        }
1422    }
1423}
1424
1425impl Default for Indexes {
1426    fn default() -> Self {
1427        Indexes::Plain {}
1428    }
1429}
1430
1431/// Type of payload storage
1432#[derive( Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq)]
1433#[serde(tag = "type", content = "options", rename_all = "snake_case")]
1434pub enum PayloadStorageType {
1435    // Store payload on disk and in memory, read from memory if possible
1436    Mmap,
1437    // Store payload on disk and in memory, populate on load
1438    InRamMmap,
1439}
1440
1441#[cfg(any(test, feature = "testing"))]
1442#[allow(clippy::derivable_impls)]
1443impl Default for PayloadStorageType {
1444    fn default() -> Self {
1445        PayloadStorageType::Mmap
1446    }
1447}
1448
1449impl PayloadStorageType {
1450    /// Convert user-facing `on_disk_payload` (true = store on disk) to storage type.
1451    /// Returns `Mmap` or `InRamMmap`; for RocksDB-backed variants use collection config.
1452    pub fn from_on_disk_payload(on_disk: bool) -> Self {
1453        if on_disk { Self::Mmap } else { Self::InRamMmap }
1454    }
1455
1456    pub fn is_on_disk(&self) -> bool {
1457        match self {
1458            PayloadStorageType::Mmap => true,
1459            PayloadStorageType::InRamMmap => false,
1460        }
1461    }
1462}
1463
1464#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, )]
1465#[serde(rename_all = "snake_case")]
1466pub struct SegmentConfig {
1467    #[serde(default)]
1468    pub vector_data: HashMap<VectorNameBuf, VectorDataConfig>,
1469    #[serde(default)]
1470    #[serde(skip_serializing_if = "HashMap::is_empty")]
1471    pub sparse_vector_data: HashMap<VectorNameBuf, SparseVectorDataConfig>,
1472    /// Defines payload storage type
1473    pub payload_storage_type: PayloadStorageType,
1474}
1475
1476impl SegmentConfig {
1477    /// Helper to get vector specific quantization config.
1478    ///
1479    /// This grabs the quantization config for the given vector name if it exists.
1480    ///
1481    /// If no quantization is configured, `None` is returned.
1482    pub fn quantization_config(&self, vector_name: &VectorName) -> Option<&QuantizationConfig> {
1483        self.vector_data
1484            .get(vector_name)
1485            .and_then(|v| v.quantization_config.as_ref())
1486    }
1487
1488    /// Check if any vector storages are indexed
1489    pub fn is_any_vector_indexed(&self) -> bool {
1490        self.vector_data
1491            .values()
1492            .any(|config| config.index.is_indexed())
1493            || self
1494                .sparse_vector_data
1495                .values()
1496                .any(|config| config.is_indexed())
1497    }
1498
1499    /// Check if any vector storage is on-disk
1500    pub fn is_any_on_disk(&self) -> bool {
1501        self.vector_data
1502            .values()
1503            .any(|config| config.storage_type.is_on_disk())
1504            || self
1505                .sparse_vector_data
1506                .values()
1507                .any(|config| config.index.index_type.is_on_disk())
1508    }
1509
1510    pub fn is_appendable(&self) -> bool {
1511        self.vector_data
1512            .values()
1513            .map(|vector_config| vector_config.is_appendable())
1514            .chain(
1515                self.sparse_vector_data
1516                    .values()
1517                    .map(|sparse_vector_config| {
1518                        sparse_vector_config.index.index_type.is_appendable()
1519                    }),
1520            )
1521            .all(|v| v)
1522    }
1523
1524    pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
1525        // Vector data have to be compatible between two segments.
1526        // Sparse vector data can be different, but a placeholder check is implemented to catch
1527        // and enforce compatibility check for future changes.
1528        // Payload storage type can be different.
1529
1530        // Assert segment config fields
1531        let Self {
1532            vector_data: _,
1533            sparse_vector_data: _,
1534            payload_storage_type: _,
1535        } = self;
1536
1537        check_vectors_map_compatible(
1538            &self.vector_data,
1539            &other.vector_data,
1540            VectorDataConfig::check_compatible,
1541        )?;
1542
1543        check_vectors_map_compatible(
1544            &self.sparse_vector_data,
1545            &other.sparse_vector_data,
1546            SparseVectorDataConfig::check_compatible,
1547        )?;
1548
1549        Ok(())
1550    }
1551}
1552
1553fn check_vectors_map_compatible<C, F>(
1554    this: &HashMap<String, C>,
1555    other: &HashMap<String, C>,
1556    check: F,
1557) -> Result<(), String>
1558where
1559    F: Fn(&C, &C) -> Result<(), String>,
1560{
1561    if this.len() != other.len() {
1562        let expected_keys: Vec<String> = this.keys().map(|k| format!("{k:?}")).collect();
1563        let actual_keys: Vec<String> = other.keys().map(|k| format!("{k:?}")).collect();
1564        return Err(format!(
1565            "Incompatible configs: expected vector storages with keys {expected_keys:?}, but got {actual_keys:?}"
1566        ));
1567    }
1568
1569    for (vector_name, config) in this {
1570        let Some(other_config) = other.get(vector_name) else {
1571            return Err(format!(
1572                "Incompatible configs: expected vector storage with key {vector_name:?} not found in other config"
1573            ));
1574        };
1575
1576        check(config, other_config)
1577            .map_err(|err| format!("Incompatible config for vector {vector_name:?}: {err}"))?;
1578    }
1579
1580    Ok(())
1581}
1582
1583/// Storage types for vectors
1584#[derive(Debug, Deserialize, Serialize, JsonSchema,  Eq, PartialEq, Copy, Clone)]
1585pub enum VectorStorageType {
1586    /// Storage in memory (RAM)
1587    ///
1588    /// Will be very fast at the cost of consuming a lot of memory.
1589    Memory,
1590    /// Storage in mmap file, not appendable
1591    ///
1592    /// Search performance is defined by disk speed and the fraction of vectors that fit in memory.
1593    Mmap,
1594    /// Storage in chunked mmap files, appendable
1595    ///
1596    /// Search performance is defined by disk speed and the fraction of vectors that fit in memory.
1597    ChunkedMmap,
1598    /// Same as `ChunkedMmap`, but vectors are forced to be locked in RAM
1599    /// In this way we avoid cold requests to disk, but risk to run out of memory
1600    ///
1601    /// Designed as a replacement for `Memory`, which doesn't depend on RocksDB
1602    InRamChunkedMmap,
1603    /// Storage in a single mmap file, not appendable
1604    /// Pre-fetched into RAM on load
1605    InRamMmap,
1606    /// Placeholder storage: contains no data, all vectors reported as deleted.
1607    /// Used for newly created named vectors on immutable segments.
1608    /// No files on disk, reconstructed from config on load.
1609    Empty,
1610}
1611
1612#[cfg(any(test, feature = "testing"))]
1613#[allow(clippy::derivable_impls)]
1614impl Default for VectorStorageType {
1615    fn default() -> Self {
1616        VectorStorageType::InRamChunkedMmap
1617    }
1618}
1619
1620/// Storage types for vectors
1621#[derive(
1622    Default, Debug, Deserialize, Serialize, JsonSchema,  Eq, PartialEq, Copy, Clone, Hash,
1623)]
1624#[serde(rename_all = "snake_case")]
1625pub enum VectorStorageDatatype {
1626    // Single-precision floating point
1627    #[default]
1628    Float32,
1629    // Half-precision floating point
1630    Float16,
1631    // Unsigned 8-bit integer
1632    Uint8,
1633    // TurboQuant 4-bit compressed storage
1634    Turbo4,
1635}
1636
1637#[derive(
1638    Debug, Default, Deserialize, Serialize, JsonSchema,  Eq, PartialEq, Copy, Clone, Hash,
1639)]
1640#[serde(rename_all = "snake_case")]
1641pub struct MultiVectorConfig {
1642    /// How to compare multivector points
1643    pub comparator: MultiVectorComparator,
1644}
1645
1646impl MultiVectorConfig {
1647    fn check_compatible(&self, other: &Self) -> Result<(), String> {
1648        // Assert multi-vector config fields
1649        let Self { comparator } = self;
1650
1651        if *comparator != other.comparator {
1652            return Err(format!(
1653                "Incompatible configs: expected multi-vector comparator {comparator:?}, but got {other_comparator:?}",
1654                other_comparator = other.comparator
1655            ));
1656        }
1657
1658        Ok(())
1659    }
1660}
1661
1662#[derive(
1663    Debug, Default, Deserialize, Serialize, JsonSchema,  Eq, PartialEq, Copy, Clone, Hash,
1664)]
1665#[serde(rename_all = "snake_case")]
1666pub enum MultiVectorComparator {
1667    #[default]
1668    MaxSim,
1669}
1670
1671impl VectorStorageType {
1672    /// Convert user-facing `on_disk` (true = store on disk) to appendable vector storage type.
1673    /// Returns `ChunkedMmap` or `InRamChunkedMmap`.
1674    pub fn from_on_disk(on_disk: bool) -> Self {
1675        if on_disk {
1676            Self::ChunkedMmap
1677        } else {
1678            Self::InRamChunkedMmap
1679        }
1680    }
1681
1682    /// Whether this storage type is a mmap on disk
1683    pub fn is_on_disk(&self) -> bool {
1684        match self {
1685            Self::Memory | Self::InRamChunkedMmap | Self::InRamMmap => false,
1686            Self::Mmap | Self::ChunkedMmap => true,
1687            // Empty storage has no actual data; report based on what the
1688            // runtime EmptyDenseVectorStorage was configured with.
1689            // This fallback returns true to be safe, but callers that need
1690            // the real on-disk status should check the storage instance.
1691            Self::Empty => true,
1692        }
1693    }
1694
1695    /// Whether this is a placeholder empty storage type
1696    pub fn is_empty(&self) -> bool {
1697        matches!(self, Self::Empty)
1698    }
1699}
1700
1701/// Config of single vector data storage
1702#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, )]
1703#[serde(rename_all = "snake_case")]
1704pub struct VectorDataConfig {
1705    /// Size/dimensionality of the vectors used
1706    pub size: usize,
1707    /// Type of distance function used for measuring distance between vectors
1708    pub distance: Distance,
1709    /// Type of storage this vector uses
1710    pub storage_type: VectorStorageType,
1711    /// Type of index used for search
1712    pub index: Indexes,
1713    /// Vector specific quantization config that overrides collection config
1714    pub quantization_config: Option<QuantizationConfig>,
1715    /// Vector specific configuration to enable multiple vectors per point
1716    #[serde(default, skip_serializing_if = "Option::is_none")]
1717    pub multivector_config: Option<MultiVectorConfig>,
1718    /// Vector specific configuration to set specific storage element type
1719    #[serde(default, skip_serializing_if = "Option::is_none")]
1720    pub datatype: Option<VectorStorageDatatype>,
1721}
1722
1723impl VectorDataConfig {
1724    /// Whether this vector data can be appended to
1725    ///
1726    /// This requires an index and storage type that both support appending.
1727    pub fn is_appendable(&self) -> bool {
1728        let is_index_appendable = match self.index {
1729            Indexes::Plain {} => true,
1730            Indexes::Hnsw(_) => false,
1731        };
1732        let is_storage_appendable = match self.storage_type {
1733            VectorStorageType::Memory => true,
1734            VectorStorageType::Mmap => false,
1735            VectorStorageType::ChunkedMmap => true,
1736            VectorStorageType::InRamChunkedMmap => true,
1737            VectorStorageType::InRamMmap => false,
1738            VectorStorageType::Empty => false,
1739        };
1740        is_index_appendable && is_storage_appendable
1741    }
1742
1743    pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
1744        // Size and distance have to be the same for both segments.
1745        // Storage type, index and quantization config can be different.
1746        //
1747        // Assert vector data config fields
1748        let Self {
1749            size,
1750            distance,
1751            storage_type: _,
1752            index: _,
1753            quantization_config: _,
1754            multivector_config,
1755            datatype,
1756        } = self;
1757
1758        if *size != other.size {
1759            return Err(format!(
1760                "Incompatible configs: expected vector size {size}, but got {other_size}",
1761                other_size = other.size
1762            ));
1763        }
1764
1765        if *distance != other.distance {
1766            return Err(format!(
1767                "Incompatible configs: expected distance {distance:?}, but got {other_distance:?}",
1768                other_distance = other.distance
1769            ));
1770        }
1771
1772        let left_datatype = datatype.unwrap_or(VectorStorageDatatype::Float32);
1773        let right_datatype = other.datatype.unwrap_or(VectorStorageDatatype::Float32);
1774        if left_datatype != right_datatype {
1775            return Err(format!(
1776                "Incompatible configs: expected vector storage datatype {left_datatype:?}, but got {right_datatype:?}",
1777            ));
1778        }
1779
1780        match (multivector_config, &other.multivector_config) {
1781            (None, None) => {}
1782            (Some(this), Some(other)) => {
1783                MultiVectorConfig::check_compatible(this, other)?;
1784            }
1785            _ => {
1786                return Err(format!(
1787                    "Incompatible configs: expected multivector config {this_multivector_config:?}, but got {other_multivector_config:?}",
1788                    this_multivector_config = multivector_config,
1789                    other_multivector_config = other.multivector_config
1790                ));
1791            }
1792        }
1793        Ok(())
1794    }
1795}
1796
1797#[derive(
1798    Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, JsonSchema, 
1799)]
1800#[serde(rename_all = "snake_case")]
1801pub enum SparseVectorStorageType {
1802    /// Storage in memory maps (gridstore storage)
1803    #[default]
1804    Mmap,
1805    /// Placeholder storage: contains no data, all vectors reported as deleted.
1806    /// Used for newly created sparse named vectors on immutable segments.
1807    Empty,
1808}
1809
1810impl SparseVectorStorageType {
1811    /// Whether this storage type is a mmap on disk
1812    pub fn is_on_disk(&self) -> bool {
1813        match self {
1814            // Both options are on disk, but we keep it explicit for the case if someone adds a new
1815            // storage type in the future
1816            Self::Mmap | Self::Empty => true,
1817        }
1818    }
1819}
1820
1821/// Config of single sparse vector data storage
1822#[derive(
1823    Copy, Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Validate, 
1824)]
1825#[serde(rename_all = "snake_case")]
1826pub struct SparseVectorDataConfig {
1827    /// Sparse inverted index config
1828    pub index: SparseIndexConfig,
1829
1830    /// Type of storage this sparse vector uses
1831    #[serde(default = "default_sparse_vector_storage_type_when_not_in_config")]
1832    pub storage_type: SparseVectorStorageType,
1833
1834    /// Configures addition value modifications for sparse vectors.
1835    /// Default: none
1836    #[serde(default, skip_serializing_if = "Option::is_none")]
1837    pub modifier: Option<Modifier>,
1838}
1839
1840/// If the storage type is not in config, it means it is the OnDisk variant
1841fn default_sparse_vector_storage_type_when_not_in_config() -> SparseVectorStorageType {
1842    SparseVectorStorageType::default()
1843}
1844
1845impl SparseVectorDataConfig {
1846    pub fn is_indexed(&self) -> bool {
1847        true
1848    }
1849
1850    pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
1851        // Both index and storage type can be different for two segments to be compatible
1852
1853        // Assert sparse vector config fields
1854        let Self {
1855            index: _,
1856            storage_type: _,
1857            modifier,
1858        } = self;
1859
1860        if modifier != &other.modifier {
1861            return Err(format!(
1862                "Incompatible configs: expected sparse vector modifier {modifier:?}, but got {other_modifier:?}",
1863                other_modifier = other.modifier
1864            ));
1865        }
1866
1867        Ok(())
1868    }
1869}
1870
1871/// Default value based on experiments and observations
1872pub const DEFAULT_FULL_SCAN_THRESHOLD: usize = 10_000;
1873
1874pub const DEFAULT_SPARSE_FULL_SCAN_THRESHOLD: usize = 5_000;
1875
1876/// Persistable state of segment configuration
1877#[derive(Debug, Deserialize, Serialize, Clone)]
1878#[serde(rename_all = "snake_case")]
1879pub struct SegmentState {
1880    #[serde(default)]
1881    pub initial_version: Option<SeqNumberType>,
1882    pub version: Option<SeqNumberType>,
1883    pub config: SegmentConfig,
1884}
1885
1886pub type RawGeoPoint = (f64, f64);
1887
1888/// Geo point payload schema
1889#[derive(
1890    Debug,
1891    Deserialize,
1892    Serialize,
1893    JsonSchema,
1894    Clone,
1895    Copy,
1896    PartialEq,
1897    Eq,
1898    Hash,
1899    Default,
1900    PartialOrd,
1901    Ord,
1902    Pod,
1903    Zeroable,
1904)]
1905#[serde(try_from = "GeoPointShadow")]
1906#[repr(C)]
1907pub struct GeoPoint {
1908    pub lon: OrderedFloat<f64>,
1909    pub lat: OrderedFloat<f64>,
1910}
1911
1912/// Ordered sequence of GeoPoints representing the line
1913#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
1914pub struct GeoLineString {
1915    pub points: Vec<GeoPoint>,
1916}
1917
1918#[derive(Deserialize)]
1919struct GeoPointShadow {
1920    pub lon: f64,
1921    pub lat: f64,
1922}
1923
1924#[derive(Debug)]
1925pub struct GeoPointValidationError {
1926    pub lon: f64,
1927    pub lat: f64,
1928}
1929
1930// The error type has to implement Display
1931impl std::fmt::Display for GeoPointValidationError {
1932    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1933        write!(
1934            formatter,
1935            "Wrong format of GeoPoint payload: expected `lat` = {} within [-90;90] and `lon` = {} within [-180;180]",
1936            self.lat, self.lon,
1937        )
1938    }
1939}
1940
1941impl GeoPoint {
1942    pub fn validate(lon: f64, lat: f64) -> Result<(), GeoPointValidationError> {
1943        let max_lon = 180f64;
1944        let min_lon = -180f64;
1945        let max_lat = 90f64;
1946        let min_lat = -90f64;
1947
1948        if !(min_lon..=max_lon).contains(&lon) || !(min_lat..=max_lat).contains(&lat) {
1949            return Err(GeoPointValidationError { lon, lat });
1950        }
1951        Ok(())
1952    }
1953
1954    pub fn new(lon: f64, lat: f64) -> Result<Self, GeoPointValidationError> {
1955        Self::validate(lon, lat)?;
1956        Ok(Self::new_unchecked(lon, lat))
1957    }
1958
1959    pub const fn new_unchecked(lon: f64, lat: f64) -> Self {
1960        GeoPoint {
1961            lon: OrderedFloat(lon),
1962            lat: OrderedFloat(lat),
1963        }
1964    }
1965}
1966
1967impl TryFrom<GeoPointShadow> for GeoPoint {
1968    type Error = GeoPointValidationError;
1969
1970    fn try_from(value: GeoPointShadow) -> Result<Self, Self::Error> {
1971        let GeoPointShadow { lon, lat } = value;
1972        GeoPoint::validate(lon, lat)?;
1973
1974        Ok(Self::new_unchecked(lon, lat))
1975    }
1976}
1977
1978impl From<GeoPoint> for geo::Point {
1979    fn from(
1980        GeoPoint {
1981            lon: OrderedFloat(lon),
1982            lat: OrderedFloat(lat),
1983        }: GeoPoint,
1984    ) -> Self {
1985        Self::new(lon, lat)
1986    }
1987}
1988
1989impl From<RawGeoPoint> for GeoPoint {
1990    fn from((lon, lat): RawGeoPoint) -> Self {
1991        GeoPoint::new(lon, lat).expect("invalid GeoPoint coordinates")
1992    }
1993}
1994
1995impl From<GeoPoint> for RawGeoPoint {
1996    fn from(geo_point: GeoPoint) -> Self {
1997        (geo_point.lon.0, geo_point.lat.0)
1998    }
1999}
2000
2001pub trait PayloadContainer {
2002    /// Return value from payload by path.
2003    /// If value is not present in the payload, returns empty vector.
2004    fn get_value(&self, path: &JsonPath) -> MultiValue<&Value>;
2005
2006    fn get_value_cloned(&self, path: &JsonPath) -> MultiValue<Value> {
2007        self.get_value(path).into_iter().cloned().collect()
2008    }
2009}
2010
2011/// Construct a [`Payload`] value from a JSON literal.
2012///
2013/// Similar to [`serde_json::json!`] but only allows objects (aka maps).
2014macro_rules! payload_json {
2015    ($($tt:tt)*) => {
2016        match ::serde_json::json!( { $($tt)* } ) {
2017            ::serde_json::Value::Object(map) => $crate::segment::types::Payload(map),
2018            _ => unreachable!(),
2019        }
2020    };
2021}
2022
2023#[allow(clippy::unnecessary_wraps)] // Used as schemars example
2024fn payload_example() -> Option<Payload> {
2025    Some(payload_json! {
2026        "city": "London",
2027        "color": "green",
2028    })
2029}
2030
2031#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize, JsonSchema, Hash)]
2032#[schemars(example = "payload_example")]
2033pub struct Payload(pub Map<String, Value>);
2034
2035impl Payload {
2036    pub fn merge(&mut self, value: &Payload) {
2037        utils::merge_map(&mut self.0, &value.0)
2038    }
2039
2040    pub fn merge_by_key(&mut self, value: &Payload, key: &JsonPath) {
2041        JsonPath::value_set(Some(key), &mut self.0, &value.0);
2042    }
2043
2044    pub fn remove(&mut self, path: &JsonPath) -> Vec<Value> {
2045        path.value_remove(&mut self.0).to_vec()
2046    }
2047
2048    pub fn len(&self) -> usize {
2049        self.0.len()
2050    }
2051
2052    pub fn is_empty(&self) -> bool {
2053        self.0.is_empty()
2054    }
2055
2056    pub fn contains_key(&self, key: &str) -> bool {
2057        self.0.contains_key(key)
2058    }
2059
2060    pub fn keys(&self) -> impl Iterator<Item = &String> {
2061        self.0.keys()
2062    }
2063}
2064
2065impl PayloadContainer for Map<String, Value> {
2066    fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2067        path.value_get(self)
2068    }
2069}
2070
2071impl PayloadContainer for Payload {
2072    fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2073        path.value_get(&self.0)
2074    }
2075}
2076
2077impl PayloadContainer for OwnedPayloadRef<'_> {
2078    fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2079        path.value_get(self.as_ref())
2080    }
2081}
2082
2083impl Default for Payload {
2084    fn default() -> Self {
2085        Payload(Map::new())
2086    }
2087}
2088
2089impl IntoIterator for Payload {
2090    type Item = (String, Value);
2091    type IntoIter = serde_json::map::IntoIter;
2092
2093    fn into_iter(self) -> serde_json::map::IntoIter {
2094        self.0.into_iter()
2095    }
2096}
2097
2098impl From<Map<String, Value>> for Payload {
2099    fn from(value: serde_json::Map<String, Value>) -> Self {
2100        Payload(value)
2101    }
2102}
2103
2104#[derive(Clone, Debug)]
2105pub enum OwnedPayloadRef<'a> {
2106    Ref(&'a Map<String, Value>),
2107    Owned(Rc<Map<String, Value>>),
2108}
2109
2110impl Deref for OwnedPayloadRef<'_> {
2111    type Target = Map<String, Value>;
2112
2113    fn deref(&self) -> &Self::Target {
2114        match self {
2115            OwnedPayloadRef::Ref(reference) => reference,
2116            OwnedPayloadRef::Owned(owned) => owned.deref(),
2117        }
2118    }
2119}
2120
2121impl AsRef<Map<String, Value>> for OwnedPayloadRef<'_> {
2122    fn as_ref(&self) -> &Map<String, Value> {
2123        match self {
2124            OwnedPayloadRef::Ref(reference) => reference,
2125            OwnedPayloadRef::Owned(owned) => owned.deref(),
2126        }
2127    }
2128}
2129
2130impl From<Payload> for OwnedPayloadRef<'_> {
2131    fn from(payload: Payload) -> Self {
2132        OwnedPayloadRef::Owned(Rc::new(payload.0))
2133    }
2134}
2135
2136impl From<Map<String, Value>> for OwnedPayloadRef<'_> {
2137    fn from(payload: Map<String, Value>) -> Self {
2138        OwnedPayloadRef::Owned(Rc::new(payload))
2139    }
2140}
2141
2142impl<'a> From<&'a Payload> for OwnedPayloadRef<'a> {
2143    fn from(payload: &'a Payload) -> Self {
2144        OwnedPayloadRef::Ref(&payload.0)
2145    }
2146}
2147
2148impl<'a> From<&'a Map<String, Value>> for OwnedPayloadRef<'a> {
2149    fn from(payload: &'a Map<String, Value>) -> Self {
2150        OwnedPayloadRef::Ref(payload)
2151    }
2152}
2153
2154/// Payload interface structure which ensures that user is allowed to pass payload in
2155/// both - array and single element forms.
2156///
2157/// Example:
2158///
2159/// Both versions should work:
2160/// ```json
2161/// {..., "payload": {"city": {"type": "keyword", "value": ["Berlin", "London"] }}},
2162/// {..., "payload": {"city": {"type": "keyword", "value": "Moscow" }}},
2163/// ```
2164#[derive(Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Clone)]
2165#[serde(untagged, rename_all = "snake_case")]
2166pub enum PayloadVariant<T> {
2167    List(Vec<T>),
2168    Value(T),
2169}
2170
2171/// All possible names of payload types
2172#[derive(
2173    Debug, Deserialize, Serialize, JsonSchema,  Clone, Copy, PartialEq, Hash, Eq, EnumIter,
2174)]
2175#[serde(rename_all = "snake_case")]
2176pub enum PayloadSchemaType {
2177    Keyword,
2178    Integer,
2179    Float,
2180    Geo,
2181    Text,
2182    Bool,
2183    Datetime,
2184    Uuid,
2185}
2186
2187impl PayloadSchemaType {
2188    /// Human-readable type name
2189    pub fn name(&self) -> &'static str {
2190        serde_variant::to_variant_name(&self).unwrap_or("unknown")
2191    }
2192
2193    pub fn expand(&self) -> PayloadSchemaParams {
2194        match self {
2195            Self::Keyword => PayloadSchemaParams::Keyword(KeywordIndexParams::default()),
2196            Self::Integer => PayloadSchemaParams::Integer(IntegerIndexParams::default()),
2197            Self::Float => PayloadSchemaParams::Float(FloatIndexParams::default()),
2198            Self::Geo => PayloadSchemaParams::Geo(GeoIndexParams::default()),
2199            Self::Text => PayloadSchemaParams::Text(TextIndexParams::default()),
2200            Self::Bool => PayloadSchemaParams::Bool(BoolIndexParams::default()),
2201            Self::Datetime => PayloadSchemaParams::Datetime(DatetimeIndexParams::default()),
2202            Self::Uuid => PayloadSchemaParams::Uuid(UuidIndexParams::default()),
2203        }
2204    }
2205}
2206
2207/// Payload type with parameters
2208#[derive(Debug, Deserialize, Serialize, JsonSchema,  Clone, PartialEq, Hash, Eq)]
2209#[serde(untagged, rename_all = "snake_case")]
2210
2211pub enum PayloadSchemaParams {
2212    Keyword(KeywordIndexParams),
2213    Integer(IntegerIndexParams),
2214    Float(FloatIndexParams),
2215    Geo(GeoIndexParams),
2216    Text(TextIndexParams),
2217    Bool(BoolIndexParams),
2218    Datetime(DatetimeIndexParams),
2219    Uuid(UuidIndexParams),
2220}
2221
2222impl PayloadSchemaParams {
2223    /// Human-readable type name
2224    pub fn name(&self) -> &'static str {
2225        self.kind().name()
2226    }
2227
2228    pub fn kind(&self) -> PayloadSchemaType {
2229        match self {
2230            PayloadSchemaParams::Keyword(_) => PayloadSchemaType::Keyword,
2231            PayloadSchemaParams::Integer(_) => PayloadSchemaType::Integer,
2232            PayloadSchemaParams::Float(_) => PayloadSchemaType::Float,
2233            PayloadSchemaParams::Geo(_) => PayloadSchemaType::Geo,
2234            PayloadSchemaParams::Text(_) => PayloadSchemaType::Text,
2235            PayloadSchemaParams::Bool(_) => PayloadSchemaType::Bool,
2236            PayloadSchemaParams::Datetime(_) => PayloadSchemaType::Datetime,
2237            PayloadSchemaParams::Uuid(_) => PayloadSchemaType::Uuid,
2238        }
2239    }
2240
2241    pub fn tenant_optimization(&self) -> bool {
2242        match self {
2243            PayloadSchemaParams::Keyword(keyword) => keyword.is_tenant.unwrap_or_default(),
2244            PayloadSchemaParams::Integer(integer) => integer.is_principal.unwrap_or_default(),
2245            PayloadSchemaParams::Float(float) => float.is_principal.unwrap_or_default(),
2246            PayloadSchemaParams::Datetime(datetime) => datetime.is_principal.unwrap_or_default(),
2247            PayloadSchemaParams::Uuid(uuid) => uuid.is_tenant.unwrap_or_default(),
2248            PayloadSchemaParams::Geo(_)
2249            | PayloadSchemaParams::Text(_)
2250            | PayloadSchemaParams::Bool(_) => false,
2251        }
2252    }
2253
2254    pub fn is_on_disk(&self) -> bool {
2255        match self {
2256            PayloadSchemaParams::Keyword(i) => i.on_disk.unwrap_or_default(),
2257            PayloadSchemaParams::Integer(i) => i.on_disk.unwrap_or_default(),
2258            PayloadSchemaParams::Float(i) => i.on_disk.unwrap_or_default(),
2259            PayloadSchemaParams::Datetime(i) => i.on_disk.unwrap_or_default(),
2260            PayloadSchemaParams::Uuid(i) => i.on_disk.unwrap_or_default(),
2261            PayloadSchemaParams::Text(i) => i.on_disk.unwrap_or_default(),
2262            PayloadSchemaParams::Geo(i) => i.on_disk.unwrap_or_default(),
2263            PayloadSchemaParams::Bool(i) => i.on_disk.unwrap_or_default(),
2264        }
2265    }
2266
2267    pub fn enable_hnsw(&self) -> bool {
2268        match self {
2269            PayloadSchemaParams::Keyword(params) => params.enable_hnsw.unwrap_or(true),
2270            PayloadSchemaParams::Integer(params) => params.enable_hnsw.unwrap_or(true),
2271            PayloadSchemaParams::Float(params) => params.enable_hnsw.unwrap_or(true),
2272            PayloadSchemaParams::Datetime(params) => params.enable_hnsw.unwrap_or(true),
2273            PayloadSchemaParams::Uuid(params) => params.enable_hnsw.unwrap_or(true),
2274            PayloadSchemaParams::Text(params) => params.enable_hnsw.unwrap_or(true),
2275            PayloadSchemaParams::Geo(params) => params.enable_hnsw.unwrap_or(true),
2276            PayloadSchemaParams::Bool(params) => params.enable_hnsw.unwrap_or(true),
2277        }
2278    }
2279}
2280
2281impl Validate for PayloadSchemaParams {
2282    fn validate(&self) -> Result<(), ValidationErrors> {
2283        match self {
2284            PayloadSchemaParams::Keyword(_) => Ok(()),
2285            PayloadSchemaParams::Integer(integer_index_params) => integer_index_params.validate(),
2286            PayloadSchemaParams::Float(_) => Ok(()),
2287            PayloadSchemaParams::Geo(_) => Ok(()),
2288            PayloadSchemaParams::Text(_) => Ok(()),
2289            PayloadSchemaParams::Bool(_) => Ok(()),
2290            PayloadSchemaParams::Datetime(_) => Ok(()),
2291            PayloadSchemaParams::Uuid(_) => Ok(()),
2292        }
2293    }
2294}
2295
2296#[derive(Clone, Debug, Eq, Deserialize, Serialize, JsonSchema)]
2297#[serde(untagged, rename_all = "snake_case")]
2298pub enum PayloadFieldSchema {
2299    FieldType(PayloadSchemaType),
2300    FieldParams(PayloadSchemaParams),
2301}
2302
2303impl PartialEq for PayloadFieldSchema {
2304    fn eq(&self, other: &Self) -> bool {
2305        match (self, other) {
2306            (Self::FieldType(this), Self::FieldType(other)) => this == other,
2307            (Self::FieldParams(this), Self::FieldParams(other)) => this == other,
2308            (Self::FieldType(this), Self::FieldParams(other)) => &this.expand() == other,
2309            (Self::FieldParams(this), Self::FieldType(other)) => this == &other.expand(),
2310        }
2311    }
2312}
2313
2314impl hash::Hash for PayloadFieldSchema {
2315    fn hash<H: hash::Hasher>(&self, state: &mut H) {
2316        match self {
2317            PayloadFieldSchema::FieldType(default) => default.expand().hash(state),
2318            PayloadFieldSchema::FieldParams(params) => params.hash(state),
2319        }
2320    }
2321}
2322
2323impl Validate for PayloadFieldSchema {
2324    fn validate(&self) -> Result<(), ValidationErrors> {
2325        match self {
2326            PayloadFieldSchema::FieldType(_) => Ok(()), // nothing to validate
2327            PayloadFieldSchema::FieldParams(payload_schema_params) => {
2328                payload_schema_params.validate()
2329            }
2330        }
2331    }
2332}
2333
2334impl Display for PayloadFieldSchema {
2335    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2336        match self {
2337            PayloadFieldSchema::FieldType(t) => write!(f, "{}", t.name()),
2338            PayloadFieldSchema::FieldParams(params) => match params {
2339                PayloadSchemaParams::Keyword(_)
2340                | PayloadSchemaParams::Float(_)
2341                | PayloadSchemaParams::Geo(_)
2342                | PayloadSchemaParams::Bool(_)
2343                | PayloadSchemaParams::Datetime(_)
2344                | PayloadSchemaParams::Uuid(_) => write!(f, "{}", params.name()),
2345                PayloadSchemaParams::Integer(integer_params) => {
2346                    let range = integer_params.range.unwrap_or(true);
2347                    let lookup = integer_params.lookup.unwrap_or(true);
2348                    if range && lookup {
2349                        write!(f, "integer")
2350                    } else {
2351                        write!(f, "integer (with range: {range}, lookup: {lookup})")
2352                    }
2353                }
2354                PayloadSchemaParams::Text(text_params) => {
2355                    if text_params.phrase_matching.unwrap_or_default() {
2356                        write!(f, "text (with phrase_matching: true)")
2357                    } else {
2358                        write!(f, "text")
2359                    }
2360                }
2361            },
2362        }
2363    }
2364}
2365
2366impl PayloadFieldSchema {
2367    pub fn expand(&self) -> Cow<'_, PayloadSchemaParams> {
2368        match self {
2369            PayloadFieldSchema::FieldType(t) => Cow::Owned(t.expand()),
2370            PayloadFieldSchema::FieldParams(p) => Cow::Borrowed(p),
2371        }
2372    }
2373
2374    /// Human-readable type name
2375    pub fn name(&self) -> &'static str {
2376        match self {
2377            PayloadFieldSchema::FieldType(field_type) => field_type.name(),
2378            PayloadFieldSchema::FieldParams(field_params) => field_params.name(),
2379        }
2380    }
2381
2382    pub fn is_tenant(&self) -> bool {
2383        match self {
2384            PayloadFieldSchema::FieldType(_) => false,
2385            PayloadFieldSchema::FieldParams(params) => params.tenant_optimization(),
2386        }
2387    }
2388
2389    pub fn is_on_disk(&self) -> bool {
2390        match self {
2391            PayloadFieldSchema::FieldType(_) => false,
2392            PayloadFieldSchema::FieldParams(params) => params.is_on_disk(),
2393        }
2394    }
2395
2396    pub fn kind(&self) -> PayloadSchemaType {
2397        match self {
2398            PayloadFieldSchema::FieldType(t) => *t,
2399            PayloadFieldSchema::FieldParams(p) => p.kind(),
2400        }
2401    }
2402
2403    /// Check if this type supports a `match` condition
2404    pub fn supports_match(&self) -> bool {
2405        match self {
2406            PayloadFieldSchema::FieldType(payload_schema_type) => match payload_schema_type {
2407                PayloadSchemaType::Keyword => true,
2408                PayloadSchemaType::Integer => true,
2409                PayloadSchemaType::Uuid => true,
2410                PayloadSchemaType::Bool => true,
2411                PayloadSchemaType::Float => false,
2412                PayloadSchemaType::Geo => false,
2413                PayloadSchemaType::Text => false,
2414                PayloadSchemaType::Datetime => false,
2415            },
2416            PayloadFieldSchema::FieldParams(payload_schema_params) => match payload_schema_params {
2417                PayloadSchemaParams::Keyword(_) => true,
2418                PayloadSchemaParams::Integer(integer_index_params) => {
2419                    integer_index_params.lookup == Some(true)
2420                }
2421                PayloadSchemaParams::Uuid(_) => true,
2422                PayloadSchemaParams::Bool(_) => true,
2423                PayloadSchemaParams::Float(_) => false,
2424                PayloadSchemaParams::Geo(_) => false,
2425                PayloadSchemaParams::Text(_) => false,
2426                PayloadSchemaParams::Datetime(_) => false,
2427            },
2428        }
2429    }
2430
2431    pub fn enable_hnsw(&self) -> bool {
2432        match self {
2433            PayloadFieldSchema::FieldType(_) => true,
2434            PayloadFieldSchema::FieldParams(p) => p.enable_hnsw(),
2435        }
2436    }
2437}
2438
2439impl From<PayloadSchemaType> for PayloadFieldSchema {
2440    fn from(payload_schema_type: PayloadSchemaType) -> Self {
2441        PayloadFieldSchema::FieldType(payload_schema_type)
2442    }
2443}
2444
2445impl TryFrom<PayloadIndexInfo> for PayloadFieldSchema {
2446    type Error = String;
2447
2448    fn try_from(index_info: PayloadIndexInfo) -> Result<Self, Self::Error> {
2449        let PayloadIndexInfo {
2450            data_type,
2451            params,
2452            points: _,
2453        } = index_info;
2454
2455        match params {
2456            None => Ok(PayloadFieldSchema::FieldType(data_type)),
2457
2458            Some(params) if data_type == params.kind() => {
2459                Ok(PayloadFieldSchema::FieldParams(params))
2460            }
2461
2462            Some(params) => Err(format!(
2463                "payload field with type {data_type:?} has parameters of type {:?}",
2464                params.kind(),
2465            )),
2466        }
2467    }
2468}
2469
2470#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2471#[serde(untagged)]
2472pub enum ValueVariants {
2473    String(String),
2474    Integer(IntPayloadType),
2475    Bool(bool),
2476}
2477
2478impl ValueVariants {
2479    pub fn to_value(&self) -> Value {
2480        match self {
2481            ValueVariants::String(keyword) => Value::String(keyword.clone()),
2482            &ValueVariants::Integer(integer) => Value::Number(integer.into()),
2483            &ValueVariants::Bool(flag) => Value::Bool(flag),
2484        }
2485    }
2486}
2487
2488#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
2489#[serde(untagged)]
2490pub enum AnyVariants {
2491    Strings(IndexSet<String, FnvBuildHasher>),
2492    Integers(IndexSet<IntPayloadType, FnvBuildHasher>),
2493}
2494
2495impl Hash for AnyVariants {
2496    fn hash<H: Hasher>(&self, state: &mut H) {
2497        mem::discriminant(self).hash(state);
2498        match self {
2499            AnyVariants::Strings(index_set) => {
2500                for item in index_set {
2501                    item.hash(state);
2502                }
2503            }
2504            AnyVariants::Integers(index_set) => {
2505                for item in index_set {
2506                    item.hash(state);
2507                }
2508            }
2509        }
2510    }
2511}
2512
2513impl AnyVariants {
2514    pub fn len(&self) -> usize {
2515        match self {
2516            AnyVariants::Strings(index_set) => index_set.len(),
2517            AnyVariants::Integers(index_set) => index_set.len(),
2518        }
2519    }
2520
2521    pub fn is_empty(&self) -> bool {
2522        match self {
2523            AnyVariants::Strings(index_set) => index_set.is_empty(),
2524            AnyVariants::Integers(index_set) => index_set.is_empty(),
2525        }
2526    }
2527}
2528
2529/// Exact match of the given value
2530#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2531#[serde(rename_all = "snake_case")]
2532pub struct MatchValue {
2533    pub value: ValueVariants,
2534}
2535
2536/// Full-text match of the strings.
2537#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2538#[serde(rename_all = "snake_case")]
2539pub struct MatchText {
2540    pub text: String,
2541}
2542
2543/// Full-text match of at least one token of the string.
2544#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2545#[serde(rename_all = "snake_case")]
2546pub struct MatchTextAny {
2547    pub text_any: String,
2548}
2549
2550impl<S: Into<String>> From<S> for MatchText {
2551    fn from(text: S) -> Self {
2552        MatchText { text: text.into() }
2553    }
2554}
2555
2556/// Full-text phrase match of the string.
2557#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2558#[serde(rename_all = "snake_case")]
2559pub struct MatchPhrase {
2560    pub phrase: String,
2561}
2562
2563impl<S: Into<String>> From<S> for MatchPhrase {
2564    fn from(text: S) -> Self {
2565        MatchPhrase {
2566            phrase: text.into(),
2567        }
2568    }
2569}
2570
2571/// Exact match on any of the given values
2572#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2573#[serde(rename_all = "snake_case")]
2574pub struct MatchAny {
2575    pub any: AnyVariants,
2576}
2577
2578/// Should have at least one value not matching the any given values
2579#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2580#[serde(rename_all = "snake_case")]
2581pub struct MatchExcept {
2582    pub except: AnyVariants,
2583}
2584
2585/// Match filter request
2586#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
2587#[serde(untagged, rename_all = "snake_case")]
2588pub enum MatchInterface {
2589    Value(MatchValue),
2590    Text(MatchText),
2591    TextAny(MatchTextAny),
2592    Phrase(MatchPhrase),
2593    Any(MatchAny),
2594    Except(MatchExcept),
2595}
2596
2597/// Match filter request
2598#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2599#[serde(untagged, from = "MatchInterface")]
2600pub enum Match {
2601    Value(MatchValue),
2602    Text(MatchText),
2603    TextAny(MatchTextAny),
2604    Phrase(MatchPhrase),
2605    Any(MatchAny),
2606    Except(MatchExcept),
2607}
2608
2609impl Match {
2610    pub fn new_value(value: ValueVariants) -> Self {
2611        Self::Value(MatchValue { value })
2612    }
2613
2614    pub fn new_text(text: &str) -> Self {
2615        Self::Text(MatchText { text: text.into() })
2616    }
2617
2618    pub fn new_any(any: AnyVariants) -> Self {
2619        Self::Any(MatchAny { any })
2620    }
2621
2622    pub fn new_except(except: AnyVariants) -> Self {
2623        Self::Except(MatchExcept { except })
2624    }
2625}
2626
2627impl From<AnyVariants> for Match {
2628    fn from(any: AnyVariants) -> Self {
2629        Self::Any(MatchAny { any })
2630    }
2631}
2632
2633impl From<MatchInterface> for Match {
2634    fn from(value: MatchInterface) -> Self {
2635        match value {
2636            MatchInterface::Value(value) => Self::Value(MatchValue { value: value.value }),
2637            MatchInterface::Text(text) => Self::Text(MatchText { text: text.text }),
2638            MatchInterface::TextAny(text_any) => Self::TextAny(MatchTextAny {
2639                text_any: text_any.text_any,
2640            }),
2641            MatchInterface::Any(any) => Self::Any(MatchAny { any: any.any }),
2642            MatchInterface::Except(except) => Self::Except(MatchExcept {
2643                except: except.except,
2644            }),
2645            MatchInterface::Phrase(MatchPhrase { phrase }) => Self::Phrase(MatchPhrase { phrase }),
2646        }
2647    }
2648}
2649
2650impl From<bool> for Match {
2651    fn from(flag: bool) -> Self {
2652        Self::Value(MatchValue {
2653            value: ValueVariants::Bool(flag),
2654        })
2655    }
2656}
2657
2658impl From<String> for Match {
2659    fn from(keyword: String) -> Self {
2660        Self::Value(MatchValue {
2661            value: ValueVariants::String(keyword),
2662        })
2663    }
2664}
2665
2666impl From<EcoString> for Match {
2667    fn from(keyword: EcoString) -> Self {
2668        Self::Value(MatchValue {
2669            value: ValueVariants::String(keyword.into()),
2670        })
2671    }
2672}
2673
2674impl From<IntPayloadType> for Match {
2675    fn from(integer: IntPayloadType) -> Self {
2676        Self::Value(MatchValue {
2677            value: ValueVariants::Integer(integer),
2678        })
2679    }
2680}
2681
2682impl From<Vec<String>> for Match {
2683    fn from(keywords: Vec<String>) -> Self {
2684        let keywords: IndexSet<String, FnvBuildHasher> = keywords.into_iter().collect();
2685        Self::Any(MatchAny {
2686            any: AnyVariants::Strings(keywords),
2687        })
2688    }
2689}
2690
2691impl From<ValueVariants> for Match {
2692    fn from(value: ValueVariants) -> Self {
2693        Self::Value(MatchValue { value })
2694    }
2695}
2696
2697impl From<Vec<String>> for MatchExcept {
2698    fn from(keywords: Vec<String>) -> Self {
2699        let keywords: IndexSet<String, FnvBuildHasher> = keywords.into_iter().collect();
2700        MatchExcept {
2701            except: AnyVariants::Strings(keywords),
2702        }
2703    }
2704}
2705
2706impl From<Vec<IntPayloadType>> for Match {
2707    fn from(integers: Vec<IntPayloadType>) -> Self {
2708        let integers: IndexSet<_, FnvBuildHasher> = integers.into_iter().collect();
2709        Self::Any(MatchAny {
2710            any: AnyVariants::Integers(integers),
2711        })
2712    }
2713}
2714
2715impl From<Vec<IntPayloadType>> for MatchExcept {
2716    fn from(integers: Vec<IntPayloadType>) -> Self {
2717        let integers: IndexSet<_, FnvBuildHasher> = integers.into_iter().collect();
2718        MatchExcept {
2719            except: AnyVariants::Integers(integers),
2720        }
2721    }
2722}
2723
2724#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, JsonSchema)]
2725#[serde(untagged)]
2726pub enum RangeInterface {
2727    Float(Range<OrderedFloat<FloatPayloadType>>),
2728    DateTime(Range<DateTimePayloadType>),
2729}
2730
2731impl Hash for RangeInterface {
2732    fn hash<H: hash::Hasher>(&self, state: &mut H) {
2733        match self {
2734            RangeInterface::Float(range) => {
2735                let Range { lt, gt, gte, lte } = range;
2736                lt.hash(state);
2737                gt.hash(state);
2738                gte.hash(state);
2739                lte.hash(state);
2740            }
2741            RangeInterface::DateTime(range) => {
2742                let Range { lt, gt, gte, lte } = range;
2743                lt.hash(state);
2744                gt.hash(state);
2745                gte.hash(state);
2746                lte.hash(state);
2747            }
2748        }
2749    }
2750}
2751
2752#[derive(serde::Deserialize)]
2753#[serde(untagged)]
2754enum RangeInterfaceUntagged {
2755    Float(Range<OrderedFloatPayloadType>),
2756    DateTime(Range<DateTimePayloadType>),
2757}
2758
2759impl<'de> serde::Deserialize<'de> for RangeInterface {
2760    /// Parses range bounds, treating string bounds as RFC3339 datetimes for REST/JSON `datetime_range` filters.
2761    /// Preserves clear user-facing errors when datetime formats are invalid.
2762    /// Example accepted datetime bound: `2014-01-01T00:00:00Z`.
2763    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2764    where
2765        D: serde::Deserializer<'de>,
2766    {
2767        if !deserializer.is_human_readable() {
2768            return RangeInterfaceUntagged::deserialize(deserializer).map(|parsed| match parsed {
2769                RangeInterfaceUntagged::Float(r) => RangeInterface::Float(r),
2770                RangeInterfaceUntagged::DateTime(r) => RangeInterface::DateTime(r),
2771            });
2772        }
2773
2774        let value = serde_json::Value::deserialize(deserializer)?;
2775
2776        // If any range bound is a string -> treat as datetime range
2777        if let Some(obj) = value.as_object() {
2778            let keys = ["lt", "gt", "lte", "gte"];
2779            let has_string_bound = keys
2780                .iter()
2781                .any(|k| obj.get(*k).is_some_and(|v| v.is_string()));
2782
2783            if has_string_bound {
2784                return serde_json::from_value::<Range<DateTimePayloadType>>(value)
2785                    .map(RangeInterface::DateTime)
2786                    .map_err(serde::de::Error::custom);
2787            }
2788        }
2789
2790        // Fallback to existing untagged behavior
2791        let parsed = serde_json::from_value::<RangeInterfaceUntagged>(value)
2792            .map_err(serde::de::Error::custom)?;
2793
2794        Ok(match parsed {
2795            RangeInterfaceUntagged::Float(r) => RangeInterface::Float(r),
2796            RangeInterfaceUntagged::DateTime(r) => RangeInterface::DateTime(r),
2797        })
2798    }
2799}
2800
2801type OrderedFloatPayloadType = OrderedFloat<FloatPayloadType>;
2802
2803/// Range filter request
2804#[macro_rules_attribute::macro_rules_derive(crate::segment::common::macros::schemars_rename_generics)]
2805#[derive_args(< OrderedFloatPayloadType > => "Range", < DateTimePayloadType > => "DatetimeRange")]
2806#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
2807#[serde(rename_all = "snake_case")]
2808pub struct Range<T> {
2809    /// point.key < range.lt
2810    pub lt: Option<T>,
2811    /// point.key > range.gt
2812    pub gt: Option<T>,
2813    /// point.key >= range.gte
2814    pub gte: Option<T>,
2815    /// point.key <= range.lte
2816    pub lte: Option<T>,
2817}
2818
2819impl<T: Copy> Range<T> {
2820    /// Convert range to a range of another type
2821    pub fn map<U, F: Fn(T) -> U>(&self, f: F) -> Range<U> {
2822        let Self { lt, gt, gte, lte } = self;
2823        Range {
2824            lt: lt.map(&f),
2825            gt: gt.map(&f),
2826            gte: gte.map(&f),
2827            lte: lte.map(&f),
2828        }
2829    }
2830}
2831
2832impl<T: Copy + PartialOrd> Range<T> {
2833    pub fn check_range(&self, number: T) -> bool {
2834        let Self { lt, gt, gte, lte } = self;
2835        lt.is_none_or(|x| number < x)
2836            && gt.is_none_or(|x| number > x)
2837            && lte.is_none_or(|x| number <= x)
2838            && gte.is_none_or(|x| number >= x)
2839    }
2840}
2841
2842/// Values count filter request
2843#[derive(Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq, Hash)]
2844#[serde(rename_all = "snake_case")]
2845pub struct ValuesCount {
2846    /// point.key.length() < values_count.lt
2847    pub lt: Option<usize>,
2848    /// point.key.length() > values_count.gt
2849    pub gt: Option<usize>,
2850    /// point.key.length() >= values_count.gte
2851    pub gte: Option<usize>,
2852    /// point.key.length() <= values_count.lte
2853    pub lte: Option<usize>,
2854}
2855
2856impl ValuesCount {
2857    pub fn check_count(&self, count: usize) -> bool {
2858        let Self { lt, gt, gte, lte } = self;
2859        lt.is_none_or(|x| count < x)
2860            && gt.is_none_or(|x| count > x)
2861            && lte.is_none_or(|x| count <= x)
2862            && gte.is_none_or(|x| count >= x)
2863    }
2864
2865    pub fn check_count_from(&self, value: &Value) -> bool {
2866        let count = match value {
2867            Value::Null => 0,
2868            Value::Array(array) => array.len(),
2869            Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Object(_) => 1,
2870        };
2871
2872        self.check_count(count)
2873    }
2874}
2875
2876#[cfg(test)]
2877impl From<std::ops::Range<usize>> for ValuesCount {
2878    fn from(range: std::ops::Range<usize>) -> Self {
2879        Self {
2880            gte: Some(range.start),
2881            lt: Some(range.end),
2882            gt: None,
2883            lte: None,
2884        }
2885    }
2886}
2887
2888/// Geo filter request
2889///
2890/// Matches coordinates inside the rectangle, described by coordinates of lop-left and bottom-right edges
2891#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema)]
2892#[serde(rename_all = "snake_case")]
2893pub struct GeoBoundingBox {
2894    /// Coordinates of the top left point of the area rectangle
2895    pub top_left: GeoPoint,
2896    /// Coordinates of the bottom right point of the area rectangle
2897    pub bottom_right: GeoPoint,
2898}
2899
2900impl GeoBoundingBox {
2901    pub fn check_point(&self, point: &GeoPoint) -> bool {
2902        let longitude_check = if self.top_left.lon > self.bottom_right.lon {
2903            // Handle antimeridian crossing
2904            point.lon > self.top_left.lon || point.lon < self.bottom_right.lon
2905        } else {
2906            self.top_left.lon < point.lon && point.lon < self.bottom_right.lon
2907        };
2908
2909        let latitude_check = self.bottom_right.lat < point.lat && point.lat < self.top_left.lat;
2910
2911        longitude_check && latitude_check
2912    }
2913}
2914
2915/// Geo filter request
2916///
2917/// Matches coordinates inside the circle of `radius` and center with coordinates `center`
2918#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize, JsonSchema)]
2919#[serde(rename_all = "snake_case")]
2920pub struct GeoRadius {
2921    /// Coordinates of the top left point of the area rectangle
2922    pub center: GeoPoint,
2923    /// Radius of the area in meters
2924    pub radius: OrderedFloat<f64>,
2925}
2926
2927impl Hash for GeoRadius {
2928    fn hash<H: hash::Hasher>(&self, state: &mut H) {
2929        let GeoRadius { center, radius } = self;
2930        center.hash(state);
2931        // Hash f64 by converting to bits
2932        OrderedFloat(*radius).hash(state);
2933    }
2934}
2935
2936impl GeoRadius {
2937    pub fn check_point(&self, point: &GeoPoint) -> bool {
2938        let query_center = Point::from(self.center);
2939        Haversine.distance(query_center, Point::from(*point)) < self.radius.0
2940    }
2941}
2942
2943#[derive(Deserialize)]
2944pub struct GeoPolygonShadow {
2945    pub exterior: GeoLineString,
2946    pub interiors: Option<Vec<GeoLineString>>,
2947}
2948
2949pub struct PolygonWrapper {
2950    pub polygon: Polygon,
2951}
2952
2953impl PolygonWrapper {
2954    pub fn check_point(&self, point: &GeoPoint) -> bool {
2955        let point_new = Point::new(point.lon.0, point.lat.0);
2956        self.polygon.contains(&point_new)
2957    }
2958}
2959
2960/// Geo filter request
2961///
2962/// Matches coordinates inside the polygon, defined by `exterior` and `interiors`
2963#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2964#[serde(try_from = "GeoPolygonShadow", rename_all = "snake_case")]
2965pub struct GeoPolygon {
2966    /// The exterior line bounds the surface
2967    /// must consist of a minimum of 4 points, and the first and last points
2968    /// must be the same.
2969    pub exterior: GeoLineString,
2970    /// Interior lines (if present) bound holes within the surface
2971    /// each GeoLineString must consist of a minimum of 4 points, and the first
2972    /// and last points must be the same.
2973    pub interiors: Option<Vec<GeoLineString>>,
2974}
2975
2976impl GeoPolygon {
2977    pub fn validate_line_string(line: &GeoLineString) -> OperationResult<()> {
2978        if line.points.len() <= 3 {
2979            return Err(OperationError::validation_error(format!(
2980                "polygon invalid, the size must be at least 4, got {}",
2981                line.points.len()
2982            )));
2983        }
2984
2985        if let (Some(first), Some(last)) = (line.points.first(), line.points.last())
2986            && ((first.lat - last.lat).abs() > f64::EPSILON
2987                || (first.lon - last.lon).abs() > f64::EPSILON)
2988        {
2989            return Err(OperationError::validation_error(
2990                "polygon invalid, the first and the last points should be the same to form a closed line",
2991            ));
2992        }
2993
2994        Ok(())
2995    }
2996
2997    // convert GeoPolygon to Geo crate Polygon class for checking point intersection
2998    pub fn convert(&self) -> PolygonWrapper {
2999        let exterior_line: LineString = LineString(
3000            self.exterior
3001                .points
3002                .iter()
3003                .map(|p| Coord {
3004                    x: p.lon.0,
3005                    y: p.lat.0,
3006                })
3007                .collect(),
3008        );
3009
3010        // Convert the interior points to coordinates (if any)
3011        let interior_lines: Vec<LineString> = match &self.interiors {
3012            None => vec![],
3013            Some(interiors) => interiors
3014                .iter()
3015                .map(|interior_points| {
3016                    interior_points
3017                        .points
3018                        .iter()
3019                        .map(|p| Coord {
3020                            x: p.lon.0,
3021                            y: p.lat.0,
3022                        })
3023                        .collect()
3024                })
3025                .map(LineString)
3026                .collect(),
3027        };
3028        PolygonWrapper {
3029            polygon: Polygon::new(exterior_line, interior_lines),
3030        }
3031    }
3032}
3033
3034impl TryFrom<GeoPolygonShadow> for GeoPolygon {
3035    type Error = OperationError;
3036
3037    fn try_from(value: GeoPolygonShadow) -> OperationResult<Self> {
3038        let GeoPolygonShadow {
3039            exterior,
3040            interiors,
3041        } = value;
3042        Self::validate_line_string(&exterior)?;
3043
3044        if let Some(interiors) = &interiors {
3045            for interior in interiors {
3046                Self::validate_line_string(interior)?;
3047            }
3048        }
3049
3050        Ok(GeoPolygon {
3051            exterior,
3052            interiors,
3053        })
3054    }
3055}
3056
3057/// All possible payload filtering conditions
3058#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Hash)]
3059#[validate(schema(function = "validate_field_condition"))]
3060#[serde(rename_all = "snake_case")]
3061pub struct FieldCondition {
3062    /// Payload key
3063    pub key: PayloadKeyType,
3064    /// Check if point has field with a given value
3065    #[serde(skip_serializing_if = "Option::is_none")]
3066    pub r#match: Option<Match>,
3067    /// Check if points value lies in a given range
3068    #[serde(skip_serializing_if = "Option::is_none")]
3069    pub range: Option<RangeInterface>,
3070    /// Check if points geolocation lies in a given area
3071    #[serde(skip_serializing_if = "Option::is_none")]
3072    pub geo_bounding_box: Option<GeoBoundingBox>,
3073    /// Check if geo point is within a given radius
3074    #[serde(skip_serializing_if = "Option::is_none")]
3075    pub geo_radius: Option<GeoRadius>,
3076    /// Check if geo point is within a given polygon
3077    #[serde(skip_serializing_if = "Option::is_none")]
3078    pub geo_polygon: Option<GeoPolygon>,
3079    /// Check number of values of the field
3080    #[serde(skip_serializing_if = "Option::is_none")]
3081    pub values_count: Option<ValuesCount>,
3082    /// Check that the field is empty, alternative syntax for `is_empty: "field_name"`
3083    #[serde(skip_serializing_if = "Option::is_none")]
3084    pub is_empty: Option<bool>,
3085    /// Check that the field is null, alternative syntax for `is_null: "field_name"`
3086    #[serde(skip_serializing_if = "Option::is_none")]
3087    pub is_null: Option<bool>,
3088}
3089
3090impl FieldCondition {
3091    pub fn new_match(key: PayloadKeyType, r#match: Match) -> Self {
3092        Self {
3093            key,
3094            r#match: Some(r#match),
3095            range: None,
3096            geo_bounding_box: None,
3097            geo_radius: None,
3098            geo_polygon: None,
3099            values_count: None,
3100            is_empty: None,
3101            is_null: None,
3102        }
3103    }
3104
3105    pub fn new_range(key: PayloadKeyType, range: Range<OrderedFloat<FloatPayloadType>>) -> Self {
3106        Self {
3107            key,
3108            r#match: None,
3109            range: Some(RangeInterface::Float(range)),
3110            geo_bounding_box: None,
3111            geo_radius: None,
3112            geo_polygon: None,
3113            values_count: None,
3114            is_empty: None,
3115            is_null: None,
3116        }
3117    }
3118
3119    pub fn new_datetime_range(
3120        key: PayloadKeyType,
3121        datetime_range: Range<DateTimePayloadType>,
3122    ) -> Self {
3123        Self {
3124            key,
3125            r#match: None,
3126            range: Some(RangeInterface::DateTime(datetime_range)),
3127            geo_bounding_box: None,
3128            geo_radius: None,
3129            geo_polygon: None,
3130            values_count: None,
3131            is_empty: None,
3132            is_null: None,
3133        }
3134    }
3135
3136    pub fn new_geo_bounding_box(key: PayloadKeyType, geo_bounding_box: GeoBoundingBox) -> Self {
3137        Self {
3138            key,
3139            r#match: None,
3140            range: None,
3141            geo_bounding_box: Some(geo_bounding_box),
3142            geo_radius: None,
3143            geo_polygon: None,
3144            values_count: None,
3145            is_empty: None,
3146            is_null: None,
3147        }
3148    }
3149
3150    pub fn new_geo_radius(key: PayloadKeyType, geo_radius: GeoRadius) -> Self {
3151        Self {
3152            key,
3153            r#match: None,
3154            range: None,
3155            geo_bounding_box: None,
3156            geo_radius: Some(geo_radius),
3157            geo_polygon: None,
3158            values_count: None,
3159            is_empty: None,
3160            is_null: None,
3161        }
3162    }
3163
3164    pub fn new_geo_polygon(key: PayloadKeyType, geo_polygon: GeoPolygon) -> Self {
3165        Self {
3166            key,
3167            r#match: None,
3168            range: None,
3169            geo_bounding_box: None,
3170            geo_radius: None,
3171            geo_polygon: Some(geo_polygon),
3172            values_count: None,
3173            is_empty: None,
3174            is_null: None,
3175        }
3176    }
3177
3178    pub fn new_values_count(key: PayloadKeyType, values_count: ValuesCount) -> Self {
3179        Self {
3180            key,
3181            r#match: None,
3182            range: None,
3183            geo_bounding_box: None,
3184            geo_radius: None,
3185            geo_polygon: None,
3186            values_count: Some(values_count),
3187            is_empty: None,
3188            is_null: None,
3189        }
3190    }
3191
3192    pub fn new_is_empty(key: PayloadKeyType, is_empty: bool) -> Self {
3193        Self {
3194            key,
3195            r#match: None,
3196            range: None,
3197            geo_bounding_box: None,
3198            geo_radius: None,
3199            geo_polygon: None,
3200            values_count: None,
3201            is_empty: Some(is_empty),
3202            is_null: None,
3203        }
3204    }
3205
3206    pub fn new_is_null(key: PayloadKeyType, is_null: bool) -> Self {
3207        Self {
3208            key,
3209            r#match: None,
3210            range: None,
3211            geo_bounding_box: None,
3212            geo_radius: None,
3213            geo_polygon: None,
3214            values_count: None,
3215            is_empty: None,
3216            is_null: Some(is_null),
3217        }
3218    }
3219
3220    pub fn all_fields_none(&self) -> bool {
3221        matches!(
3222            self,
3223            FieldCondition {
3224                r#match: None,
3225                range: None,
3226                geo_bounding_box: None,
3227                geo_radius: None,
3228                geo_polygon: None,
3229                values_count: None,
3230                key: _,
3231                is_empty: None,
3232                is_null: None,
3233            }
3234        )
3235    }
3236
3237    fn input_size(&self) -> usize {
3238        if self.r#match.is_none() {
3239            return 0;
3240        }
3241
3242        match self.r#match.as_ref().unwrap() {
3243            Match::Any(match_any) => match_any.any.len(),
3244            Match::Except(match_except) => match_except.except.len(),
3245            Match::Value(_) => 0,
3246            Match::Text(_) => 0,
3247            Match::Phrase(_) => 0,
3248            Match::TextAny(_) => 0,
3249        }
3250    }
3251}
3252
3253pub fn validate_field_condition(field_condition: &FieldCondition) -> Result<(), ValidationError> {
3254    if field_condition.all_fields_none() {
3255        Err(ValidationError::new(
3256            "At least one field condition must be specified",
3257        ))
3258    } else {
3259        Ok(())
3260    }
3261}
3262
3263/// Payload field
3264#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3265pub struct PayloadField {
3266    /// Payload field name
3267    pub key: PayloadKeyType,
3268}
3269
3270/// Select points with empty payload for a specified field
3271#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3272pub struct IsEmptyCondition {
3273    pub is_empty: PayloadField,
3274}
3275
3276/// Select points with null payload for a specified field
3277#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3278pub struct IsNullCondition {
3279    pub is_null: PayloadField,
3280}
3281
3282impl From<JsonPath> for IsNullCondition {
3283    fn from(key: PayloadKeyType) -> Self {
3284        IsNullCondition {
3285            is_null: PayloadField { key },
3286        }
3287    }
3288}
3289
3290impl From<JsonPath> for IsEmptyCondition {
3291    fn from(key: PayloadKeyType) -> Self {
3292        IsEmptyCondition {
3293            is_empty: PayloadField { key },
3294        }
3295    }
3296}
3297
3298/// ID-based filtering condition
3299#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
3300pub struct HasIdCondition {
3301    #[schemars(schema_with = "HashSet::<PointIdType>::json_schema")]
3302    pub has_id: MaybeArc<AHashSet<PointIdType>>,
3303}
3304
3305impl Hash for HasIdCondition {
3306    fn hash<H: hash::Hasher>(&self, state: &mut H) {
3307        unordered_hash_unique(state, self.has_id.iter());
3308    }
3309}
3310
3311/// Filter points which have specific vector assigned
3312#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3313pub struct HasVectorCondition {
3314    pub has_vector: VectorNameBuf,
3315}
3316
3317impl From<VectorNameBuf> for HasVectorCondition {
3318    fn from(vector: VectorNameBuf) -> Self {
3319        HasVectorCondition { has_vector: vector }
3320    }
3321}
3322
3323/// Threshold determining when to use an `Arc` in `HasIdCondition` if the condition includes many points.
3324/// Since we're cloning filters quite a lot, using an Arc for larger conditions reduces risk of memory leaks
3325/// and potentially improves performance in some places.
3326const HAS_ID_CONDITION_ARC_THRESHOLD: usize = 1_000;
3327
3328impl From<AHashSet<PointIdType>> for HasIdCondition {
3329    fn from(has_id: AHashSet<PointIdType>) -> Self {
3330        if has_id.len() > HAS_ID_CONDITION_ARC_THRESHOLD {
3331            HasIdCondition {
3332                has_id: MaybeArc::arc(has_id),
3333            }
3334        } else {
3335            HasIdCondition {
3336                has_id: MaybeArc::no_arc(has_id),
3337            }
3338        }
3339    }
3340}
3341
3342impl FromIterator<PointIdType> for HasIdCondition {
3343    fn from_iter<T: IntoIterator<Item = PointIdType>>(iter: T) -> Self {
3344        let items: AHashSet<_> = iter.into_iter().collect();
3345        // Arc-Threshold applies here, since we're reusing the From implementation from AHashSet.
3346        Self::from(items)
3347    }
3348}
3349
3350/// Select points with payload for a specified nested field
3351#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Validate, Hash)]
3352pub struct Nested {
3353    pub key: PayloadKeyType,
3354    #[validate(nested)]
3355    pub filter: Filter,
3356}
3357
3358#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Validate, Hash)]
3359pub struct NestedCondition {
3360    #[validate(nested)]
3361    pub nested: Nested,
3362}
3363
3364/// Container to work around the untagged enum limitation for condition
3365impl NestedCondition {
3366    pub fn new(nested: Nested) -> Self {
3367        Self { nested }
3368    }
3369
3370    /// Get the raw key without any modifications
3371    pub fn raw_key(&self) -> &PayloadKeyType {
3372        &self.nested.key
3373    }
3374
3375    /// Nested is made to be used with arrays, so we add `[]` to the key if it is not present for convenience
3376    pub fn array_key(&self) -> PayloadKeyType {
3377        self.raw_key().array_key()
3378    }
3379
3380    pub fn filter(&self) -> &Filter {
3381        &self.nested.filter
3382    }
3383}
3384
3385#[derive(Clone, Debug, Serialize, JsonSchema, PartialEq, Eq, Hash)]
3386#[serde(untagged)]
3387#[serde(
3388    expecting = "Expected some form of condition, which can be a field condition (like {\"key\": ..., \"match\": ... }), or some other mentioned in the documentation: https://qdrant.tech/documentation/concepts/filtering/#filtering-conditions"
3389)]
3390#[allow(clippy::large_enum_variant)]
3391pub enum Condition {
3392    /// Check if field satisfies provided condition
3393    Field(FieldCondition),
3394    /// Check if payload field is empty: equals to empty array, or does not exists
3395    IsEmpty(IsEmptyCondition),
3396    /// Check if payload field equals `NULL`
3397    IsNull(IsNullCondition),
3398    /// Check if points id is in a given set
3399    HasId(HasIdCondition),
3400    /// Check if point has vector assigned
3401    HasVector(HasVectorCondition),
3402    /// Nested filters
3403    Nested(NestedCondition),
3404    /// Nested filter
3405    Filter(Filter),
3406
3407    #[serde(skip)]
3408    CustomIdChecker(CustomIdChecker),
3409}
3410
3411#[derive(Deserialize)]
3412#[serde(untagged)]
3413#[serde(
3414    expecting = "Expected some form of condition, which can be a field condition (like {\"key\": ..., \"match\": ... }), or some other mentioned in the documentation: https://qdrant.tech/documentation/concepts/filtering/#filtering-conditions"
3415)]
3416#[allow(clippy::large_enum_variant, dead_code)]
3417enum ConditionUntagged {
3418    Field(FieldCondition),
3419    IsEmpty(IsEmptyCondition),
3420    IsNull(IsNullCondition),
3421    HasId(HasIdCondition),
3422    HasVector(HasVectorCondition),
3423    Nested(NestedCondition),
3424    Filter(Filter),
3425
3426    #[serde(skip)]
3427    CustomIdChecker(CustomIdChecker),
3428}
3429
3430impl From<ConditionUntagged> for Condition {
3431    fn from(condition: ConditionUntagged) -> Self {
3432        match condition {
3433            ConditionUntagged::Field(condition) => Condition::Field(condition),
3434            ConditionUntagged::IsEmpty(condition) => Condition::IsEmpty(condition),
3435            ConditionUntagged::IsNull(condition) => Condition::IsNull(condition),
3436            ConditionUntagged::HasId(condition) => Condition::HasId(condition),
3437            ConditionUntagged::HasVector(condition) => Condition::HasVector(condition),
3438            ConditionUntagged::Nested(condition) => Condition::Nested(condition),
3439            ConditionUntagged::Filter(condition) => Condition::Filter(condition),
3440            ConditionUntagged::CustomIdChecker(condition) => Condition::CustomIdChecker(condition),
3441        }
3442    }
3443}
3444
3445impl<'de> serde::Deserialize<'de> for Condition {
3446    /// Deserializes Condition with special handling for FieldCondition to preserve
3447    /// readable RFC3339 datetime parse errors. Other variants use ConditionUntagged
3448    /// for compiler-level safety when new variants are added.
3449    /// Example accepted datetime value: `2014-01-01T00:00:00Z`.
3450    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3451    where
3452        D: serde::Deserializer<'de>,
3453    {
3454        // Buffer into serde_value::Value which, unlike serde_json::Value,
3455        // can represent byte arrays from non-human-readable formats (e.g. CBOR).
3456        // Note: we cannot rely on `deserializer.is_human_readable()` here because
3457        // serde's internal ContentDeserializer (used by flatten + untagged) always
3458        // reports `true` regardless of the original format.
3459        let value = serde_value::Value::deserialize(deserializer)?;
3460
3461        // Special case: FieldCondition first to surface datetime parse errors.
3462        // Untagged enum would swallow these errors with generic message.
3463        if let serde_value::Value::Map(obj) = &value
3464            && obj.contains_key(&serde_value::Value::String("key".into()))
3465        {
3466            return value
3467                .deserialize_into()
3468                .map(Condition::Field)
3469                .map_err(serde::de::Error::custom);
3470        }
3471
3472        // All other variants handled by ConditionUntagged (compiler-safe)
3473        value
3474            .deserialize_into::<ConditionUntagged>()
3475            .map(Condition::from)
3476            .map_err(serde::de::Error::custom)
3477    }
3478}
3479
3480impl Condition {
3481    pub fn new_custom(checker: Arc<dyn CustomIdCheckerCondition + Send + Sync + 'static>) -> Self {
3482        Condition::CustomIdChecker(CustomIdChecker(checker))
3483    }
3484}
3485
3486#[derive(Debug, Clone)]
3487pub struct CustomIdChecker(pub Arc<dyn CustomIdCheckerCondition + Send + Sync + 'static>);
3488
3489impl Hash for CustomIdChecker {
3490    fn hash<H: hash::Hasher>(&self, state: &mut H) {
3491        // We cannot hash the inner function
3492        // This means that two different CustomIdChecker conditions will have the same hash,
3493        // but that's acceptable since we cannot do better, and only expected to be used
3494        // for logging and profiling purposes.
3495        std::ptr::hash(Arc::as_ptr(&self.0), state);
3496    }
3497}
3498
3499impl PartialEq for CustomIdChecker {
3500    fn eq(&self, other: &Self) -> bool {
3501        // We cannot compare the inner function
3502        // This means that two different CustomIdChecker conditions will never be equal,
3503        // but that's acceptable since we cannot do better, and only expected to be used
3504        // for logging and profiling purposes.
3505        Arc::ptr_eq(&self.0, &other.0)
3506    }
3507}
3508
3509impl Eq for CustomIdChecker {}
3510
3511impl Condition {
3512    pub fn new_nested(key: JsonPath, filter: Filter) -> Self {
3513        Self::Nested(NestedCondition {
3514            nested: Nested { key, filter },
3515        })
3516    }
3517
3518    pub fn size_estimation(&self) -> usize {
3519        match self {
3520            Condition::Field(field_condition) => field_condition.input_size(),
3521            Condition::HasId(has_id_condition) => has_id_condition.has_id.len(),
3522            Condition::Filter(filter) => filter.max_condition_input_size(),
3523            Condition::Nested(nested) => nested.filter().max_condition_input_size(),
3524            Condition::IsEmpty(_)
3525            | Condition::IsNull(_)
3526            | Condition::HasVector(_)
3527            | Condition::CustomIdChecker(_) => 0,
3528        }
3529    }
3530
3531    pub fn sub_conditions_count(&self) -> usize {
3532        match self {
3533            Condition::Nested(nested_condition) => {
3534                nested_condition.filter().total_conditions_count()
3535            }
3536            Condition::Filter(filter) => filter.total_conditions_count(),
3537            Condition::Field(_)
3538            | Condition::IsEmpty(_)
3539            | Condition::IsNull(_)
3540            | Condition::CustomIdChecker(_)
3541            | Condition::HasId(_)
3542            | Condition::HasVector(_) => 1,
3543        }
3544    }
3545
3546    pub fn targeted_key(&self) -> Option<PayloadKeyType> {
3547        match self {
3548            Condition::Field(field_condition) => Some(field_condition.key.clone()),
3549            Condition::IsEmpty(is_empty_condition) => Some(is_empty_condition.is_empty.key.clone()),
3550            Condition::IsNull(is_null_condition) => Some(is_null_condition.is_null.key.clone()),
3551            Condition::Nested(nested_condition) => Some(nested_condition.array_key()),
3552            Condition::Filter(filter) => filter.iter_conditions().find_map(|c| c.targeted_key()),
3553            Condition::HasId(_) | Condition::HasVector(_) | Condition::CustomIdChecker(_) => None,
3554        }
3555    }
3556}
3557
3558// The validator crate does not support deriving for enums.
3559impl Validate for Condition {
3560    fn validate(&self) -> Result<(), ValidationErrors> {
3561        match self {
3562            Condition::HasId(_)
3563            | Condition::IsEmpty(_)
3564            | Condition::IsNull(_)
3565            | Condition::HasVector(_) => Ok(()),
3566            Condition::Field(field_condition) => field_condition.validate(),
3567            Condition::Nested(nested_condition) => nested_condition.validate(),
3568            Condition::Filter(filter) => filter.validate(),
3569            Condition::CustomIdChecker(_) => Ok(()),
3570        }
3571    }
3572}
3573
3574pub trait CustomIdCheckerCondition: fmt::Debug {
3575    fn estimate_cardinality(&self, points: usize) -> CardinalityEstimation;
3576    fn check(&self, point_id: ExtendedPointId) -> bool;
3577}
3578
3579/// Options for specifying which payload to include or not
3580#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Hash)]
3581#[serde(untagged, rename_all = "snake_case")]
3582#[serde(
3583    expecting = "Expected a boolean, an array of strings, or an object with an include/exclude field"
3584)]
3585pub enum WithPayloadInterface {
3586    /// If `true` - return all payload,
3587    /// If `false` - do not return payload
3588    Bool(bool),
3589    /// Specify which fields to return
3590    Fields(Vec<JsonPath>),
3591    /// Specify included or excluded fields
3592    Selector(PayloadSelector),
3593}
3594
3595impl From<bool> for WithPayloadInterface {
3596    fn from(b: bool) -> Self {
3597        WithPayloadInterface::Bool(b)
3598    }
3599}
3600
3601impl Default for WithPayloadInterface {
3602    fn default() -> Self {
3603        WithPayloadInterface::Bool(false)
3604    }
3605}
3606
3607/// Options for specifying which vector to include
3608#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3609#[serde(untagged, rename_all = "snake_case")]
3610#[serde(expecting = "Expected a boolean, or an array of strings")]
3611pub enum WithVector {
3612    /// If `true` - return all vector,
3613    /// If `false` - do not return vector
3614    Bool(bool),
3615    /// Specify which vector to return
3616    Selector(Vec<VectorNameBuf>),
3617}
3618
3619impl WithVector {
3620    pub fn is_enabled(&self) -> bool {
3621        match self {
3622            WithVector::Bool(b) => *b,
3623            WithVector::Selector(_) => true,
3624        }
3625    }
3626
3627    /// Merges two `WithVector` options, additively.
3628    pub fn merge(&self, other: &WithVector) -> WithVector {
3629        match (self, other) {
3630            // if any is true, then true
3631            (WithVector::Bool(true), _) => WithVector::Bool(true),
3632            (_, WithVector::Bool(true)) => WithVector::Bool(true),
3633
3634            // if both are false, then false
3635            (WithVector::Bool(false), WithVector::Bool(false)) => WithVector::Bool(false),
3636
3637            // merge selectors
3638            (WithVector::Selector(s1), WithVector::Selector(s2)) => {
3639                WithVector::Selector(s1.iter().chain(s2).unique().cloned().collect())
3640            }
3641
3642            // use selector from the other option
3643            (WithVector::Bool(false), WithVector::Selector(s)) => WithVector::Selector(s.clone()),
3644            (WithVector::Selector(s), WithVector::Bool(false)) => WithVector::Selector(s.clone()),
3645        }
3646    }
3647}
3648
3649impl From<bool> for WithVector {
3650    fn from(b: bool) -> Self {
3651        WithVector::Bool(b)
3652    }
3653}
3654
3655impl From<VectorNameBuf> for WithVector {
3656    fn from(name: VectorNameBuf) -> Self {
3657        WithVector::Selector(vec![name])
3658    }
3659}
3660
3661impl Default for WithVector {
3662    fn default() -> Self {
3663        WithVector::Bool(false)
3664    }
3665}
3666
3667impl WithPayloadInterface {
3668    pub fn is_required(&self) -> bool {
3669        match self {
3670            WithPayloadInterface::Bool(b) => *b,
3671            WithPayloadInterface::Fields(_) | WithPayloadInterface::Selector(_) => true,
3672        }
3673    }
3674}
3675
3676impl From<bool> for WithPayload {
3677    fn from(x: bool) -> Self {
3678        WithPayload {
3679            enable: x,
3680            payload_selector: None,
3681        }
3682    }
3683}
3684
3685impl From<WithPayloadInterface> for WithPayload {
3686    fn from(interface: WithPayloadInterface) -> Self {
3687        match interface {
3688            WithPayloadInterface::Bool(enable) => WithPayload {
3689                enable,
3690                payload_selector: None,
3691            },
3692            WithPayloadInterface::Fields(fields) => WithPayload {
3693                enable: true,
3694                payload_selector: Some(PayloadSelector::new_include(fields)),
3695            },
3696            WithPayloadInterface::Selector(selector) => WithPayload {
3697                enable: true,
3698                payload_selector: Some(selector),
3699            },
3700        }
3701    }
3702}
3703
3704impl From<&WithPayloadInterface> for WithPayload {
3705    fn from(interface: &WithPayloadInterface) -> Self {
3706        WithPayload::from(interface.clone())
3707    }
3708}
3709
3710#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3711#[serde(deny_unknown_fields, rename_all = "snake_case")]
3712pub struct PayloadSelectorInclude {
3713    /// Only include this payload keys
3714    pub include: Vec<PayloadKeyType>,
3715}
3716
3717impl PayloadSelectorInclude {
3718    pub fn new(include: Vec<PayloadKeyType>) -> Self {
3719        Self { include }
3720    }
3721}
3722
3723#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3724#[serde(deny_unknown_fields, rename_all = "snake_case")]
3725pub struct PayloadSelectorExclude {
3726    /// Exclude this fields from returning payload
3727    pub exclude: Vec<PayloadKeyType>,
3728}
3729
3730impl PayloadSelectorExclude {
3731    pub fn new(exclude: Vec<PayloadKeyType>) -> Self {
3732        Self { exclude }
3733    }
3734}
3735
3736/// Specifies how to treat payload selector
3737#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3738#[serde(untagged, rename_all = "snake_case")]
3739pub enum PayloadSelector {
3740    /// Include only this fields into response payload
3741    Include(PayloadSelectorInclude),
3742    /// Exclude this fields from result payload. Keep all other fields.
3743    Exclude(PayloadSelectorExclude),
3744}
3745
3746impl From<PayloadSelectorExclude> for WithPayloadInterface {
3747    fn from(selector: PayloadSelectorExclude) -> Self {
3748        WithPayloadInterface::Selector(PayloadSelector::Exclude(selector))
3749    }
3750}
3751
3752impl From<PayloadSelectorInclude> for WithPayloadInterface {
3753    fn from(selector: PayloadSelectorInclude) -> Self {
3754        WithPayloadInterface::Selector(PayloadSelector::Include(selector))
3755    }
3756}
3757
3758impl PayloadSelector {
3759    pub fn new_include(vecs_payload_key_type: Vec<PayloadKeyType>) -> Self {
3760        PayloadSelector::Include(PayloadSelectorInclude {
3761            include: vecs_payload_key_type,
3762        })
3763    }
3764
3765    pub fn new_exclude(vecs_payload_key_type: Vec<PayloadKeyType>) -> Self {
3766        PayloadSelector::Exclude(PayloadSelectorExclude {
3767            exclude: vecs_payload_key_type,
3768        })
3769    }
3770
3771    /// Process payload selector
3772    pub fn process(&self, x: Payload) -> Payload {
3773        match self {
3774            PayloadSelector::Include(selector) => JsonPath::value_filter(&x.0, |key, _| {
3775                selector
3776                    .include
3777                    .iter()
3778                    .any(|pattern| pattern.check_include_pattern(key))
3779            })
3780            .into(),
3781            PayloadSelector::Exclude(selector) => JsonPath::value_filter(&x.0, |key, _| {
3782                selector
3783                    .exclude
3784                    .iter()
3785                    .all(|pattern| !pattern.check_exclude_pattern(key))
3786            })
3787            .into(),
3788        }
3789    }
3790}
3791
3792#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Default, PartialEq, Eq)]
3793#[serde(deny_unknown_fields, rename_all = "snake_case")]
3794pub struct WithPayload {
3795    /// Enable return payloads or not
3796    pub enable: bool,
3797    /// Filter include and exclude payloads
3798    pub payload_selector: Option<PayloadSelector>,
3799}
3800
3801#[derive(
3802    Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Default, Hash,
3803)]
3804#[serde(rename_all = "snake_case")]
3805pub struct MinShould {
3806    #[validate(nested)]
3807    pub conditions: Vec<Condition>,
3808    pub min_count: usize,
3809}
3810
3811#[derive(
3812    Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Default, Hash,
3813)]
3814#[serde(deny_unknown_fields, rename_all = "snake_case")]
3815pub struct Filter {
3816    /// At least one of those conditions should match
3817    #[validate(nested)]
3818    #[serde(
3819        default,
3820        with = "MaybeOneOrMany",
3821        skip_serializing_if = "Option::is_none"
3822    )]
3823    #[schemars(with = "MaybeOneOrMany<Condition>")]
3824    pub should: Option<Vec<Condition>>,
3825    /// At least minimum amount of given conditions should match
3826    #[validate(nested)]
3827    #[serde(skip_serializing_if = "Option::is_none")]
3828    pub min_should: Option<MinShould>,
3829    /// All conditions must match
3830    #[validate(nested)]
3831    #[serde(
3832        default,
3833        with = "MaybeOneOrMany",
3834        skip_serializing_if = "Option::is_none"
3835    )]
3836    #[schemars(with = "MaybeOneOrMany<Condition>")]
3837    pub must: Option<Vec<Condition>>,
3838    /// All conditions must NOT match
3839    #[validate(nested)]
3840    #[serde(
3841        default,
3842        with = "MaybeOneOrMany",
3843        skip_serializing_if = "Option::is_none"
3844    )]
3845    #[schemars(with = "MaybeOneOrMany<Condition>")]
3846    pub must_not: Option<Vec<Condition>>,
3847}
3848
3849impl Filter {
3850    pub fn new() -> Self {
3851        Filter {
3852            should: None,
3853            min_should: None,
3854            must: None,
3855            must_not: None,
3856        }
3857    }
3858
3859    pub fn new_should(condition: Condition) -> Self {
3860        Filter {
3861            should: Some(vec![condition]),
3862            min_should: None,
3863            must: None,
3864            must_not: None,
3865        }
3866    }
3867
3868    pub fn new_min_should(min_should: MinShould) -> Self {
3869        Filter {
3870            should: None,
3871            min_should: Some(min_should),
3872            must: None,
3873            must_not: None,
3874        }
3875    }
3876
3877    pub fn new_must(condition: Condition) -> Self {
3878        Filter {
3879            should: None,
3880            min_should: None,
3881            must: Some(vec![condition]),
3882            must_not: None,
3883        }
3884    }
3885
3886    pub fn new_must_not(condition: Condition) -> Self {
3887        Filter {
3888            should: None,
3889            min_should: None,
3890            must: None,
3891            must_not: Some(vec![condition]),
3892        }
3893    }
3894
3895    /// Create an extended filtering condition, which would also include filter by given list of IDs.
3896    pub fn with_point_ids(self, ids: impl IntoIterator<Item = PointIdType>) -> Filter {
3897        let has_id_condition: HasIdCondition = ids.into_iter().collect();
3898
3899        let Filter {
3900            should,
3901            min_should,
3902            must,
3903            must_not,
3904        } = self;
3905
3906        let new_must = match must {
3907            Some(mut must) => {
3908                must.push(Condition::HasId(has_id_condition));
3909                Some(must)
3910            }
3911            None => Some(vec![Condition::HasId(has_id_condition)]),
3912        };
3913
3914        Filter {
3915            should,
3916            min_should,
3917            must: new_must,
3918            must_not,
3919        }
3920    }
3921
3922    pub fn merge(&self, other: &Filter) -> Filter {
3923        self.clone().merge_owned(other.clone())
3924    }
3925
3926    pub fn merge_owned(self, other: Filter) -> Filter {
3927        let merge_component = |this, other| -> Option<Vec<Condition>> {
3928            match (this, other) {
3929                (None, None) => None,
3930                (Some(this), None) => Some(this),
3931                (None, Some(other)) => Some(other),
3932                (Some(mut this), Some(mut other)) => {
3933                    this.append(&mut other);
3934                    Some(this)
3935                }
3936            }
3937        };
3938        Filter {
3939            should: merge_component(self.should, other.should),
3940            min_should: {
3941                match (self.min_should, other.min_should) {
3942                    (None, None) => None,
3943                    (Some(this), None) => Some(this),
3944                    (None, Some(other)) => Some(other),
3945                    (Some(mut this), Some(mut other)) => {
3946                        this.conditions.append(&mut other.conditions);
3947
3948                        // The union of conditions should be able to have at least the bigger of the two min_counts
3949                        this.min_count = this.min_count.max(other.min_count);
3950
3951                        Some(this)
3952                    }
3953                }
3954            },
3955            must: merge_component(self.must, other.must),
3956            must_not: merge_component(self.must_not, other.must_not),
3957        }
3958    }
3959
3960    pub fn merge_opts(this: Option<Self>, other: Option<Self>) -> Option<Self> {
3961        match (this, other) {
3962            (None, None) => None,
3963            (Some(this), None) => Some(this),
3964            (None, Some(other)) => Some(other),
3965            (Some(this), Some(other)) => Some(this.merge_owned(other)),
3966        }
3967    }
3968
3969    pub fn iter_conditions(&self) -> impl Iterator<Item = &Condition> {
3970        self.must
3971            .iter()
3972            .flatten()
3973            .chain(self.must_not.iter().flatten())
3974            .chain(self.should.iter().flatten())
3975            .chain(self.min_should.iter().flat_map(|i| &i.conditions))
3976    }
3977
3978    /// Returns the total amount of conditions of the filter, including all nested filter.
3979    pub fn total_conditions_count(&self) -> usize {
3980        fn count_all_conditions(field: Option<&Vec<Condition>>) -> usize {
3981            field
3982                .map(|i| i.iter().map(|j| j.sub_conditions_count()).sum::<usize>())
3983                .unwrap_or(0)
3984        }
3985
3986        count_all_conditions(self.should.as_ref())
3987            + count_all_conditions(self.min_should.as_ref().map(|i| &i.conditions))
3988            + count_all_conditions(self.must.as_ref())
3989            + count_all_conditions(self.must_not.as_ref())
3990    }
3991
3992    /// Returns the size of the largest condition.
3993    pub fn max_condition_input_size(&self) -> usize {
3994        self.iter_conditions()
3995            .map(|i| i.size_estimation())
3996            .max()
3997            .unwrap_or(0)
3998    }
3999}
4000
4001#[derive(Debug, Clone, Copy, Eq, PartialEq)]
4002pub enum SnapshotFormat {
4003    /// Created by Qdrant `<0.11.0`.
4004    ///
4005    /// The collection snapshot contains nested tar archives for segments.
4006    /// Segment tar archives contain a plain copy of the segment directory.
4007    ///
4008    /// ```plaintext
4009    /// ./0/segments/
4010    /// ├── 0b31e274-dc65-40e4-8493-67ebed4bcf10.tar
4011    /// │   ├── segment.json
4012    /// │   ├── CURRENT
4013    /// │   ├── 000009.sst
4014    /// │   ├── 000010.sst
4015    /// │   └── …
4016    /// ├── 1d6c96ec-7965-491a-9c45-362d55361e9b.tar
4017    /// └── …
4018    /// ```
4019    Ancient,
4020    /// Qdrant `>=0.11.0` `<=1.13` (and maybe even later).
4021    ///
4022    /// The collection snapshot contains nested tar archives for segments.
4023    /// Distinguished by a single top-level directory `snapshot` in each segment
4024    /// tar archive. RocksDB data stored as backups and requires unpacking
4025    /// procedure.
4026    ///
4027    /// ```plaintext
4028    /// ./0/segments/
4029    /// ├── 0b31e274-dc65-40e4-8493-67ebed4bcf10.tar
4030    /// │   └── snapshot/                               # single top-level dir
4031    /// │       ├── db_backup/                          # rockdb backup
4032    /// │       │   ├── meta/
4033    /// │       │   ├── private/
4034    /// │       │   └── shared_checksum/
4035    /// │       ├── payload_index_db_backup             # rocksdb backup
4036    /// │       │   ├── meta/
4037    /// │       │   ├── private/
4038    /// │       │   └── shared_checksum/
4039    /// │       └── files/                              # regular files
4040    /// │           ├── segment.json
4041    /// │           └── …
4042    /// ├── 1d6c96ec-7965-491a-9c45-362d55361e9b.tar
4043    /// └── …
4044    /// ```
4045    Regular,
4046    /// New experimental format.
4047    ///
4048    /// ```plaintext
4049    /// ./0/segments/
4050    /// ├── 0b31e274-dc65-40e4-8493-67ebed4bcf10/
4051    /// │   ├── db_backup/                              # rockdb backup
4052    /// │   │   ├── meta/
4053    /// │   │   ├── private/
4054    /// │   │   └── shared_checksum/
4055    /// │   ├── payload_index_db_backup                 # rocksdb backup
4056    /// │   │   ├── meta/
4057    /// │   │   ├── private/
4058    /// │   │   └── shared_checksum/
4059    /// │   └── files/                                  # regular files
4060    /// │       ├── segment.json
4061    /// │       └── …
4062    /// ├── 1d6c96ec-7965-491a-9c45-362d55361e9b/
4063    /// └── …
4064    /// ```
4065    Streamable,
4066}
4067
4068#[cfg(test)]
4069pub(crate) mod test_utils {
4070    use super::{GeoLineString, GeoPoint, GeoPolygon};
4071
4072    pub fn build_polygon(exterior_points: Vec<(f64, f64)>) -> GeoPolygon {
4073        let exterior_line = GeoLineString {
4074            points: exterior_points
4075                .into_iter()
4076                .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4077                .collect(),
4078        };
4079
4080        GeoPolygon {
4081            exterior: exterior_line,
4082            interiors: None,
4083        }
4084    }
4085
4086    pub fn build_polygon_with_interiors(
4087        exterior_points: Vec<(f64, f64)>,
4088        interiors_points: Vec<Vec<(f64, f64)>>,
4089    ) -> GeoPolygon {
4090        let exterior_line = GeoLineString {
4091            points: exterior_points
4092                .into_iter()
4093                .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4094                .collect(),
4095        };
4096
4097        let interior_lines = Some(
4098            interiors_points
4099                .into_iter()
4100                .map(|points| GeoLineString {
4101                    points: points
4102                        .into_iter()
4103                        .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4104                        .collect(),
4105                })
4106                .collect(),
4107        );
4108
4109        GeoPolygon {
4110            exterior: exterior_line,
4111            interiors: interior_lines,
4112        }
4113    }
4114}
4115
4116#[cfg(test)]
4117mod tests {
4118    #![expect(clippy::wildcard_enum_match_arm, reason = "test code")]
4119
4120    use itertools::Itertools;
4121    use rstest::rstest;
4122    use serde_json;
4123
4124    use super::test_utils::build_polygon_with_interiors;
4125    use super::*;
4126
4127    #[test]
4128    #[ignore]
4129    fn test_rmp_vs_cbor_deserialize() {
4130        let payload = payload_json! {"payload_key": "payload_value"};
4131        let raw = rmp_serde::to_vec(&payload).unwrap();
4132        let de_record: Payload = serde_cbor::from_slice(&raw).unwrap();
4133        eprintln!("payload = {payload:#?}");
4134        eprintln!("de_record = {de_record:#?}");
4135    }
4136
4137    #[rstest]
4138    #[case::rfc_3339("2020-03-01T00:00:00Z")]
4139    #[case::rfc_3339_custom_tz("2020-03-01T00:00:00-09:00")]
4140    #[case::rfc_3339_custom_tz_no_colon("2020-03-01 00:00:00-0900")]
4141    #[case::rfc_3339_custom_tz_no_colon_and_t("2020-03-01T00:00:00-0900")]
4142    #[case::rfc_3339_custom_tz_no_minutes("2020-03-01 00:00:00-09")]
4143    #[case::rfc_3339_and_decimals("2020-03-01T00:00:00.123456Z")]
4144    #[case::without_z("2020-03-01T00:00:00")]
4145    #[case::without_z_and_decimals("2020-03-01T00:00:00.12")]
4146    #[case::space_sep_without_z("2020-03-01 00:00:00")]
4147    #[case::space_sep_without_z_and_decimals("2020-03-01 00:00:00.123456")]
4148    #[case::t_sep_without_seconds("2020-03-01T00:00")]
4149    #[case::space_sep_without_seconds("2020-03-01 00:00")]
4150    #[case::date_only("2020-03-01")]
4151    fn test_datetime_deserialization(#[case] datetime: &str) {
4152        let datetime = DateTimePayloadType::from_str(datetime).unwrap();
4153        let serialized = serde_json::to_string(&datetime).unwrap();
4154        let deserialized: DateTimePayloadType = serde_json::from_str(&serialized).unwrap();
4155        assert_eq!(datetime, deserialized);
4156    }
4157
4158    #[test]
4159    fn test_datetime_deserialization_equivalency() {
4160        let datetime_str = "2020-03-01T01:02:03.123456Z";
4161        let datetime_str_no_z = "2020-03-01T01:02:03.123456";
4162        let datetime = DateTimePayloadType::from_str(datetime_str).unwrap();
4163        let datetime_no_z = DateTimePayloadType::from_str(datetime_str_no_z).unwrap();
4164
4165        // Having or not the Z at the end of the string both mean UTC time
4166        assert_eq!(datetime.timestamp(), datetime_no_z.timestamp());
4167    }
4168
4169    #[test]
4170    fn test_invalid_datetime_range_returns_clear_rfc3339_error() {
4171        let json = r#"{
4172            "key": "created_at",
4173            "range": {
4174                "gte": "2014-01-01T00:00:00BAD"
4175            }
4176        }"#;
4177
4178        let err = serde_json::from_str::<Condition>(json)
4179            .unwrap_err()
4180            .to_string();
4181
4182        assert!(err.contains("RFC3339"), "err was: {err}");
4183        assert!(err.contains("2014-01-01T00:00:00BAD"), "err was: {err}");
4184        assert!(err.contains("Example"), "err was: {err}");
4185    }
4186
4187    /// Regression test: DateTimePayloadType binary serialization roundtrip.
4188    /// Ensures DateTimePayloadType parses binary-encoded RFC3339 strings.
4189    #[test]
4190    fn test_datetime_payload_type_binary_roundtrip() {
4191        let original = DateTimePayloadType::from_str("2024-06-15T12:30:45Z").unwrap();
4192
4193        // rmp-serde uses non-human-readable format
4194        let binary = rmp_serde::to_vec(&original).expect("serialize");
4195        let restored: DateTimePayloadType = rmp_serde::from_slice(&binary).expect("deserialize");
4196
4197        assert_eq!(original, restored);
4198    }
4199
4200    /// Regression test: RangeInterface with datetime binary roundtrip.
4201    /// Ensures the RangeInterface datetime deserialization works in binary.
4202    #[test]
4203    fn test_range_interface_datetime_binary_roundtrip() {
4204        let dt_gte = DateTimePayloadType::from_str("2024-01-01T00:00:00Z").unwrap();
4205        let dt_lte = DateTimePayloadType::from_str("2024-12-31T23:59:59Z").unwrap();
4206
4207        let range = RangeInterface::DateTime(Range {
4208            lt: None,
4209            gt: None,
4210            gte: Some(dt_gte),
4211            lte: Some(dt_lte),
4212        });
4213
4214        // rmp-serde uses non-human-readable format
4215        let binary = rmp_serde::to_vec(&range).expect("serialize");
4216        let restored: RangeInterface = rmp_serde::from_slice(&binary).expect("deserialize");
4217
4218        assert_eq!(range, restored);
4219    }
4220
4221    /// Regression test: Non-FieldCondition JSON deserialization uses ConditionUntagged fallback.
4222    /// Ensures compiler-safe handling of other Condition variants.
4223    #[test]
4224    fn test_condition_json_fallback_to_untagged() {
4225        // IsEmptyCondition (no "key" field at top level, uses "is_empty" instead)
4226        let is_empty_json = r#"{"is_empty": {"key": "optional_field"}}"#;
4227        let condition: Condition = serde_json::from_str(is_empty_json).unwrap();
4228        assert!(matches!(condition, Condition::IsEmpty(_)));
4229
4230        // HasIdCondition
4231        let has_id_json = r#"{"has_id": [1, 2, 3]}"#;
4232        let condition: Condition = serde_json::from_str(has_id_json).unwrap();
4233        assert!(matches!(condition, Condition::HasId(_)));
4234
4235        // Nested Filter
4236        let nested_json = r#"{"nested": {"key": "items", "filter": {"must": []}}}"#;
4237        let condition: Condition = serde_json::from_str(nested_json).unwrap();
4238        assert!(matches!(condition, Condition::Nested(_)));
4239    }
4240
4241    #[test]
4242    fn test_datetime_wrapper_transcoding() {
4243        let expected = DateTimeWrapper(chrono::Utc::now());
4244        let transcoded = DateTimeWrapper::from_str(&expected.to_string()).unwrap();
4245        assert_eq!(expected, transcoded);
4246    }
4247
4248    #[test]
4249    fn test_timezone_ordering() {
4250        let datetimes = [
4251            "2000-06-08 00:18:53+0900",
4252            "2000-06-07 07:25:34-1100",
4253            "2000-07-10T00:18:53+0100",
4254            "2000-07-11 00:25:34-01:00",
4255            "2000-07-11 00:25:35-01",
4256        ];
4257
4258        let sorted_datetimes: Vec<_> = datetimes
4259            .iter()
4260            .enumerate()
4261            .map(|(i, s)| (i, DateTimePayloadType::from_str(s).unwrap()))
4262            .sorted_by_key(|(_, dt)| dt.timestamp())
4263            .collect();
4264
4265        sorted_datetimes
4266            .array_windows()
4267            .for_each(|[(i1, dt1), (i2, dt2)]| {
4268                assert!(
4269                    i1 < i2,
4270                    "i1: {}, dt1: {}, ts1: {}\ni2: {}, dt2: {}, ts2: {}",
4271                    i1,
4272                    dt1.0,
4273                    dt1.timestamp(),
4274                    i2,
4275                    dt2.0,
4276                    dt2.timestamp()
4277                );
4278            });
4279    }
4280
4281    #[test]
4282    fn test_geo_radius_check_point() {
4283        let radius = GeoRadius {
4284            center: GeoPoint::new_unchecked(0.0, 0.0),
4285            radius: OrderedFloat(80000.0),
4286        };
4287
4288        let inside_result = radius.check_point(&GeoPoint::new_unchecked(0.5, 0.5));
4289        assert!(inside_result);
4290
4291        let outside_result = radius.check_point(&GeoPoint::new_unchecked(1.5, 1.5));
4292        assert!(!outside_result);
4293    }
4294
4295    #[test]
4296    fn test_geo_boundingbox_check_point() {
4297        let bounding_box = GeoBoundingBox {
4298            top_left: GeoPoint::new_unchecked(-1.0, 1.0),
4299            bottom_right: GeoPoint::new_unchecked(1.0, -1.0),
4300        };
4301
4302        // haversine distance between (0, 0) and (0.5, 0.5) is 78626.29627999048
4303        let inside_result = bounding_box.check_point(&GeoPoint::new_unchecked(-0.5, 0.5));
4304        assert!(inside_result);
4305
4306        // haversine distance between (0, 0) and (0.5, 0.5) is 235866.91169814655
4307        let outside_result = bounding_box.check_point(&GeoPoint::new_unchecked(1.5, 1.5));
4308        assert!(!outside_result);
4309    }
4310
4311    #[test]
4312    fn test_geo_boundingbox_antimeridian_check_point() {
4313        // Use the bounding box for USA: (74.071028, 167), (18.7763, -66.885417)
4314        let bounding_box = GeoBoundingBox {
4315            top_left: GeoPoint::new_unchecked(167.0, 74.071028),
4316            bottom_right: GeoPoint::new_unchecked(-66.885417, 18.7763),
4317        };
4318
4319        // Test NYC, which is inside the bounding box
4320        let inside_result =
4321            bounding_box.check_point(&GeoPoint::new_unchecked(-73.991516, 40.75798));
4322        assert!(inside_result);
4323
4324        // Test Berlin, which is outside the bounding box
4325        let outside_result = bounding_box.check_point(&GeoPoint::new_unchecked(13.41053, 52.52437));
4326        assert!(!outside_result);
4327    }
4328
4329    #[test]
4330    fn test_geo_polygon_check_point() {
4331        let test_cases = [
4332            // Create a GeoPolygon with a square shape
4333            (
4334                // Exterior
4335                vec![
4336                    (-1.0, -1.0),
4337                    (1.0, -1.0),
4338                    (1.0, 1.0),
4339                    (-1.0, 1.0),
4340                    (-1.0, -1.0),
4341                ],
4342                // Interiors
4343                vec![vec![]],
4344                // Expected results
4345                vec![((0.5, 0.5), true), ((1.5, 1.5), false), ((1.0, 0.0), false)],
4346            ),
4347            // Create a GeoPolygon as a `twisted square`
4348            (
4349                // Exterior
4350                vec![
4351                    (-1.0, -1.0),
4352                    (1.0, 1.0),
4353                    (1.0, -1.0),
4354                    (-1.0, 1.0),
4355                    (-1.0, -1.0),
4356                ],
4357                // Interiors
4358                vec![vec![]],
4359                // Expected results
4360                vec![((0.5, 0.0), true), ((0.0, 0.5), false), ((0.0, 0.0), false)],
4361            ),
4362            // Create a GeoPolygon with an interior (a 'hole' inside the polygon)
4363            (
4364                // Exterior
4365                vec![
4366                    (-1.0, -1.0),
4367                    (1.5, -1.0),
4368                    (1.5, 1.5),
4369                    (-1.0, 1.5),
4370                    (-1.0, -1.0),
4371                ],
4372                // Interiors
4373                vec![vec![
4374                    (-0.5, -0.5),
4375                    (-0.5, 0.5),
4376                    (0.5, 0.5),
4377                    (0.5, -0.5),
4378                    (-0.5, -0.5),
4379                ]],
4380                // Expected results
4381                vec![((0.6, 0.6), true), ((0.0, 0.0), false), ((0.5, 0.5), false)],
4382            ),
4383        ];
4384
4385        for (exterior, interiors, points) in test_cases {
4386            let polygon = build_polygon_with_interiors(exterior, interiors);
4387
4388            for ((lon, lat), expected_result) in points {
4389                let inside_result = polygon
4390                    .convert()
4391                    .check_point(&GeoPoint::new_unchecked(lon, lat));
4392                assert_eq!(inside_result, expected_result);
4393            }
4394        }
4395    }
4396
4397    #[test]
4398    fn test_serialize_query() {
4399        let filter = Filter {
4400            must: Some(vec![Condition::Field(FieldCondition::new_match(
4401                JsonPath::new("hello"),
4402                "world".to_owned().into(),
4403            ))]),
4404            must_not: None,
4405            should: None,
4406            min_should: None,
4407        };
4408        let json = serde_json::to_string_pretty(&filter).unwrap();
4409        eprintln!("{json}")
4410    }
4411
4412    #[test]
4413    fn test_deny_unknown_fields() {
4414        let query1 = r#"
4415         {
4416            "wrong": "query"
4417         }
4418         "#;
4419        let filter: Result<Filter, _> = serde_json::from_str(query1);
4420
4421        assert!(filter.is_err())
4422    }
4423
4424    #[test]
4425    fn test_parse_match_query() {
4426        let query = r#"
4427        {
4428            "key": "hello",
4429            "match": { "value": 42 }
4430        }
4431        "#;
4432        let condition: FieldCondition = serde_json::from_str(query).unwrap();
4433        assert_eq!(
4434            condition.r#match.unwrap(),
4435            Match::Value(MatchValue {
4436                value: ValueVariants::Integer(42)
4437            })
4438        );
4439
4440        let query = r#"
4441        {
4442            "key": "hello",
4443            "match": { "value": true }
4444        }
4445        "#;
4446        let condition: FieldCondition = serde_json::from_str(query).unwrap();
4447        assert_eq!(
4448            condition.r#match.unwrap(),
4449            Match::Value(MatchValue {
4450                value: ValueVariants::Bool(true)
4451            })
4452        );
4453
4454        let query = r#"
4455        {
4456            "key": "hello",
4457            "match": { "value": "world" }
4458        }
4459        "#;
4460
4461        let condition: FieldCondition = serde_json::from_str(query).unwrap();
4462        assert_eq!(
4463            condition.r#match.unwrap(),
4464            Match::Value(MatchValue {
4465                value: ValueVariants::String("world".to_owned())
4466            })
4467        );
4468    }
4469
4470    #[test]
4471    fn test_parse_match_any() {
4472        let query = r#"
4473        {
4474            "should": [
4475                {
4476                    "key": "Jason",
4477                    "match": {
4478                        "any": [
4479                            "Bourne",
4480                            "Momoa",
4481                            "Statham"
4482                        ]
4483                    }
4484                }
4485            ]
4486        }
4487        "#;
4488
4489        let filter: Filter = serde_json::from_str(query).unwrap();
4490        let should = filter.should.unwrap();
4491
4492        assert_eq!(should.len(), 1);
4493        let Some(Condition::Field(c)) = should.first() else {
4494            panic!("Condition::Field expected")
4495        };
4496
4497        assert_eq!(c.key.to_string(), "Jason");
4498
4499        let Match::Any(m) = c.r#match.as_ref().unwrap() else {
4500            panic!("Match::Any expected")
4501        };
4502        if let AnyVariants::Strings(kws) = &m.any {
4503            assert_eq!(kws.len(), 3);
4504            let expect: IndexSet<_, FnvBuildHasher> = ["Bourne", "Momoa", "Statham"]
4505                .into_iter()
4506                .map(|i| i.to_string())
4507                .collect();
4508            assert_eq!(kws, &expect);
4509        } else {
4510            panic!("AnyVariants::Keywords expected");
4511        }
4512    }
4513
4514    #[test]
4515    fn test_parse_match_any_mixed_types() {
4516        let query = r#"
4517        {
4518            "should": [
4519                {
4520                    "key": "Jason",
4521                    "match": {
4522                        "any": [
4523                            "Bourne",
4524                            42
4525                        ]
4526                    }
4527                }
4528            ]
4529        }
4530        "#;
4531
4532        let result: Result<Filter, _> = serde_json::from_str(query);
4533        assert!(result.is_err());
4534    }
4535
4536    #[test]
4537    fn test_parse_nested_match_query() {
4538        let query = r#"
4539        {
4540            "key": "hello.nested",
4541            "match": { "value": 42 }
4542        }
4543        "#;
4544        let condition: FieldCondition = serde_json::from_str(query).unwrap();
4545        assert_eq!(
4546            condition.r#match.unwrap(),
4547            Match::Value(MatchValue {
4548                value: ValueVariants::Integer(42)
4549            })
4550        );
4551
4552        let query = r#"
4553        {
4554            "key": "hello.nested",
4555            "match": { "value": true }
4556        }
4557        "#;
4558        let condition: FieldCondition = serde_json::from_str(query).unwrap();
4559        assert_eq!(
4560            condition.r#match.unwrap(),
4561            Match::Value(MatchValue {
4562                value: ValueVariants::Bool(true)
4563            })
4564        );
4565
4566        let query = r#"
4567        {
4568            "key": "hello.nested",
4569            "match": { "value": "world" }
4570        }
4571        "#;
4572
4573        let condition: FieldCondition = serde_json::from_str(query).unwrap();
4574        assert_eq!(
4575            condition.r#match.unwrap(),
4576            Match::Value(MatchValue {
4577                value: ValueVariants::String("world".to_owned())
4578            })
4579        );
4580    }
4581
4582    #[test]
4583    fn test_parse_empty_query() {
4584        let query = r#"
4585        {
4586            "should": [
4587                {
4588                    "is_empty" : {
4589                        "key" : "Jason"
4590                    }
4591                }
4592            ]
4593        }
4594        "#;
4595
4596        let filter: Filter = serde_json::from_str(query).unwrap();
4597        let should = filter.should.unwrap();
4598
4599        assert_eq!(should.len(), 1);
4600        let Some(Condition::IsEmpty(c)) = should.first() else {
4601            panic!("Condition::IsEmpty expected")
4602        };
4603
4604        assert_eq!(c.is_empty.key.to_string(), "Jason");
4605    }
4606
4607    #[test]
4608    fn test_parse_null_query() {
4609        let query = r#"
4610        {
4611            "should": [
4612                {
4613                    "is_null" : {
4614                        "key" : "Jason"
4615                    }
4616                }
4617            ]
4618        }
4619        "#;
4620
4621        let filter: Filter = serde_json::from_str(query).unwrap();
4622        let should = filter.should.unwrap();
4623
4624        assert_eq!(should.len(), 1);
4625        let Some(Condition::IsNull(c)) = should.first() else {
4626            panic!("Condition::IsNull expected")
4627        };
4628
4629        assert_eq!(c.is_null.key.to_string(), "Jason");
4630    }
4631
4632    #[test]
4633    fn test_parse_nested_filter_query() {
4634        let query = r#"
4635        {
4636          "must": [
4637            {
4638              "nested": {
4639                "key": "country.cities",
4640                "filter": {
4641                  "must": [
4642                    {
4643                      "key": "population",
4644                      "range": {
4645                        "gte": 8
4646                      }
4647                    },
4648                    {
4649                      "key": "sightseeing",
4650                      "values_count": {
4651                        "lt": 3
4652                      }
4653                    }
4654                  ]
4655                }
4656              }
4657            }
4658          ]
4659        }
4660        "#;
4661        let filter: Filter = serde_json::from_str(query).unwrap();
4662        let musts = filter.must.unwrap();
4663        assert_eq!(musts.len(), 1);
4664        match musts.first() {
4665            Some(Condition::Nested(nested_condition)) => {
4666                assert_eq!(nested_condition.raw_key().to_string(), "country.cities");
4667                assert_eq!(nested_condition.array_key().to_string(), "country.cities[]");
4668                let nested_musts = nested_condition.filter().must.as_ref().unwrap();
4669                assert_eq!(nested_musts.len(), 2);
4670                let first_must = nested_musts.first().unwrap();
4671                match first_must {
4672                    Condition::Field(c) => {
4673                        assert_eq!(c.key.to_string(), "population");
4674                        assert!(c.range.is_some());
4675                    }
4676                    _ => panic!("Condition::Field expected"),
4677                }
4678
4679                let second_must = nested_musts.get(1).unwrap();
4680                match second_must {
4681                    Condition::Field(c) => {
4682                        assert_eq!(c.key.to_string(), "sightseeing");
4683                        assert!(c.values_count.is_some());
4684                    }
4685                    _ => panic!("Condition::Field expected"),
4686                }
4687            }
4688            o => panic!("Condition::Nested expected but got {o:?}"),
4689        };
4690    }
4691
4692    #[test]
4693    fn test_parse_single_nested_filter_query() {
4694        let query = r#"
4695        {
4696          "must": {
4697              "nested": {
4698                "key": "country.cities",
4699                "filter": {
4700                  "must": {
4701                      "key": "population",
4702                      "range": {
4703                        "gte": 8
4704                      }
4705                    }
4706                }
4707              }
4708            }
4709        }
4710        "#;
4711        let filter: Filter = serde_json::from_str(query).unwrap();
4712        let musts = filter.must.unwrap();
4713        assert_eq!(musts.len(), 1);
4714
4715        let first_must = musts.first().unwrap();
4716        let Condition::Nested(nested_condition) = first_must else {
4717            panic!("Condition::Nested expected but got {first_must:?}")
4718        };
4719
4720        assert_eq!(nested_condition.raw_key().to_string(), "country.cities");
4721        assert_eq!(nested_condition.array_key().to_string(), "country.cities[]");
4722
4723        let nested_must = nested_condition.filter().must.as_ref().unwrap();
4724        assert_eq!(nested_must.len(), 1);
4725
4726        let must = nested_must.first().unwrap();
4727        let Condition::Field(c) = must else {
4728            panic!("Condition::Field expected, got {must:?}")
4729        };
4730
4731        assert_eq!(c.key.to_string(), "population");
4732        assert!(c.range.is_some());
4733    }
4734
4735    #[test]
4736    fn test_payload_query_parse() {
4737        let query1 = r#"
4738        {
4739            "must": [
4740                {
4741                    "key": "hello",
4742                    "match": {
4743                        "value": 42
4744                    }
4745                },
4746                {
4747                    "must_not": [
4748                        {
4749                            "has_id": [1, 2, 3, 4]
4750                        },
4751                        {
4752                            "key": "geo_field",
4753                            "geo_bounding_box": {
4754                                "top_left": {
4755                                    "lon": 13.410146,
4756                                    "lat": 52.519289
4757                                },
4758                                "bottom_right": {
4759                                    "lon": 13.432683,
4760                                    "lat": 52.505582
4761                                }
4762                            }
4763                        }
4764                    ]
4765                }
4766            ]
4767        }
4768        "#;
4769
4770        let filter: Filter = serde_json::from_str(query1).unwrap();
4771        eprintln!("{filter:?}");
4772        let must = filter.must.unwrap();
4773        let _must_not = filter.must_not;
4774        assert_eq!(must.len(), 2);
4775        match must.get(1) {
4776            Some(Condition::Filter(f)) => {
4777                let must_not = &f.must_not;
4778                match must_not {
4779                    Some(v) => assert_eq!(v.len(), 2),
4780                    None => panic!("Filter expected"),
4781                }
4782            }
4783            _ => panic!("Condition expected"),
4784        }
4785    }
4786
4787    #[test]
4788    fn test_nested_payload_query_parse() {
4789        let query1 = r#"
4790        {
4791            "must": [
4792                {
4793                    "key": "hello.nested.world",
4794                    "match": {
4795                        "value": 42
4796                    }
4797                },
4798                {
4799                    "key": "foo.nested.bar",
4800                    "match": {
4801                        "value": 1
4802                    }
4803                }
4804            ]
4805        }
4806        "#;
4807
4808        let filter: Filter = serde_json::from_str(query1).unwrap();
4809        let must = filter.must.unwrap();
4810        assert_eq!(must.len(), 2);
4811    }
4812
4813    #[test]
4814    fn test_min_should_query_parse() {
4815        let query1 = r#"
4816        {
4817            "min_should": {
4818                "conditions": [
4819                    {
4820                        "key": "hello.nested.world",
4821                        "match": {
4822                            "value": 42
4823                        }
4824                    },
4825                    {
4826                        "key": "foo.nested.bar",
4827                        "match": {
4828                            "value": 1
4829                        }
4830                    }
4831                ],
4832                "min_count": 2
4833            }
4834        }
4835        "#;
4836
4837        let filter: Filter = serde_json::from_str(query1).unwrap();
4838        let min_should = filter.min_should.unwrap();
4839        assert_eq!(min_should.conditions.len(), 2);
4840    }
4841
4842    #[test]
4843    fn test_min_should_nested_parse() {
4844        let query1 = r#"
4845        {
4846            "must": [
4847                {
4848                    "min_should": {
4849                        "conditions": [
4850                            {
4851                                "key": "hello.nested.world",
4852                                "match": {
4853                                    "value": 42
4854                                }
4855                            },
4856                            {
4857                                "key": "foo.nested.bar",
4858                                "match": {
4859                                    "value": 1
4860                                }
4861                            }
4862                        ],
4863                        "min_count": 2
4864                    }
4865                }
4866            ]
4867        }
4868        "#;
4869
4870        let filter: Filter = serde_json::from_str(query1).unwrap();
4871        let must = filter.must.unwrap();
4872        assert_eq!(must.len(), 1);
4873
4874        match must.first() {
4875            Some(Condition::Filter(f)) => {
4876                let min_should = &f.min_should;
4877                match min_should {
4878                    Some(v) => assert_eq!(v.conditions.len(), 2),
4879                    None => panic!("Filter expected"),
4880                }
4881            }
4882            _ => panic!("Condition expected"),
4883        }
4884    }
4885
4886    #[test]
4887    fn test_geo_validation() {
4888        let query1 = r#"
4889        {
4890            "must": [
4891                {
4892                    "key": "geo_field",
4893                    "geo_bounding_box": {
4894                        "top_left": {
4895                            "lon": 1113.410146,
4896                            "lat": 52.519289
4897                        },
4898                        "bottom_right": {
4899                            "lon": 13.432683,
4900                            "lat": 52.505582
4901                        }
4902                    }
4903                }
4904            ]
4905        }
4906        "#;
4907        let filter: Result<Filter, _> = serde_json::from_str(query1);
4908        assert!(filter.is_err());
4909
4910        let query2 = r#"
4911        {
4912            "must": [
4913                {
4914                    "key": "geo_field",
4915                    "geo_polygon": {
4916                        "exterior": {},
4917                        "interiors": []
4918                    }
4919                }
4920            ]
4921        }
4922        "#;
4923        let filter: Result<Filter, _> = serde_json::from_str(query2);
4924        assert!(filter.is_err());
4925
4926        let query3 = r#"
4927        {
4928            "must": [
4929                {
4930                    "key": "geo_field",
4931                    "geo_polygon": {
4932                        "exterior":{
4933                            "points": [
4934                                {"lon": -12.0, "lat": -34.0},
4935                                {"lon": 11.0, "lat": -22.0},
4936                                {"lon": -32.0, "lat": -14.0}
4937                            ]
4938                        },
4939                        "interiors": []
4940                    }
4941                }
4942            ]
4943        }
4944        "#;
4945        let filter: Result<Filter, _> = serde_json::from_str(query3);
4946        assert!(filter.is_err());
4947
4948        let query4 = r#"
4949        {
4950            "must": [
4951                {
4952                    "key": "geo_field",
4953                    "geo_polygon": {
4954                        "exterior": {
4955                            "points": [
4956                                {"lon": -12.0, "lat": -34.0},
4957                                {"lon": 11.0, "lat": -22.0},
4958                                {"lon": -32.0, "lat": -14.0},
4959                                {"lon": -12.0, "lat": -34.0}
4960                            ]
4961                        },
4962                        "interiors": []
4963                    }
4964                }
4965            ]
4966        }
4967        "#;
4968        let filter: Result<Filter, _> = serde_json::from_str(query4);
4969        assert!(filter.is_ok());
4970
4971        let query5 = r#"
4972            {
4973                "must": [
4974                    {
4975                        "key": "geo_field",
4976                        "geo_polygon": {
4977                            "exterior": {
4978                                    "points": [
4979                                        {"lon": -12.0, "lat": -34.0},
4980                                        {"lon": 11.0, "lat": -22.0},
4981                                        {"lon": -32.0, "lat": -14.0},
4982                                        {"lon": -12.0, "lat": -34.0}
4983                                    ]
4984                                },
4985                            "interiors": [
4986                                {
4987                                    "points": [
4988                                        {"lon": -12.0, "lat": -34.0},
4989                                        {"lon": 11.0, "lat": -22.0},
4990                                        {"lon": -32.0, "lat": -14.0}
4991                                    ]
4992                                }
4993                            ]
4994                        }
4995                    }
4996                ]
4997            }
4998            "#;
4999        let filter: Result<Filter, _> = serde_json::from_str(query5);
5000        assert!(filter.is_err());
5001
5002        let query6 = r#"
5003            {
5004                "must": [
5005                    {
5006                        "key": "geo_field",
5007                        "geo_polygon": {
5008                            "exterior": {
5009                                    "points": [
5010                                        {"lon": -12.0, "lat": -34.0},
5011                                        {"lon": 11.0, "lat": -22.0},
5012                                        {"lon": -32.0, "lat": -14.0},
5013                                        {"lon": -12.0, "lat": -34.0}
5014                                    ]
5015                                },
5016                            "interiors": [
5017                                {
5018                                    "points": [
5019                                        {"lon": -12.0, "lat": -34.0},
5020                                        {"lon": 11.0, "lat": -22.0},
5021                                        {"lon": -32.0, "lat": -14.0},
5022                                        {"lon": -12.0, "lat": -34.0}
5023                                    ]
5024                                }
5025                            ]
5026                        }
5027                    }
5028                ]
5029            }
5030            "#;
5031        let filter: Result<Filter, _> = serde_json::from_str(query6);
5032        assert!(filter.is_ok());
5033    }
5034
5035    #[test]
5036    fn test_payload_parsing() {
5037        let ft = PayloadFieldSchema::FieldType(PayloadSchemaType::Keyword);
5038        let ft_json = serde_json::to_string(&ft).unwrap();
5039        eprintln!("ft_json = {ft_json:?}");
5040
5041        let ft = PayloadFieldSchema::FieldParams(PayloadSchemaParams::Text(Default::default()));
5042        let ft_json = serde_json::to_string(&ft).unwrap();
5043        eprintln!("ft_json = {ft_json:?}");
5044
5045        let query = r#""keyword""#;
5046        let field_type: PayloadSchemaType = serde_json::from_str(query).unwrap();
5047        eprintln!("field_type = {field_type:?}");
5048    }
5049
5050    #[test]
5051    fn merge_filters() {
5052        let condition1 = Condition::Field(FieldCondition::new_match(
5053            JsonPath::new("summary"),
5054            Match::new_text("Berlin"),
5055        ));
5056        let mut this = Filter::new_must(condition1.clone());
5057        this.should = Some(vec![condition1.clone()]);
5058
5059        let condition2 = Condition::Field(FieldCondition::new_match(
5060            JsonPath::new("city"),
5061            Match::new_value(ValueVariants::String("Osaka".into())),
5062        ));
5063        let other = Filter::new_must(condition2.clone());
5064
5065        let merged = this.merge(&other);
5066
5067        assert!(merged.must.is_some());
5068        assert_eq!(merged.must.as_ref().unwrap().len(), 2);
5069        assert!(merged.must_not.is_none());
5070        assert!(merged.should.is_some());
5071        assert_eq!(merged.should.as_ref().unwrap().len(), 1);
5072
5073        assert!(merged.must.as_ref().unwrap().contains(&condition1));
5074        assert!(merged.must.as_ref().unwrap().contains(&condition2));
5075        assert!(merged.should.as_ref().unwrap().contains(&condition1));
5076    }
5077
5078    #[test]
5079    fn test_payload_selector_include() {
5080        let payload = payload_json! {
5081            "a": 1,
5082            "b": {
5083                "c": 123,
5084                "e": {
5085                    "f": [1,2,3],
5086                    "g": 7,
5087                    "h": "text",
5088                    "i": [
5089                        {
5090                            "j": 1,
5091                            "k": 2
5092
5093                        },
5094                        {
5095                            "j": 3,
5096                            "k": 4
5097                        }
5098                    ]
5099                }
5100            }
5101        };
5102
5103        // include root & nested
5104        let selector =
5105            PayloadSelector::new_include(vec![JsonPath::new("a"), JsonPath::new("b.e.f")]);
5106        let payload = selector.process(payload);
5107
5108        let expected = payload_json! {
5109            "a": 1,
5110            "b": {
5111                "e": {
5112                    "f": [1,2,3],
5113                }
5114            }
5115        };
5116        assert_eq!(payload, expected);
5117    }
5118
5119    #[test]
5120    fn test_payload_selector_array_include() {
5121        let payload = payload_json! {
5122            "a": 1,
5123            "b": {
5124                "c": 123,
5125                "f": [1,2,3,4,5],
5126            }
5127        };
5128
5129        // handles duplicates
5130        let selector = PayloadSelector::new_include(vec![JsonPath::new("a"), JsonPath::new("a")]);
5131        let payload = selector.process(payload);
5132
5133        let expected = payload_json! {
5134            "a": 1
5135        };
5136        assert_eq!(payload, expected);
5137
5138        // ignore path that points to array
5139        let selector = PayloadSelector::new_include(vec![JsonPath::new("b.f[0]")]);
5140        let payload = selector.process(payload);
5141
5142        // nothing included
5143        let expected = payload_json! {};
5144        assert_eq!(payload, expected);
5145    }
5146
5147    #[test]
5148    fn test_payload_selector_no_implicit_array_include() {
5149        let payload = payload_json! {
5150            "a": 1,
5151            "b": {
5152                "c": [
5153                    {
5154                        "d": 1,
5155                        "e": 2
5156                    },
5157                    {
5158                        "d": 3,
5159                        "e": 4
5160                    }
5161                ],
5162            }
5163        };
5164
5165        let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c")]);
5166        let selected_payload = selector.process(payload.clone());
5167
5168        let expected = payload_json! {
5169            "b": {
5170                "c": [
5171                    {
5172                        "d": 1,
5173                        "e": 2
5174                    },
5175                    {
5176                        "d": 3,
5177                        "e": 4
5178                    }
5179                ]
5180            }
5181        };
5182        assert_eq!(selected_payload, expected);
5183
5184        // with explicit array traversal ([] notation)
5185        let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c[].d")]);
5186        let selected_payload = selector.process(payload.clone());
5187
5188        let expected = payload_json! {
5189            "b": {
5190                "c": [
5191                    {"d": 1},
5192                    {"d": 3}
5193                ]
5194            }
5195        };
5196        assert_eq!(selected_payload, expected);
5197
5198        // shortcuts implicit array traversal
5199        let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c.d")]);
5200        let selected_payload = selector.process(payload);
5201
5202        let expected = payload_json! {
5203            "b": {
5204                "c": []
5205            }
5206        };
5207        assert_eq!(selected_payload, expected);
5208    }
5209
5210    #[test]
5211    fn test_payload_selector_exclude() {
5212        let payload = payload_json! {
5213            "a": 1,
5214            "b": {
5215                "c": 123,
5216                "e": {
5217                    "f": [1,2,3],
5218                    "g": 7,
5219                    "h": "text",
5220                    "i": [
5221                        {
5222                            "j": 1,
5223                            "k": 2
5224
5225                        },
5226                        {
5227                            "j": 3,
5228                            "k": 4
5229                        }
5230                    ]
5231                }
5232            }
5233        };
5234
5235        // exclude
5236        let selector =
5237            PayloadSelector::new_exclude(vec![JsonPath::new("a"), JsonPath::new("b.e.f")]);
5238        let payload = selector.process(payload);
5239
5240        // root removal & nested removal
5241        let expected = payload_json! {
5242            "b": {
5243                "c": 123,
5244                "e": {
5245                    "g": 7,
5246                    "h": "text",
5247                    "i": [
5248                        {
5249                            "j": 1,
5250                            "k": 2
5251
5252                        },
5253                        {
5254                            "j": 3,
5255                            "k": 4
5256                        }
5257                    ]
5258                }
5259            }
5260        };
5261        assert_eq!(payload, expected);
5262    }
5263
5264    #[test]
5265    fn test_payload_selector_array_exclude() {
5266        let payload = payload_json! {
5267            "a": 1,
5268            "b": {
5269                "c": 123,
5270                "f": [1,2,3,4,5],
5271            }
5272        };
5273
5274        // handles duplicates
5275        let selector = PayloadSelector::new_exclude(vec![JsonPath::new("a"), JsonPath::new("a")]);
5276        let payload = selector.process(payload);
5277
5278        // single removal
5279        let expected = payload_json! {
5280            "b": {
5281                "c": 123,
5282                "f": [1,2,3,4,5],
5283            }
5284        };
5285        assert_eq!(payload, expected);
5286
5287        // ignore path that points to array
5288        let selector = PayloadSelector::new_exclude(vec![JsonPath::new("b.f[0]")]);
5289
5290        let payload = selector.process(payload);
5291
5292        // no removal
5293        let expected = payload_json! {
5294            "b": {
5295                "c": 123,
5296                "f": [1,2,3,4,5],
5297            }
5298        };
5299        assert_eq!(payload, expected);
5300    }
5301
5302    #[test]
5303    fn test_extended_point_id_cbor_roundtrip() {
5304        let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
5305
5306        for point_id in [ExtendedPointId::Uuid(uuid), ExtendedPointId::NumId(42)] {
5307            let cbor_bytes = serde_cbor::to_vec(&point_id).unwrap();
5308            let deserialized: ExtendedPointId = serde_cbor::from_slice(&cbor_bytes).unwrap();
5309            assert_eq!(point_id, deserialized);
5310        }
5311    }
5312
5313    #[test]
5314    fn test_filter_with_match_and_has_id_uuid_cbor_roundtrip() {
5315        let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
5316        let filter = Filter {
5317            should: None,
5318            min_should: None,
5319            must: Some(vec![Condition::Field(FieldCondition::new_match(
5320                crate::segment::json_path::JsonPath::new("org_id"),
5321                Match::new_value(ValueVariants::String("test_org".to_string())),
5322            ))]),
5323            must_not: Some(vec![Condition::HasId(HasIdCondition {
5324                has_id: [ExtendedPointId::Uuid(uuid)].into_iter().collect(),
5325            })]),
5326        };
5327
5328        let cbor_bytes = serde_cbor::to_vec(&filter).unwrap();
5329        let deserialized: Filter = serde_cbor::from_slice(&cbor_bytes).unwrap();
5330        assert_eq!(filter, deserialized);
5331    }
5332}
5333
5334fn shard_key_string_example() -> String {
5335    "region_1".to_string()
5336}
5337
5338fn shard_key_number_example() -> u64 {
5339    12
5340}
5341
5342#[derive(Deserialize, Serialize, JsonSchema,  Debug, Clone, PartialEq, Eq, Hash)]
5343#[serde(untagged)]
5344pub enum ShardKey {
5345    #[schemars(
5346        schema_with = "String::json_schema",
5347        example = "shard_key_string_example"
5348    )]
5349    Keyword(EcoString),
5350    #[schemars(example = "shard_key_number_example")]
5351    
5352    Number(u64),
5353}
5354
5355impl From<String> for ShardKey {
5356    fn from(s: String) -> Self {
5357        ShardKey::Keyword(EcoString::from(s))
5358    }
5359}
5360
5361impl From<EcoString> for ShardKey {
5362    fn from(s: EcoString) -> Self {
5363        ShardKey::Keyword(s)
5364    }
5365}
5366
5367impl From<&str> for ShardKey {
5368    fn from(s: &str) -> Self {
5369        ShardKey::Keyword(EcoString::from(s))
5370    }
5371}
5372
5373impl From<u64> for ShardKey {
5374    fn from(n: u64) -> Self {
5375        ShardKey::Number(n)
5376    }
5377}
5378
5379impl Display for ShardKey {
5380    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5381        match self {
5382            ShardKey::Keyword(keyword) => write!(f, "\"{keyword}\""),
5383            ShardKey::Number(number) => write!(f, "{number}"),
5384        }
5385    }
5386}