1#![allow(deprecated)]
4
5use std::borrow::Cow;
6use std::cmp::Ordering;
7use std::collections::{BTreeMap, HashMap, HashSet};
8use std::fmt::{self, Display, Formatter};
9use std::hash::{self, Hash, Hasher};
10use std::mem;
11use std::num::NonZeroU32;
12use std::ops::Deref;
13use std::rc::Rc;
14use std::str::FromStr;
15use std::sync::Arc;
16
17use ahash::AHashSet;
18use bytemuck::{Pod, Zeroable};
19use crate::common::stable_hash::StableHash;
20use crate::common::types::{PointOffsetType, ScoreType};
21use ecow::EcoString;
22use fnv::FnvBuildHasher;
23use geo::{Contains, Coord, Distance as GeoDistance, Haversine, LineString, Point, Polygon};
24use indexmap::IndexSet;
25use itertools::Itertools;
26use num_derive::FromPrimitive;
27use ordered_float::OrderedFloat;
28use schemars::JsonSchema;
29use serde::{Deserialize, Deserializer, Serialize};
30use serde_json::{Map, Value};
31use strum::{EnumIter, EnumString};
32use uuid::Uuid;
33use validator::{Validate, ValidationError, ValidationErrors};
34use zerocopy::native_endian::U64;
35
36use crate::segment::common::anonymize::Anonymize;
37use crate::segment::common::operation_error::{OperationError, OperationResult};
38use crate::segment::common::utils::{self, MaybeOneOrMany, MultiValue};
39use crate::segment::data_types::index::{
40 BoolIndexParams, DatetimeIndexParams, FloatIndexParams, GeoIndexParams, IntegerIndexParams,
41 KeywordIndexParams, TextIndexParams, UuidIndexParams,
42};
43use crate::segment::data_types::modifier::Modifier;
44use crate::segment::data_types::order_by::OrderValue;
45use crate::segment::data_types::primitive::PrimitiveVectorElement;
46use crate::segment::data_types::tiny_map::TinyMap;
47use crate::segment::data_types::vectors::{DenseVector, VectorStructInternal};
48use crate::segment::index::field_index::CardinalityEstimation;
49use crate::segment::index::sparse_index::sparse_index_config::SparseIndexConfig;
50use crate::segment::json_path::JsonPath;
51use crate::segment::spaces::metric::{Metric, MetricPostProcessing};
52use crate::segment::spaces::simple::{CosineMetric, DotProductMetric, EuclidMetric, ManhattanMetric};
53use crate::segment::types::utils::unordered_hash_unique;
54use crate::segment::utils::maybe_arc::MaybeArc;
55
56pub type PayloadKeyType = JsonPath;
57pub type PayloadKeyTypeRef<'a> = &'a JsonPath;
58pub type SeqNumberType = u64;
60pub type FloatPayloadType = f64;
62pub type IntPayloadType = i64;
64pub type DateTimePayloadType = DateTimeWrapper;
66pub type UuidPayloadType = Uuid;
68pub type UuidIntType = u128;
70pub type VectorName = str;
72pub type VectorNameBuf = String;
74
75#[derive(Clone, Copy, Serialize, JsonSchema, Debug, PartialEq, Eq, PartialOrd, Hash)]
77#[serde(transparent)]
78pub struct DateTimeWrapper(pub chrono::DateTime<chrono::Utc>);
79
80impl DateTimeWrapper {
81 pub fn timestamp(&self) -> i64 {
83 self.0.timestamp_micros()
84 }
85
86 pub fn from_timestamp(ts: i64) -> Option<Self> {
87 Some(Self(chrono::DateTime::from_timestamp_micros(ts)?))
88 }
89}
90
91impl<'de> Deserialize<'de> for DateTimePayloadType {
92 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
99 where
100 D: Deserializer<'de>,
101 {
102 let str_datetime: Cow<'de, str> = Cow::deserialize(deserializer)?;
103
104 match DateTimePayloadType::from_str(str_datetime.as_ref()) {
105 Ok(datetime) => Ok(datetime),
106 Err(_) => Err(serde::de::Error::custom(format!(
107 "'{str_datetime}' does not match accepted datetime format (RFC3339). Example: 2014-01-01T00:00:00Z"
108 ))),
109 }
110 }
111}
112
113impl FromStr for DateTimePayloadType {
114 type Err = chrono::ParseError;
115
116 fn from_str(s: &str) -> Result<Self, Self::Err> {
117 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(s)
119 .or_else(|_| chrono::DateTime::from_str(s))
121 .or_else(|_| chrono::DateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f%#z"))
125 .or_else(|_| chrono::DateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f%#z"))
126 .map(|dt| chrono::DateTime::<chrono::Utc>::from(dt).into())
127 {
128 return Ok(datetime);
129 }
130
131 let datetime = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")
140 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f"))
141 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M"))
142 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M"))
143 .or_else(|_| chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d").map(Into::into))?;
144
145 let datetime_utc = datetime.and_utc().into();
147 Ok(datetime_utc)
148 }
149}
150
151impl Display for DateTimePayloadType {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 write!(f, "{}", self.0)
154 }
155}
156
157impl From<chrono::DateTime<chrono::Utc>> for DateTimePayloadType {
158 fn from(dt: chrono::DateTime<chrono::Utc>) -> Self {
159 DateTimeWrapper(dt)
160 }
161}
162
163fn id_num_example() -> u64 {
164 42
165}
166
167fn id_uuid_example() -> String {
168 "550e8400-e29b-41d4-a716-446655440000".to_string()
169}
170
171#[derive(Debug, Serialize, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, JsonSchema)]
173#[serde(untagged)]
174pub enum ExtendedPointId {
175 #[schemars(example = "id_num_example")]
176 NumId(u64),
177 #[schemars(example = "id_uuid_example")]
178 Uuid(Uuid),
179}
180
181impl StableHash for ExtendedPointId {
182 fn stable_hash<W: FnMut(&[u8])>(&self, write: &mut W) {
183 match self {
184 ExtendedPointId::NumId(num) => {
185 0u64.stable_hash(write); num.stable_hash(write);
187 }
188 ExtendedPointId::Uuid(uuid) => {
189 1u64.stable_hash(write); uuid.as_bytes().len().stable_hash(write); write(uuid.as_bytes());
193 }
194 }
195 }
196}
197
198impl ExtendedPointId {
199 #[cfg(any(test, feature = "testing"))]
200 pub fn as_u64(&self) -> u64 {
201 match self {
202 ExtendedPointId::NumId(num) => *num,
203 ExtendedPointId::Uuid(_) => panic!("Cannot convert UUID to u64"),
204 }
205 }
206
207 pub fn is_uuid(&self) -> bool {
208 matches!(self, ExtendedPointId::Uuid(..))
209 }
210}
211
212impl std::fmt::Display for ExtendedPointId {
213 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
214 match self {
215 ExtendedPointId::NumId(idx) => write!(f, "{idx}"),
216 ExtendedPointId::Uuid(uuid) => write!(f, "{uuid}"),
217 }
218 }
219}
220
221impl From<u64> for ExtendedPointId {
222 fn from(idx: u64) -> Self {
223 ExtendedPointId::NumId(idx)
224 }
225}
226
227impl FromStr for ExtendedPointId {
228 type Err = ();
229
230 fn from_str(s: &str) -> Result<Self, Self::Err> {
231 let try_num: Result<u64, _> = s.parse();
232 if let Ok(num) = try_num {
233 return Ok(Self::NumId(num));
234 }
235 let try_uuid = Uuid::from_str(s);
236 if let Ok(uuid) = try_uuid {
237 return Ok(Self::Uuid(uuid));
238 }
239 Err(())
240 }
241}
242
243impl<'de> serde::Deserialize<'de> for ExtendedPointId {
244 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245 where
246 D: serde::Deserializer<'de>,
247 {
248 let value = serde_value::Value::deserialize(deserializer)?;
249
250 if let Ok(num) = value.clone().deserialize_into() {
251 return Ok(ExtendedPointId::NumId(num));
252 }
253
254 if let Ok(uuid) = value.clone().deserialize_into() {
255 return Ok(ExtendedPointId::Uuid(uuid));
256 }
257
258 let value = crate::segment::utils::fmt::SerdeValue(&value);
259
260 Err(serde::de::Error::custom(format!(
261 "value {value} is not a valid point ID, \
262 valid values are either an unsigned integer or a UUID",
263 )))
264 }
265}
266
267pub type PointIdType = ExtendedPointId;
269
270#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
273pub enum CompactExtendedPointId {
274 NumId(U64),
275 Uuid(Uuid),
276}
277
278impl From<ExtendedPointId> for CompactExtendedPointId {
279 fn from(id: ExtendedPointId) -> Self {
280 match id {
281 ExtendedPointId::NumId(num) => CompactExtendedPointId::NumId(U64::new(num)),
282 ExtendedPointId::Uuid(uuid) => CompactExtendedPointId::Uuid(uuid),
283 }
284 }
285}
286
287impl From<CompactExtendedPointId> for ExtendedPointId {
288 fn from(id: CompactExtendedPointId) -> Self {
289 match id {
290 CompactExtendedPointId::NumId(num) => ExtendedPointId::NumId(num.get()),
291 CompactExtendedPointId::Uuid(uuid) => ExtendedPointId::Uuid(uuid),
292 }
293 }
294}
295
296#[derive(
298 Debug,
299 Deserialize,
300 Serialize,
301 JsonSchema,
302
303 Clone,
304 Copy,
305 FromPrimitive,
306 PartialEq,
307 Eq,
308 Hash,
309 EnumString,
310 EnumIter,
311)]
312pub enum Distance {
314 Cosine,
316 Euclid,
318 Dot,
320 Manhattan,
322}
323
324impl Distance {
325 pub fn postprocess_score(&self, score: ScoreType) -> ScoreType {
326 match self {
327 Distance::Cosine => CosineMetric::postprocess(score),
328 Distance::Euclid => EuclidMetric::postprocess(score),
329 Distance::Dot => DotProductMetric::postprocess(score),
330 Distance::Manhattan => ManhattanMetric::postprocess(score),
331 }
332 }
333
334 pub fn preprocess_vector<T: PrimitiveVectorElement>(&self, vector: DenseVector) -> DenseVector
335 where
336 CosineMetric: Metric<T>,
337 EuclidMetric: Metric<T>,
338 DotProductMetric: Metric<T>,
339 ManhattanMetric: Metric<T>,
340 {
341 match self {
342 Distance::Cosine => CosineMetric::preprocess(vector),
343 Distance::Euclid => EuclidMetric::preprocess(vector),
344 Distance::Dot => DotProductMetric::preprocess(vector),
345 Distance::Manhattan => ManhattanMetric::preprocess(vector),
346 }
347 }
348
349 pub fn distance_order(&self) -> Order {
350 match self {
351 Distance::Cosine | Distance::Dot => Order::LargeBetter,
352 Distance::Euclid | Distance::Manhattan => Order::SmallBetter,
353 }
354 }
355
356 pub fn is_ordered(&self, left: ScoreType, right: ScoreType) -> bool {
357 match self.distance_order() {
358 Order::LargeBetter => left >= right,
359 Order::SmallBetter => left <= right,
360 }
361 }
362
363 pub fn check_threshold(&self, score: ScoreType, threshold: ScoreType) -> bool {
365 match self.distance_order() {
366 Order::LargeBetter => score > threshold,
367 Order::SmallBetter => score < threshold,
368 }
369 }
370}
371
372impl From<Distance> for crate::quantization::DistanceType {
378 fn from(distance: Distance) -> Self {
379 match distance {
380 Distance::Cosine => crate::quantization::DistanceType::Cosine,
381 Distance::Euclid => crate::quantization::DistanceType::L2,
382 Distance::Dot => crate::quantization::DistanceType::Dot,
383 Distance::Manhattan => crate::quantization::DistanceType::L1,
384 }
385 }
386}
387
388#[derive(Debug, PartialEq, Clone, Copy)]
389pub enum Order {
390 LargeBetter,
391 SmallBetter,
392}
393
394#[derive(Clone, Debug)]
396pub struct ScoredPoint {
397 pub id: PointIdType,
399 pub version: SeqNumberType,
401 pub score: ScoreType,
403 pub payload: Option<Payload>,
405 pub vector: Option<VectorStructInternal>,
407 pub shard_key: Option<ShardKey>,
409 pub order_value: Option<OrderValue>,
411}
412
413impl Eq for ScoredPoint {}
414
415impl Ord for ScoredPoint {
416 fn cmp(&self, other: &Self) -> Ordering {
418 match (&self.order_value, &other.order_value) {
419 (None, None) => OrderedFloat(self.score).cmp(&OrderedFloat(other.score)),
420 (Some(_), None) => Ordering::Greater,
421 (None, Some(_)) => Ordering::Less,
422 (Some(self_order), Some(other_order)) => self_order.cmp(other_order),
423 }
424 }
425}
426
427impl PartialOrd for ScoredPoint {
428 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
429 Some(self.cmp(other))
430 }
431}
432
433impl PartialEq for ScoredPoint {
434 fn eq(&self, other: &Self) -> bool {
435 (self.id, &self.score) == (other.id, &other.score)
436 }
437}
438
439#[derive(Debug, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq)]
441#[serde(rename_all = "snake_case")]
442pub enum SegmentType {
443 Plain,
445 Indexed,
447 Special,
449}
450
451#[derive(Debug, Serialize, JsonSchema, Clone, PartialEq, Eq)]
453#[serde(rename_all = "snake_case")]
454pub struct PayloadIndexInfo {
455 pub data_type: PayloadSchemaType,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub params: Option<PayloadSchemaParams>,
458 pub points: usize,
460}
461
462impl PayloadIndexInfo {
463 pub fn new(field_type: PayloadFieldSchema, points_count: usize) -> Self {
464 match field_type {
465 PayloadFieldSchema::FieldType(data_type) => PayloadIndexInfo {
466 data_type,
467 params: None,
468 points: points_count,
469 },
470 PayloadFieldSchema::FieldParams(schema_params) => PayloadIndexInfo {
471 data_type: schema_params.kind(),
472 params: Some(schema_params),
473 points: points_count,
474 },
475 }
476 }
477}
478
479#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)]
490#[serde(rename_all = "snake_case")]
491pub enum IoBackend {
492 Mmap,
494 IoUring,
496}
497
498impl IoBackend {
499 pub fn from_universal_kind(kind: crate::common::universal_io::UniversalKind) -> Option<Self> {
501 match kind {
502 crate::common::universal_io::UniversalKind::Mmap => Some(Self::Mmap),
503 crate::common::universal_io::UniversalKind::IoUring => Some(Self::IoUring),
504 crate::common::universal_io::UniversalKind::DiskCache
505 | crate::common::universal_io::UniversalKind::SimpleDiskCache
506 | crate::common::universal_io::UniversalKind::S3
507 | crate::common::universal_io::UniversalKind::Gcs
508 | crate::common::universal_io::UniversalKind::Azure
509 | crate::common::universal_io::UniversalKind::UioGrpc => None,
510 }
511 }
512}
513
514#[derive(Debug, Serialize, JsonSchema, Clone, PartialEq, Eq)]
515#[serde(rename_all = "snake_case")]
516pub struct VectorDataInfo {
517 pub num_vectors: usize,
518 pub num_indexed_vectors: usize,
519 pub num_deleted_vectors: usize,
520 #[serde(skip_serializing_if = "Option::is_none")]
523
524 pub io_backend: Option<IoBackend>,
525}
526
527#[derive(Debug, Serialize, JsonSchema, Clone, PartialEq, Eq)]
529#[serde(rename_all = "snake_case")]
530pub struct SegmentInfo {
531 pub uuid: Uuid,
532 pub segment_type: SegmentType,
533 pub num_vectors: usize,
534 pub num_points: usize,
535 pub num_deferred_points: Option<usize>,
536 pub num_deleted_deferred_points: Option<usize>,
537 pub num_indexed_vectors: usize,
538 pub num_deleted_vectors: usize,
539 pub vectors_size_bytes: usize,
542 pub payloads_size_bytes: usize,
544 pub ram_usage_bytes: usize,
545 pub disk_usage_bytes: usize,
546 pub is_appendable: bool,
547 pub index_schema: HashMap<PayloadKeyType, PayloadIndexInfo>,
548 pub vector_data: HashMap<String, VectorDataInfo>,
549 #[serde(skip_serializing_if = "Option::is_none")]
552
553 pub payload_storage_io_backend: Option<IoBackend>,
554 #[serde(skip_serializing_if = "Option::is_none")]
557
558 pub deferred_internal_id: Option<PointOffsetType>,
559}
560
561#[derive(Debug, Default)]
562pub struct SizeStats {
563 pub num_vectors: usize,
564 pub num_vectors_by_name: TinyMap<VectorNameBuf, usize>,
565 pub vectors_size_bytes: usize,
566 pub payloads_size_bytes: usize,
567 pub num_points: usize,
568}
569
570#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, Copy, PartialEq, Default)]
572#[serde(rename_all = "snake_case")]
573pub struct QuantizationSearchParams {
574 #[serde(default = "default_quantization_ignore_value")]
576 pub ignore: bool,
577
578 #[serde(default)]
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub rescore: Option<bool>,
584
585 #[serde(default = "default_quantization_oversampling_value")]
593 #[validate(range(min = 1.0))]
594 #[serde(skip_serializing_if = "Option::is_none")]
595 pub oversampling: Option<f64>,
596}
597
598impl Hash for QuantizationSearchParams {
599 fn hash<H: Hasher>(&self, state: &mut H) {
600 let Self {
601 ignore,
602 rescore,
603 oversampling,
604 } = self;
605 ignore.hash(state);
606 rescore.hash(state);
607 oversampling.map(OrderedFloat).hash(state);
608 }
609}
610
611pub const fn default_quantization_ignore_value() -> bool {
612 false
613}
614
615pub const fn default_quantization_oversampling_value() -> Option<f64> {
616 None
617}
618
619pub const ACORN_MAX_SELECTIVITY_DEFAULT: f64 = 0.4;
623
624#[derive(
626 Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, Copy, PartialEq, Default, Hash,
627)]
628#[serde(rename_all = "snake_case")]
629pub struct AcornSearchParams {
630 #[serde(default)]
636 pub enable: bool,
637
638 #[serde(default)]
646 #[serde(skip_serializing_if = "Option::is_none")]
647 #[validate(range(min = 0.0, max = 1.0))]
648 pub max_selectivity: Option<OrderedFloat<f64>>,
649}
650
651#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
653#[serde(rename_all = "snake_case")]
654pub struct SearchParams {
655 #[serde(skip_serializing_if = "Option::is_none")]
658 #[validate(range(min = 1))]
659 pub hnsw_ef: Option<usize>,
660
661 #[serde(default)]
663 pub exact: bool,
664
665 #[serde(default)]
667 #[validate(nested)]
668 #[serde(skip_serializing_if = "Option::is_none")]
669 pub quantization: Option<QuantizationSearchParams>,
670
671 #[serde(default)]
675 pub indexed_only: bool,
676
677 #[serde(default)]
679 #[validate(nested)]
680 #[serde(skip_serializing_if = "Option::is_none")]
681 pub acorn: Option<AcornSearchParams>,
682
683 #[serde(default)]
687 #[validate(nested)]
688 #[serde(skip_serializing_if = "Option::is_none")]
689 pub idf: Option<IdfParams>,
690}
691
692#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
700#[serde(untagged)]
701pub enum IdfParams {
702 Scope(IdfScope),
703 Corpus(IdfCorpusParams),
704}
705
706#[derive(Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq, Hash)]
708#[serde(rename_all = "snake_case")]
709pub enum IdfScope {
710 Global,
712}
713
714#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
716#[serde(rename_all = "snake_case")]
717pub struct IdfCorpusParams {
718 pub corpus: Filter,
721}
722
723impl IdfParams {
724 pub fn corpus(&self) -> Option<&Filter> {
726 match self {
727 IdfParams::Scope(IdfScope::Global) => None,
728 IdfParams::Corpus(IdfCorpusParams { corpus }) => Some(corpus),
729 }
730 }
731}
732
733impl Validate for IdfParams {
734 fn validate(&self) -> Result<(), ValidationErrors> {
735 match self {
736 IdfParams::Scope(IdfScope::Global) => Ok(()),
737 IdfParams::Corpus(corpus_params) => corpus_params.validate(),
738 }
739 }
740}
741
742impl Validate for IdfCorpusParams {
743 fn validate(&self) -> Result<(), ValidationErrors> {
744 let IdfCorpusParams { corpus } = self;
745 corpus.validate()
746 }
747}
748
749#[derive(Debug, Deserialize, Validate, Clone, PartialEq, Eq)]
751pub struct VectorsConfigDefaults {
752 #[serde(default)]
754 #[deprecated(since = "1.19.0", note = "Use `memory` instead")]
755 pub on_disk: Option<bool>,
756 #[serde(default)]
760 #[validate(custom(function = "validate_dense_vector_memory"))]
761 pub memory: Option<Memory>,
762}
763
764fn validate_dense_vector_memory(memory: &Memory) -> Result<(), ValidationError> {
767 match memory {
768 Memory::Cold | Memory::Cached => Ok(()),
769 Memory::Pinned => {
770 let mut error = ValidationError::new("unsupported_memory_placement");
771 error.message = Some(Cow::from(
772 "`pinned` memory placement is not supported for dense vector storage",
773 ));
774 Err(error)
775 }
776 }
777}
778
779#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
781#[serde(rename_all = "snake_case")]
782#[serde(tag = "type", content = "options")]
783pub enum Indexes {
784 Plain {},
787 Hnsw(HnswConfig),
790}
791
792impl Indexes {
793 pub fn is_indexed(&self) -> bool {
794 match self {
795 Indexes::Plain {} => false,
796 Indexes::Hnsw(_) => true,
797 }
798 }
799
800 pub fn is_on_disk(&self) -> bool {
801 match self {
802 Indexes::Plain {} => false,
803 Indexes::Hnsw(config) => config.memory_placement().is_on_disk(),
804 }
805 }
806}
807
808#[derive(
810 Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize, JsonSchema, Validate,
811)]
812#[serde(rename_all = "snake_case")]
813
814pub struct HnswConfig {
815 pub m: usize,
817 #[validate(range(min = 4))]
819 pub ef_construct: usize,
820 #[serde(alias = "full_scan_threshold_kb")]
827 pub full_scan_threshold: usize,
828 #[serde(default = "default_max_indexing_threads")]
833 pub max_indexing_threads: usize,
834 #[serde(default, skip_serializing_if = "Option::is_none")] #[deprecated(since = "1.19.0", note = "Use `memory` instead")]
838 pub on_disk: Option<bool>,
839 #[serde(default, skip_serializing_if = "Option::is_none")] pub memory: Option<Memory>,
843 #[serde(default, skip_serializing_if = "Option::is_none")] pub payload_m: Option<usize>,
846 #[serde(default, skip_serializing_if = "Option::is_none")]
851 pub inline_storage: Option<bool>,
852}
853
854impl HnswConfig {
855 pub fn mismatch_requires_rebuild(&self, other: &Self) -> bool {
864 let HnswConfig {
865 m,
866 ef_construct,
867 full_scan_threshold,
868 max_indexing_threads: _,
869 payload_m,
870 on_disk: _,
873 memory: _,
874 inline_storage,
875 } = *self;
876
877 m != other.m
878 || ef_construct != other.ef_construct
879 || full_scan_threshold != other.full_scan_threshold
880 || payload_m != other.payload_m
881 || self.memory_placement() != other.memory_placement()
885 || inline_storage != other.inline_storage
886 }
887
888 pub fn memory_placement(&self) -> Memory {
891 Memory::resolve(self.memory, self.on_disk.map(Memory::from_on_disk))
892 .unwrap_or(Memory::Cached)
893 }
894}
895
896#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone)]
897#[serde(rename_all = "snake_case", default)]
898
899pub struct HnswGlobalConfig {
900 #[validate(range(min = 0.0, max = 1.0))]
903 pub healing_threshold: f64,
904}
905
906impl Default for HnswGlobalConfig {
907 fn default() -> Self {
908 Self {
909 healing_threshold: 0.3,
910 }
911 }
912}
913
914const fn default_max_indexing_threads() -> usize {
915 0
916}
917
918#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash)]
919#[serde(rename_all = "lowercase")]
920pub enum CompressionRatio {
921 X4,
922 X8,
923 X16,
924 X32,
925 X64,
926}
927
928#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema)]
929#[serde(rename_all = "lowercase")]
930pub enum ScalarType {
931 #[default]
932 Int8,
933}
934
935#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Validate)]
936#[serde(rename_all = "snake_case")]
937pub struct ScalarQuantizationConfig {
938 pub r#type: ScalarType,
941 #[serde(skip_serializing_if = "Option::is_none")]
943 #[validate(range(min = 0.5, max = 1.0))]
944 pub quantile: Option<f32>,
945 #[serde(skip_serializing_if = "Option::is_none")]
948 #[deprecated(since = "1.19.0", note = "Use `memory` instead")]
949 pub always_ram: Option<bool>,
950 #[serde(default, skip_serializing_if = "Option::is_none")]
953 pub memory: Option<Memory>,
954}
955
956impl ScalarQuantizationConfig {
957 pub fn mismatch_requires_rebuild(&self, other: &Self) -> bool {
963 self != other
964 }
965
966 pub fn memory_placement(&self) -> Option<Memory> {
969 Memory::resolve(self.memory, legacy_always_ram_placement(self.always_ram))
970 }
971}
972
973fn legacy_always_ram_placement(always_ram: Option<bool>) -> Option<Memory> {
976 always_ram.and_then(|always_ram| always_ram.then_some(Memory::Pinned))
977}
978
979#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
980pub struct ScalarQuantization {
981 #[validate(nested)]
982 pub scalar: ScalarQuantizationConfig,
983}
984
985#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
986#[serde(rename_all = "snake_case")]
987pub struct ProductQuantizationConfig {
988 pub compression: CompressionRatio,
989
990 #[serde(skip_serializing_if = "Option::is_none")]
992 #[deprecated(since = "1.19.0", note = "Use `memory` instead")]
993 pub always_ram: Option<bool>,
994
995 #[serde(default, skip_serializing_if = "Option::is_none")]
998 pub memory: Option<Memory>,
999}
1000
1001impl ProductQuantizationConfig {
1002 pub fn mismatch_requires_rebuild(&self, other: &Self) -> bool {
1008 self != other
1009 }
1010
1011 pub fn memory_placement(&self) -> Option<Memory> {
1014 Memory::resolve(self.memory, legacy_always_ram_placement(self.always_ram))
1015 }
1016}
1017
1018#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
1019pub struct ProductQuantization {
1020 #[validate(nested)]
1021 pub product: ProductQuantizationConfig,
1022}
1023
1024impl Hash for ScalarQuantizationConfig {
1025 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1026 self.always_ram.hash(state);
1027 self.memory.hash(state);
1028 self.r#type.hash(state);
1029 }
1030}
1031
1032impl Eq for ScalarQuantizationConfig {}
1033
1034#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash, Default)]
1035#[serde(rename_all = "snake_case")]
1036pub enum BinaryQuantizationEncoding {
1037 #[default]
1038 OneBit,
1039 TwoBits,
1040 OneAndHalfBits,
1041}
1042
1043#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
1044#[serde(rename_all = "snake_case")]
1045pub struct BinaryQuantizationConfig {
1046 #[serde(skip_serializing_if = "Option::is_none")]
1048 #[deprecated(since = "1.19.0", note = "Use `memory` instead")]
1049 pub always_ram: Option<bool>,
1050 #[serde(default, skip_serializing_if = "Option::is_none")]
1053 pub memory: Option<Memory>,
1054 #[serde(default)]
1055 #[serde(skip_serializing_if = "Option::is_none")]
1056 pub encoding: Option<BinaryQuantizationEncoding>,
1057
1058 #[serde(default)]
1061 #[serde(skip_serializing_if = "Option::is_none")]
1062 pub query_encoding: Option<BinaryQuantizationQueryEncoding>,
1063}
1064
1065impl BinaryQuantizationConfig {
1066 pub fn memory_placement(&self) -> Option<Memory> {
1069 Memory::resolve(self.memory, legacy_always_ram_placement(self.always_ram))
1070 }
1071}
1072
1073#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
1074pub struct BinaryQuantization {
1075 #[validate(nested)]
1076 pub binary: BinaryQuantizationConfig,
1077}
1078
1079#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash, Default)]
1080#[serde(rename_all = "snake_case")]
1081pub enum TurboQuantBitSize {
1082 Bits1,
1083 Bits1_5,
1084 Bits2,
1085 #[default]
1086 Bits4,
1087}
1088
1089#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
1090#[serde(rename_all = "snake_case")]
1091pub struct TurboQuantQuantizationConfig {
1092 #[serde(skip_serializing_if = "Option::is_none")]
1094 #[deprecated(since = "1.19.0", note = "Use `memory` instead")]
1095 pub always_ram: Option<bool>,
1096 #[serde(default, skip_serializing_if = "Option::is_none")]
1099 pub memory: Option<Memory>,
1100
1101 #[serde(default)]
1102 #[serde(skip_serializing_if = "Option::is_none")]
1103 pub bits: Option<TurboQuantBitSize>,
1104}
1105
1106impl TurboQuantQuantizationConfig {
1107 pub fn memory_placement(&self) -> Option<Memory> {
1110 Memory::resolve(self.memory, legacy_always_ram_placement(self.always_ram))
1111 }
1112}
1113
1114#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, Validate)]
1115pub struct TurboQuantization {
1116 #[validate(nested)]
1117 pub turbo: TurboQuantQuantizationConfig,
1118}
1119
1120#[derive(Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema, )]
1121#[serde(untagged, rename_all = "snake_case")]
1122
1123pub enum QuantizationConfig {
1124 Scalar(ScalarQuantization),
1125 Product(ProductQuantization),
1126 Binary(BinaryQuantization),
1127 Turbo(TurboQuantization),
1128}
1129
1130impl QuantizationConfig {
1131 pub fn for_appendable_segment(opt: Option<&Self>) -> Option<Self> {
1134 let appendable = crate::common::flags::feature_flags().appendable_quantization;
1135 opt.filter(|q| appendable && q.supports_appendable())
1136 .cloned()
1137 }
1138
1139 pub fn mismatch_requires_rebuild(&self, other: &Self) -> bool {
1145 self != other
1146 }
1147
1148 pub fn supports_appendable(&self) -> bool {
1149 matches!(
1150 self,
1151 QuantizationConfig::Binary(_) | QuantizationConfig::Turbo(_)
1152 )
1153 }
1154
1155 pub fn always_ram(&self) -> bool {
1156 match self {
1157 QuantizationConfig::Scalar(s) => s.scalar.always_ram == Some(true),
1158 QuantizationConfig::Product(p) => p.product.always_ram == Some(true),
1159 QuantizationConfig::Binary(b) => b.binary.always_ram == Some(true),
1160 QuantizationConfig::Turbo(t) => t.turbo.always_ram == Some(true),
1161 }
1162 }
1163
1164 pub fn memory_placement(&self) -> Option<Memory> {
1169 match self {
1170 QuantizationConfig::Scalar(s) => s.scalar.memory_placement(),
1171 QuantizationConfig::Product(p) => p.product.memory_placement(),
1172 QuantizationConfig::Binary(b) => b.binary.memory_placement(),
1173 QuantizationConfig::Turbo(t) => t.turbo.memory_placement(),
1174 }
1175 }
1176}
1177
1178impl Validate for QuantizationConfig {
1179 fn validate(&self) -> Result<(), ValidationErrors> {
1180 match self {
1181 QuantizationConfig::Scalar(scalar) => scalar.validate(),
1182 QuantizationConfig::Product(product) => product.validate(),
1183 QuantizationConfig::Binary(binary) => binary.validate(),
1184 QuantizationConfig::Turbo(turbo) => turbo.validate(),
1185 }
1186 }
1187}
1188
1189#[derive(
1190 Default, Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash,
1191)]
1192#[serde(rename_all = "lowercase")]
1193
1194pub enum BinaryQuantizationQueryEncoding {
1195 #[default]
1196 Default,
1197 Binary,
1198 Scalar4Bits,
1199 Scalar8Bits,
1200}
1201
1202impl From<ScalarQuantizationConfig> for QuantizationConfig {
1203 fn from(config: ScalarQuantizationConfig) -> Self {
1204 QuantizationConfig::Scalar(ScalarQuantization { scalar: config })
1205 }
1206}
1207
1208impl From<ProductQuantizationConfig> for QuantizationConfig {
1209 fn from(config: ProductQuantizationConfig) -> Self {
1210 QuantizationConfig::Product(ProductQuantization { product: config })
1211 }
1212}
1213
1214impl From<BinaryQuantizationConfig> for QuantizationConfig {
1215 fn from(config: BinaryQuantizationConfig) -> Self {
1216 QuantizationConfig::Binary(BinaryQuantization { binary: config })
1217 }
1218}
1219
1220#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
1221pub struct StrictModeSparse {
1222 #[serde(skip_serializing_if = "Option::is_none")]
1224 #[validate(range(min = 1))]
1225 pub max_length: Option<usize>,
1226}
1227
1228#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
1229#[schemars(deny_unknown_fields)]
1230pub struct StrictModeSparseConfig {
1231 #[validate(nested)]
1232 #[serde(flatten)]
1233 pub config: BTreeMap<VectorNameBuf, StrictModeSparse>,
1234}
1235
1236#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Default)]
1237#[schemars(deny_unknown_fields)]
1238pub struct StrictModeSparseConfigOutput {
1239 #[serde(flatten)]
1240 pub config: BTreeMap<VectorNameBuf, StrictModeSparseOutput>,
1241}
1242
1243#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Default)]
1244pub struct StrictModeSparseOutput {
1245 #[serde(skip_serializing_if = "Option::is_none")]
1247
1248 pub max_length: Option<usize>,
1249}
1250
1251impl From<StrictModeSparseConfig> for StrictModeSparseConfigOutput {
1252 fn from(config: StrictModeSparseConfig) -> Self {
1253 let StrictModeSparseConfig { config } = config;
1254 let mut new_config = StrictModeSparseConfigOutput::default();
1255 for (key, value) in config {
1256 new_config
1257 .config
1258 .insert(key, StrictModeSparseOutput::from(value));
1259 }
1260 new_config
1261 }
1262}
1263
1264impl From<StrictModeSparse> for StrictModeSparseOutput {
1265 fn from(config: StrictModeSparse) -> Self {
1266 let StrictModeSparse { max_length } = config;
1267 StrictModeSparseOutput { max_length }
1268 }
1269}
1270
1271#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
1272pub struct StrictModeMultivector {
1273 #[serde(skip_serializing_if = "Option::is_none")]
1275 #[validate(range(min = 1))]
1276 pub max_vectors: Option<usize>,
1277}
1278
1279#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default, Hash)]
1280#[schemars(deny_unknown_fields)]
1281pub struct StrictModeMultivectorConfig {
1282 #[validate(nested)]
1283 #[serde(flatten)]
1284 pub config: BTreeMap<VectorNameBuf, StrictModeMultivector>,
1285}
1286
1287#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Default)]
1288#[schemars(deny_unknown_fields)]
1289pub struct StrictModeMultivectorConfigOutput {
1290 #[serde(flatten)]
1291 pub config: BTreeMap<VectorNameBuf, StrictModeMultivectorOutput>,
1292}
1293
1294impl From<StrictModeMultivectorConfig> for StrictModeMultivectorConfigOutput {
1295 fn from(config: StrictModeMultivectorConfig) -> Self {
1296 let StrictModeMultivectorConfig { config } = config;
1297 let mut new_config = StrictModeMultivectorConfigOutput::default();
1298 for (key, value) in config {
1299 new_config
1300 .config
1301 .insert(key, StrictModeMultivectorOutput::from(value));
1302 }
1303 new_config
1304 }
1305}
1306
1307#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Default)]
1308pub struct StrictModeMultivectorOutput {
1309 #[serde(skip_serializing_if = "Option::is_none")]
1311
1312 pub max_vectors: Option<usize>,
1313}
1314
1315impl From<StrictModeMultivector> for StrictModeMultivectorOutput {
1316 fn from(config: StrictModeMultivector) -> Self {
1317 let StrictModeMultivector { max_vectors } = config;
1318 StrictModeMultivectorOutput { max_vectors }
1319 }
1320}
1321
1322#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Default)]
1323pub struct StrictModeConfig {
1324 #[serde(skip_serializing_if = "Option::is_none")]
1327 pub enabled: Option<bool>,
1328
1329 #[serde(skip_serializing_if = "Option::is_none")]
1331 #[validate(range(min = 1))]
1332 pub max_query_limit: Option<usize>,
1333
1334 #[serde(skip_serializing_if = "Option::is_none")]
1336 #[validate(range(min = 1))]
1337 pub max_timeout: Option<usize>,
1338
1339 #[serde(skip_serializing_if = "Option::is_none")]
1341 pub unindexed_filtering_retrieve: Option<bool>,
1342
1343 #[serde(skip_serializing_if = "Option::is_none")]
1345 pub unindexed_filtering_update: Option<bool>,
1346
1347 #[serde(skip_serializing_if = "Option::is_none")]
1350 pub search_max_hnsw_ef: Option<usize>,
1351
1352 #[serde(skip_serializing_if = "Option::is_none")]
1354 pub search_allow_exact: Option<bool>,
1355
1356 #[serde(skip_serializing_if = "Option::is_none")]
1358 pub search_max_oversampling: Option<f64>,
1359
1360 #[serde(skip_serializing_if = "Option::is_none")]
1362 pub upsert_max_batchsize: Option<usize>,
1363
1364 #[serde(skip_serializing_if = "Option::is_none")]
1366 pub search_max_batchsize: Option<usize>,
1367
1368 #[serde(skip_serializing_if = "Option::is_none")]
1370 pub max_collection_vector_size_bytes: Option<usize>,
1371
1372 #[serde(skip_serializing_if = "Option::is_none")]
1374 #[validate(range(min = 1))]
1375 pub read_rate_limit: Option<usize>,
1376
1377 #[serde(skip_serializing_if = "Option::is_none")]
1379 #[validate(range(min = 1))]
1380 pub write_rate_limit: Option<usize>,
1381
1382 #[serde(skip_serializing_if = "Option::is_none")]
1384 pub max_collection_payload_size_bytes: Option<usize>,
1385
1386 #[serde(skip_serializing_if = "Option::is_none")]
1388 #[validate(range(min = 1))]
1389 pub max_points_count: Option<usize>,
1390
1391 #[serde(skip_serializing_if = "Option::is_none")]
1393 pub filter_max_conditions: Option<usize>,
1394
1395 #[serde(skip_serializing_if = "Option::is_none")]
1397 pub condition_max_size: Option<usize>,
1398
1399 #[serde(skip_serializing_if = "Option::is_none")]
1401 #[validate(nested)]
1402 pub multivector_config: Option<StrictModeMultivectorConfig>,
1403
1404 #[serde(skip_serializing_if = "Option::is_none")]
1406 #[validate(nested)]
1407 pub sparse_config: Option<StrictModeSparseConfig>,
1408
1409 #[serde(skip_serializing_if = "Option::is_none")]
1411 #[validate(range(min = 0))]
1412 pub max_payload_index_count: Option<usize>,
1413
1414 #[deprecated(
1425 since = "1.19.0",
1426 note = "memory is node-wide: use the global quota config instead. Removal planned for 1.21"
1427 )]
1428 #[serde(skip_serializing_if = "Option::is_none")]
1429 #[validate(range(min = 1, max = 100))]
1430 pub max_resident_memory_percent: Option<u8>,
1431}
1432
1433impl Eq for StrictModeConfig {}
1434
1435impl Hash for StrictModeConfig {
1436 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1437 let Self {
1438 enabled,
1439 max_query_limit,
1440 max_timeout,
1441 unindexed_filtering_retrieve,
1442 unindexed_filtering_update,
1443 search_max_hnsw_ef,
1444 search_allow_exact,
1445 search_max_oversampling: _,
1447 upsert_max_batchsize,
1448 search_max_batchsize,
1449 max_collection_vector_size_bytes,
1450 read_rate_limit,
1451 write_rate_limit,
1452 max_collection_payload_size_bytes,
1453 max_points_count,
1454 filter_max_conditions,
1455 condition_max_size,
1456 multivector_config,
1457 sparse_config,
1458 max_payload_index_count,
1459 max_resident_memory_percent,
1460 } = self;
1461 enabled.hash(state);
1462 max_query_limit.hash(state);
1463 max_timeout.hash(state);
1464 unindexed_filtering_retrieve.hash(state);
1465 unindexed_filtering_update.hash(state);
1466 search_max_hnsw_ef.hash(state);
1467 search_allow_exact.hash(state);
1468 upsert_max_batchsize.hash(state);
1469 search_max_batchsize.hash(state);
1470 max_collection_vector_size_bytes.hash(state);
1471 read_rate_limit.hash(state);
1472 write_rate_limit.hash(state);
1473 max_collection_payload_size_bytes.hash(state);
1474 max_points_count.hash(state);
1475 filter_max_conditions.hash(state);
1476 condition_max_size.hash(state);
1477 multivector_config.hash(state);
1478 sparse_config.hash(state);
1479 max_payload_index_count.hash(state);
1480 max_resident_memory_percent.hash(state);
1481 }
1482}
1483
1484#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Default)]
1486pub struct StrictModeConfigOutput {
1487 #[serde(skip_serializing_if = "Option::is_none")]
1490 pub enabled: Option<bool>,
1491
1492 #[serde(skip_serializing_if = "Option::is_none")]
1494
1495 pub max_query_limit: Option<usize>,
1496
1497 #[serde(skip_serializing_if = "Option::is_none")]
1499
1500 pub max_timeout: Option<usize>,
1501
1502 #[serde(skip_serializing_if = "Option::is_none")]
1504 pub unindexed_filtering_retrieve: Option<bool>,
1505
1506 #[serde(skip_serializing_if = "Option::is_none")]
1508 pub unindexed_filtering_update: Option<bool>,
1509
1510 #[serde(skip_serializing_if = "Option::is_none")]
1513
1514 pub search_max_hnsw_ef: Option<usize>,
1515
1516 #[serde(skip_serializing_if = "Option::is_none")]
1518 pub search_allow_exact: Option<bool>,
1519
1520 #[serde(skip_serializing_if = "Option::is_none")]
1522
1523 pub search_max_oversampling: Option<f64>,
1524
1525 #[serde(skip_serializing_if = "Option::is_none")]
1527
1528 pub upsert_max_batchsize: Option<usize>,
1529 #[serde(skip_serializing_if = "Option::is_none")]
1531
1532 pub search_max_batchsize: Option<usize>,
1533
1534 #[serde(skip_serializing_if = "Option::is_none")]
1536
1537 pub max_collection_vector_size_bytes: Option<usize>,
1538
1539 #[serde(skip_serializing_if = "Option::is_none")]
1541
1542 pub read_rate_limit: Option<usize>,
1543
1544 #[serde(skip_serializing_if = "Option::is_none")]
1546
1547 pub write_rate_limit: Option<usize>,
1548
1549 #[serde(skip_serializing_if = "Option::is_none")]
1551
1552 pub max_collection_payload_size_bytes: Option<usize>,
1553
1554 #[serde(skip_serializing_if = "Option::is_none")]
1556
1557 pub max_points_count: Option<usize>,
1558
1559 #[serde(skip_serializing_if = "Option::is_none")]
1561
1562 pub filter_max_conditions: Option<usize>,
1563
1564 #[serde(skip_serializing_if = "Option::is_none")]
1566
1567 pub condition_max_size: Option<usize>,
1568
1569 #[serde(skip_serializing_if = "Option::is_none")]
1571 pub multivector_config: Option<StrictModeMultivectorConfigOutput>,
1572
1573 #[serde(skip_serializing_if = "Option::is_none")]
1575 pub sparse_config: Option<StrictModeSparseConfigOutput>,
1576
1577 #[serde(skip_serializing_if = "Option::is_none")]
1579 pub max_payload_index_count: Option<usize>,
1580
1581 #[deprecated(
1583 since = "1.19.0",
1584 note = "memory is node-wide: use the global quota config instead. Removal planned for 1.21"
1585 )]
1586 #[serde(skip_serializing_if = "Option::is_none")]
1587
1588 pub max_resident_memory_percent: Option<u8>,
1589}
1590
1591impl From<StrictModeConfig> for StrictModeConfigOutput {
1592 fn from(config: StrictModeConfig) -> Self {
1593 let StrictModeConfig {
1594 enabled,
1595 max_query_limit,
1596 max_timeout,
1597 unindexed_filtering_retrieve,
1598 unindexed_filtering_update,
1599 search_max_hnsw_ef,
1600 search_allow_exact,
1601 search_max_oversampling,
1602 upsert_max_batchsize,
1603 search_max_batchsize,
1604 max_collection_vector_size_bytes,
1605 read_rate_limit,
1606 write_rate_limit,
1607 max_collection_payload_size_bytes,
1608 max_points_count,
1609 filter_max_conditions,
1610 condition_max_size,
1611 multivector_config,
1612 sparse_config,
1613 max_payload_index_count,
1614 max_resident_memory_percent,
1615 } = config;
1616
1617 Self {
1618 enabled,
1619 max_query_limit,
1620 max_timeout,
1621 unindexed_filtering_retrieve,
1622 unindexed_filtering_update,
1623 search_max_hnsw_ef,
1624 search_allow_exact,
1625 search_max_oversampling,
1626 upsert_max_batchsize,
1627 search_max_batchsize,
1628 max_collection_vector_size_bytes,
1629 read_rate_limit,
1630 write_rate_limit,
1631 max_collection_payload_size_bytes,
1632 max_points_count,
1633 filter_max_conditions,
1634 condition_max_size,
1635 multivector_config: multivector_config.map(StrictModeMultivectorConfigOutput::from),
1636 sparse_config: sparse_config.map(StrictModeSparseConfigOutput::from),
1637 max_payload_index_count,
1638 max_resident_memory_percent,
1639 }
1640 }
1641}
1642
1643pub const DEFAULT_HNSW_EF_CONSTRUCT: usize = 100;
1644
1645impl Default for HnswConfig {
1646 fn default() -> Self {
1647 HnswConfig {
1648 m: 16,
1649 ef_construct: DEFAULT_HNSW_EF_CONSTRUCT,
1650 full_scan_threshold: DEFAULT_FULL_SCAN_THRESHOLD,
1651 max_indexing_threads: 0,
1652 on_disk: Some(false),
1653 memory: None,
1654 payload_m: None,
1655 inline_storage: None,
1656 }
1657 }
1658}
1659
1660impl Default for Indexes {
1661 fn default() -> Self {
1662 Indexes::Plain {}
1663 }
1664}
1665
1666#[derive( Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq)]
1668#[serde(tag = "type", content = "options", rename_all = "snake_case")]
1669pub enum PayloadStorageType {
1670 Mmap,
1672 InRamMmap,
1674}
1675
1676#[cfg(any(test, feature = "testing"))]
1677#[allow(clippy::derivable_impls)]
1678impl Default for PayloadStorageType {
1679 fn default() -> Self {
1680 PayloadStorageType::Mmap
1681 }
1682}
1683
1684impl PayloadStorageType {
1685 pub fn from_on_disk_payload(on_disk: bool) -> Self {
1688 if on_disk { Self::Mmap } else { Self::InRamMmap }
1689 }
1690
1691 pub fn from_memory(memory: Memory) -> Self {
1696 match memory {
1697 Memory::Cold => Self::Mmap,
1698 Memory::Cached | Memory::Pinned => Self::InRamMmap,
1699 }
1700 }
1701
1702 pub fn memory(&self) -> Memory {
1704 match self {
1705 PayloadStorageType::Mmap => Memory::Cold,
1706 PayloadStorageType::InRamMmap => Memory::Cached,
1707 }
1708 }
1709
1710 pub fn is_on_disk(&self) -> bool {
1711 match self {
1712 PayloadStorageType::Mmap => true,
1713 PayloadStorageType::InRamMmap => false,
1714 }
1715 }
1716}
1717
1718#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, )]
1719#[serde(rename_all = "snake_case")]
1720pub struct SegmentConfig {
1721 #[serde(default)]
1722 pub vector_data: HashMap<VectorNameBuf, VectorDataConfig>,
1723 #[serde(default)]
1724 #[serde(skip_serializing_if = "HashMap::is_empty")]
1725 pub sparse_vector_data: HashMap<VectorNameBuf, SparseVectorDataConfig>,
1726 pub payload_storage_type: PayloadStorageType,
1728}
1729
1730impl SegmentConfig {
1731 pub fn quantization_config(&self, vector_name: &VectorName) -> Option<&QuantizationConfig> {
1737 self.vector_data
1738 .get(vector_name)
1739 .and_then(|v| v.quantization_config.as_ref())
1740 }
1741
1742 pub fn is_any_vector_indexed(&self) -> bool {
1744 self.vector_data
1745 .values()
1746 .any(|config| config.index.is_indexed())
1747 || self
1748 .sparse_vector_data
1749 .values()
1750 .any(|config| config.is_indexed())
1751 }
1752
1753 pub fn is_any_on_disk(&self) -> bool {
1755 self.vector_data
1756 .values()
1757 .any(|config| config.storage_type.is_on_disk())
1758 || self
1759 .sparse_vector_data
1760 .values()
1761 .any(|config| config.index.index_type.is_on_disk())
1762 }
1763
1764 pub fn is_appendable(&self) -> bool {
1765 self.vector_data
1766 .values()
1767 .map(|vector_config| vector_config.is_appendable())
1768 .chain(
1769 self.sparse_vector_data
1770 .values()
1771 .map(|sparse_vector_config| {
1772 sparse_vector_config.index.index_type.is_appendable()
1773 }),
1774 )
1775 .all(|v| v)
1776 }
1777
1778 pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
1779 let Self {
1786 vector_data: _,
1787 sparse_vector_data: _,
1788 payload_storage_type: _,
1789 } = self;
1790
1791 check_vectors_map_compatible(
1792 &self.vector_data,
1793 &other.vector_data,
1794 VectorDataConfig::check_compatible,
1795 )?;
1796
1797 check_vectors_map_compatible(
1798 &self.sparse_vector_data,
1799 &other.sparse_vector_data,
1800 SparseVectorDataConfig::check_compatible,
1801 )?;
1802
1803 Ok(())
1804 }
1805}
1806
1807fn check_vectors_map_compatible<C, F>(
1808 this: &HashMap<String, C>,
1809 other: &HashMap<String, C>,
1810 check: F,
1811) -> Result<(), String>
1812where
1813 F: Fn(&C, &C) -> Result<(), String>,
1814{
1815 if this.len() != other.len() {
1816 let expected_keys: Vec<String> = this.keys().map(|k| format!("{k:?}")).collect();
1817 let actual_keys: Vec<String> = other.keys().map(|k| format!("{k:?}")).collect();
1818 return Err(format!(
1819 "Incompatible configs: expected vector storages with keys {expected_keys:?}, but got {actual_keys:?}"
1820 ));
1821 }
1822
1823 for (vector_name, config) in this {
1824 let Some(other_config) = other.get(vector_name) else {
1825 return Err(format!(
1826 "Incompatible configs: expected vector storage with key {vector_name:?} not found in other config"
1827 ));
1828 };
1829
1830 check(config, other_config)
1831 .map_err(|err| format!("Incompatible config for vector {vector_name:?}: {err}"))?;
1832 }
1833
1834 Ok(())
1835}
1836
1837#[derive(
1856 Debug, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone, Hash,
1857)]
1858#[serde(rename_all = "snake_case")]
1859pub enum Memory {
1860 Cold,
1864 Cached,
1868 Pinned,
1872}
1873
1874impl Memory {
1875 pub fn from_on_disk(on_disk: bool) -> Self {
1878 if on_disk { Self::Cold } else { Self::Cached }
1879 }
1880
1881 pub fn from_on_disk_heap(on_disk: bool) -> Self {
1885 if on_disk { Self::Cold } else { Self::Pinned }
1886 }
1887
1888 pub fn is_on_disk(self) -> bool {
1890 match self {
1891 Self::Cold => true,
1892 Self::Cached | Self::Pinned => false,
1893 }
1894 }
1895
1896 pub fn is_cold(self) -> bool {
1899 match self {
1900 Self::Cold => true,
1901 Self::Cached | Self::Pinned => false,
1902 }
1903 }
1904
1905 pub fn is_heap(self) -> bool {
1908 match self {
1909 Self::Pinned => true,
1910 Self::Cold | Self::Cached => false,
1911 }
1912 }
1913
1914 pub fn populate_on_open(self) -> bool {
1917 match self {
1918 Self::Cold => false,
1919 Self::Cached | Self::Pinned => true,
1920 }
1921 }
1922
1923 pub fn resolve(memory: Option<Self>, legacy: Option<Self>) -> Option<Self> {
1926 memory.or(legacy)
1927 }
1928
1929 pub fn resolve_or_warn(
1934 memory: Option<Self>,
1935 legacy: Option<Self>,
1936 component: &dyn std::fmt::Display,
1937 ) -> Option<Self> {
1938 if let (Some(memory), Some(legacy)) = (memory, legacy)
1939 && memory != legacy
1940 {
1941 log::warn!(
1942 "Component {component} has both `memory={memory:?}` and a deprecated storage \
1943 placement parameter implying {legacy:?} configured; using `memory={memory:?}`"
1944 );
1945 }
1946 Self::resolve(memory, legacy)
1947 }
1948
1949 pub fn clamp_to_low_memory(self) -> Self {
1957 let mode = crate::common::low_memory::low_memory_mode();
1958 if mode.skip_populate() {
1959 return Self::Cold;
1960 }
1961 if mode.prefer_disk() {
1962 return match self {
1963 Self::Pinned => Self::Cold,
1964 Self::Cold | Self::Cached => self,
1965 };
1966 }
1967 self
1968 }
1969
1970 pub fn with_populate_override(
1979 self,
1980 populate_override: Option<crate::common::universal_io::Populate>,
1981 ) -> Self {
1982 use crate::common::universal_io::Populate;
1983
1984 let Some(populate) = populate_override else {
1985 return self;
1986 };
1987 match populate {
1988 Populate::No | Populate::Auto | Populate::Partial(_) => Self::Cold,
1989 Populate::Blocking | Populate::PreferBackground => match self {
1990 Self::Cold | Self::Cached => Self::Cached,
1991 Self::Pinned => Self::Pinned,
1992 },
1993 }
1994 }
1995}
1996
1997#[derive(Debug, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone)]
1999pub enum VectorStorageType {
2000 Memory,
2004 Mmap,
2008 ChunkedMmap,
2012 InRamChunkedMmap,
2017 InRamMmap,
2020 Empty,
2024}
2025
2026#[cfg(any(test, feature = "testing"))]
2027#[allow(clippy::derivable_impls)]
2028impl Default for VectorStorageType {
2029 fn default() -> Self {
2030 VectorStorageType::InRamChunkedMmap
2031 }
2032}
2033
2034#[derive(
2036 Default, Debug, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone, Hash,
2037)]
2038#[serde(rename_all = "snake_case")]
2039pub enum VectorStorageDatatype {
2040 #[default]
2042 Float32,
2043 Float16,
2045 Uint8,
2047 Turbo4,
2049}
2050
2051#[derive(
2052 Debug, Default, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone, Hash,
2053)]
2054#[serde(rename_all = "snake_case")]
2055pub struct MultiVectorConfig {
2056 pub comparator: MultiVectorComparator,
2058}
2059
2060impl MultiVectorConfig {
2061 fn check_compatible(&self, other: &Self) -> Result<(), String> {
2062 let Self { comparator } = self;
2064
2065 if *comparator != other.comparator {
2066 return Err(format!(
2067 "Incompatible configs: expected multi-vector comparator {comparator:?}, but got {other_comparator:?}",
2068 other_comparator = other.comparator
2069 ));
2070 }
2071
2072 Ok(())
2073 }
2074}
2075
2076#[derive(
2077 Debug, Default, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone, Hash,
2078)]
2079#[serde(rename_all = "snake_case")]
2080pub enum MultiVectorComparator {
2081 #[default]
2082 MaxSim,
2083}
2084
2085impl VectorStorageType {
2086 pub fn from_on_disk(on_disk: bool) -> Self {
2089 if on_disk {
2090 Self::ChunkedMmap
2091 } else {
2092 Self::InRamChunkedMmap
2093 }
2094 }
2095
2096 pub fn appendable_from_memory(memory: Memory) -> Self {
2101 match memory {
2102 Memory::Cold => Self::ChunkedMmap,
2103 Memory::Cached | Memory::Pinned => Self::InRamChunkedMmap,
2104 }
2105 }
2106
2107 pub fn immutable_from_memory(memory: Memory) -> Self {
2112 match memory {
2113 Memory::Cold => Self::Mmap,
2114 Memory::Cached | Memory::Pinned => Self::InRamMmap,
2115 }
2116 }
2117
2118 pub fn memory(&self) -> Memory {
2120 match self {
2121 Self::Memory => Memory::Pinned,
2123 Self::Mmap | Self::ChunkedMmap => Memory::Cold,
2124 Self::InRamChunkedMmap | Self::InRamMmap => Memory::Cached,
2125 Self::Empty => Memory::Cold,
2128 }
2129 }
2130
2131 pub fn is_on_disk(&self) -> bool {
2133 match self {
2134 Self::Memory | Self::InRamChunkedMmap | Self::InRamMmap => false,
2135 Self::Mmap | Self::ChunkedMmap => true,
2136 Self::Empty => true,
2141 }
2142 }
2143
2144 pub fn is_empty(&self) -> bool {
2146 matches!(self, Self::Empty)
2147 }
2148}
2149
2150#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, )]
2152#[serde(rename_all = "snake_case")]
2153pub struct VectorDataConfig {
2154 pub size: usize,
2156 pub distance: Distance,
2158 pub storage_type: VectorStorageType,
2160 pub index: Indexes,
2162 pub quantization_config: Option<QuantizationConfig>,
2164 #[serde(default, skip_serializing_if = "Option::is_none")]
2166 pub multivector_config: Option<MultiVectorConfig>,
2167 #[serde(default, skip_serializing_if = "Option::is_none")]
2169 pub datatype: Option<VectorStorageDatatype>,
2170}
2171
2172impl VectorDataConfig {
2173 pub fn is_appendable(&self) -> bool {
2177 let is_index_appendable = match self.index {
2178 Indexes::Plain {} => true,
2179 Indexes::Hnsw(_) => false,
2180 };
2181 let is_storage_appendable = match self.storage_type {
2182 VectorStorageType::Memory => true,
2183 VectorStorageType::Mmap => false,
2184 VectorStorageType::ChunkedMmap => true,
2185 VectorStorageType::InRamChunkedMmap => true,
2186 VectorStorageType::InRamMmap => false,
2187 VectorStorageType::Empty => false,
2188 };
2189 is_index_appendable && is_storage_appendable
2190 }
2191
2192 pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
2193 let Self {
2198 size,
2199 distance,
2200 storage_type: _,
2201 index: _,
2202 quantization_config: _,
2203 multivector_config,
2204 datatype,
2205 } = self;
2206
2207 if *size != other.size {
2208 return Err(format!(
2209 "Incompatible configs: expected vector size {size}, but got {other_size}",
2210 other_size = other.size
2211 ));
2212 }
2213
2214 if *distance != other.distance {
2215 return Err(format!(
2216 "Incompatible configs: expected distance {distance:?}, but got {other_distance:?}",
2217 other_distance = other.distance
2218 ));
2219 }
2220
2221 let left_datatype = datatype.unwrap_or(VectorStorageDatatype::Float32);
2222 let right_datatype = other.datatype.unwrap_or(VectorStorageDatatype::Float32);
2223 if left_datatype != right_datatype {
2224 return Err(format!(
2225 "Incompatible configs: expected vector storage datatype {left_datatype:?}, but got {right_datatype:?}",
2226 ));
2227 }
2228
2229 match (multivector_config, &other.multivector_config) {
2230 (None, None) => {}
2231 (Some(this), Some(other)) => {
2232 MultiVectorConfig::check_compatible(this, other)?;
2233 }
2234 _ => {
2235 return Err(format!(
2236 "Incompatible configs: expected multivector config {this_multivector_config:?}, but got {other_multivector_config:?}",
2237 this_multivector_config = multivector_config,
2238 other_multivector_config = other.multivector_config
2239 ));
2240 }
2241 }
2242 Ok(())
2243 }
2244}
2245
2246#[derive(
2247 Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, JsonSchema,
2248)]
2249#[serde(rename_all = "snake_case")]
2250pub enum SparseVectorStorageType {
2251 #[default]
2253 Mmap,
2254 Empty,
2257}
2258
2259impl SparseVectorStorageType {
2260 pub fn is_on_disk(&self) -> bool {
2262 match self {
2263 Self::Mmap | Self::Empty => true,
2266 }
2267 }
2268}
2269
2270#[derive(
2272 Copy, Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Validate,
2273)]
2274#[serde(rename_all = "snake_case")]
2275pub struct SparseVectorDataConfig {
2276 pub index: SparseIndexConfig,
2278
2279 #[serde(default = "default_sparse_vector_storage_type_when_not_in_config")]
2281 pub storage_type: SparseVectorStorageType,
2282
2283 #[serde(default, skip_serializing_if = "Option::is_none")]
2286 pub modifier: Option<Modifier>,
2287}
2288
2289fn default_sparse_vector_storage_type_when_not_in_config() -> SparseVectorStorageType {
2291 SparseVectorStorageType::default()
2292}
2293
2294impl SparseVectorDataConfig {
2295 pub fn is_indexed(&self) -> bool {
2296 true
2297 }
2298
2299 pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
2300 let Self {
2304 index: _,
2305 storage_type: _,
2306 modifier,
2307 } = self;
2308
2309 if modifier != &other.modifier {
2310 return Err(format!(
2311 "Incompatible configs: expected sparse vector modifier {modifier:?}, but got {other_modifier:?}",
2312 other_modifier = other.modifier
2313 ));
2314 }
2315
2316 Ok(())
2317 }
2318}
2319
2320pub const DEFAULT_FULL_SCAN_THRESHOLD: usize = 10_000;
2322
2323pub const DEFAULT_SPARSE_FULL_SCAN_THRESHOLD: usize = 5_000;
2324
2325#[derive(Debug, Deserialize, Serialize, Clone)]
2327#[serde(rename_all = "snake_case")]
2328pub struct SegmentState {
2329 #[serde(default)]
2330 pub initial_version: Option<SeqNumberType>,
2331 pub version: Option<SeqNumberType>,
2332 pub config: SegmentConfig,
2333}
2334
2335pub type RawGeoPoint = (f64, f64);
2336
2337#[derive(
2339 Debug,
2340 Deserialize,
2341 Serialize,
2342 JsonSchema,
2343 Clone,
2344 Copy,
2345 PartialEq,
2346 Eq,
2347 Hash,
2348 Default,
2349 PartialOrd,
2350 Ord,
2351 Pod,
2352 Zeroable,
2353)]
2354#[serde(try_from = "GeoPointShadow")]
2355#[repr(C)]
2356pub struct GeoPoint {
2357 pub lon: OrderedFloat<f64>,
2358 pub lat: OrderedFloat<f64>,
2359}
2360
2361#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2363pub struct GeoLineString {
2364 pub points: Vec<GeoPoint>,
2365}
2366
2367#[derive(Deserialize)]
2368struct GeoPointShadow {
2369 pub lon: f64,
2370 pub lat: f64,
2371}
2372
2373#[derive(Debug)]
2374pub struct GeoPointValidationError {
2375 pub lon: f64,
2376 pub lat: f64,
2377}
2378
2379impl std::fmt::Display for GeoPointValidationError {
2381 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2382 write!(
2383 formatter,
2384 "Wrong format of GeoPoint payload: expected `lat` = {} within [-90;90] and `lon` = {} within [-180;180]",
2385 self.lat, self.lon,
2386 )
2387 }
2388}
2389
2390impl GeoPoint {
2391 pub fn validate(lon: f64, lat: f64) -> Result<(), GeoPointValidationError> {
2392 let max_lon = 180f64;
2393 let min_lon = -180f64;
2394 let max_lat = 90f64;
2395 let min_lat = -90f64;
2396
2397 if !(min_lon..=max_lon).contains(&lon) || !(min_lat..=max_lat).contains(&lat) {
2398 return Err(GeoPointValidationError { lon, lat });
2399 }
2400 Ok(())
2401 }
2402
2403 pub fn new(lon: f64, lat: f64) -> Result<Self, GeoPointValidationError> {
2404 Self::validate(lon, lat)?;
2405 Ok(Self::new_unchecked(lon, lat))
2406 }
2407
2408 pub const fn new_unchecked(lon: f64, lat: f64) -> Self {
2409 GeoPoint {
2410 lon: OrderedFloat(lon),
2411 lat: OrderedFloat(lat),
2412 }
2413 }
2414}
2415
2416impl TryFrom<GeoPointShadow> for GeoPoint {
2417 type Error = GeoPointValidationError;
2418
2419 fn try_from(value: GeoPointShadow) -> Result<Self, Self::Error> {
2420 let GeoPointShadow { lon, lat } = value;
2421 GeoPoint::validate(lon, lat)?;
2422
2423 Ok(Self::new_unchecked(lon, lat))
2424 }
2425}
2426
2427impl From<GeoPoint> for geo::Point {
2428 fn from(
2429 GeoPoint {
2430 lon: OrderedFloat(lon),
2431 lat: OrderedFloat(lat),
2432 }: GeoPoint,
2433 ) -> Self {
2434 Self::new(lon, lat)
2435 }
2436}
2437
2438impl From<RawGeoPoint> for GeoPoint {
2439 fn from((lon, lat): RawGeoPoint) -> Self {
2440 GeoPoint::new(lon, lat).expect("invalid GeoPoint coordinates")
2441 }
2442}
2443
2444impl From<GeoPoint> for RawGeoPoint {
2445 fn from(geo_point: GeoPoint) -> Self {
2446 (geo_point.lon.0, geo_point.lat.0)
2447 }
2448}
2449
2450pub trait PayloadContainer {
2451 fn get_value(&self, path: &JsonPath) -> MultiValue<&Value>;
2454
2455 fn get_value_cloned(&self, path: &JsonPath) -> MultiValue<Value> {
2456 self.get_value(path).into_iter().cloned().collect()
2457 }
2458}
2459
2460macro_rules! payload_json {
2464 ($($tt:tt)*) => {
2465 match ::serde_json::json!( { $($tt)* } ) {
2466 ::serde_json::Value::Object(map) => $crate::segment::types::Payload(map),
2467 _ => unreachable!(),
2468 }
2469 };
2470}
2471
2472#[allow(clippy::unnecessary_wraps)] fn payload_example() -> Option<Payload> {
2474 Some(payload_json! {
2475 "city": "London",
2476 "color": "green",
2477 })
2478}
2479
2480#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize, JsonSchema, Hash)]
2481#[schemars(example = "payload_example")]
2482pub struct Payload(pub Map<String, Value>);
2483
2484impl Payload {
2485 pub fn merge(&mut self, value: &Payload) {
2486 utils::merge_map(&mut self.0, &value.0)
2487 }
2488
2489 pub fn merge_by_key(&mut self, value: &Payload, key: &JsonPath) {
2490 JsonPath::value_set(Some(key), &mut self.0, &value.0);
2491 }
2492
2493 pub fn remove(&mut self, path: &JsonPath) -> Vec<Value> {
2494 path.value_remove(&mut self.0).to_vec()
2495 }
2496
2497 pub fn len(&self) -> usize {
2498 self.0.len()
2499 }
2500
2501 pub fn is_empty(&self) -> bool {
2502 self.0.is_empty()
2503 }
2504
2505 pub fn contains_key(&self, key: &str) -> bool {
2506 self.0.contains_key(key)
2507 }
2508
2509 pub fn keys(&self) -> impl Iterator<Item = &String> {
2510 self.0.keys()
2511 }
2512}
2513
2514impl PayloadContainer for Map<String, Value> {
2515 fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2516 path.value_get(self)
2517 }
2518}
2519
2520impl PayloadContainer for Payload {
2521 fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2522 path.value_get(&self.0)
2523 }
2524}
2525
2526impl PayloadContainer for OwnedPayloadRef<'_> {
2527 fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2528 path.value_get(self.as_ref())
2529 }
2530}
2531
2532impl Default for Payload {
2533 fn default() -> Self {
2534 Payload(Map::new())
2535 }
2536}
2537
2538impl IntoIterator for Payload {
2539 type Item = (String, Value);
2540 type IntoIter = serde_json::map::IntoIter;
2541
2542 fn into_iter(self) -> serde_json::map::IntoIter {
2543 self.0.into_iter()
2544 }
2545}
2546
2547impl From<Map<String, Value>> for Payload {
2548 fn from(value: serde_json::Map<String, Value>) -> Self {
2549 Payload(value)
2550 }
2551}
2552
2553#[derive(Clone, Debug)]
2554pub enum OwnedPayloadRef<'a> {
2555 Ref(&'a Map<String, Value>),
2556 Owned(Rc<Map<String, Value>>),
2557}
2558
2559impl Deref for OwnedPayloadRef<'_> {
2560 type Target = Map<String, Value>;
2561
2562 fn deref(&self) -> &Self::Target {
2563 match self {
2564 OwnedPayloadRef::Ref(reference) => reference,
2565 OwnedPayloadRef::Owned(owned) => owned.deref(),
2566 }
2567 }
2568}
2569
2570impl AsRef<Map<String, Value>> for OwnedPayloadRef<'_> {
2571 fn as_ref(&self) -> &Map<String, Value> {
2572 match self {
2573 OwnedPayloadRef::Ref(reference) => reference,
2574 OwnedPayloadRef::Owned(owned) => owned.deref(),
2575 }
2576 }
2577}
2578
2579impl From<Payload> for OwnedPayloadRef<'_> {
2580 fn from(payload: Payload) -> Self {
2581 OwnedPayloadRef::Owned(Rc::new(payload.0))
2582 }
2583}
2584
2585impl From<Map<String, Value>> for OwnedPayloadRef<'_> {
2586 fn from(payload: Map<String, Value>) -> Self {
2587 OwnedPayloadRef::Owned(Rc::new(payload))
2588 }
2589}
2590
2591impl<'a> From<&'a Payload> for OwnedPayloadRef<'a> {
2592 fn from(payload: &'a Payload) -> Self {
2593 OwnedPayloadRef::Ref(&payload.0)
2594 }
2595}
2596
2597impl<'a> From<&'a Map<String, Value>> for OwnedPayloadRef<'a> {
2598 fn from(payload: &'a Map<String, Value>) -> Self {
2599 OwnedPayloadRef::Ref(payload)
2600 }
2601}
2602
2603#[derive(
2605 Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Hash, Eq, EnumIter,
2606)]
2607#[serde(rename_all = "snake_case")]
2608pub enum PayloadSchemaType {
2609 Keyword,
2610 Integer,
2611 Float,
2612 Geo,
2613 Text,
2614 Bool,
2615 Datetime,
2616 Uuid,
2617}
2618
2619impl PayloadSchemaType {
2620 pub fn name(&self) -> &'static str {
2622 serde_variant::to_variant_name(&self).unwrap_or("unknown")
2623 }
2624
2625 pub fn expand(&self) -> PayloadSchemaParams {
2626 match self {
2627 Self::Keyword => PayloadSchemaParams::Keyword(KeywordIndexParams::default()),
2628 Self::Integer => PayloadSchemaParams::Integer(IntegerIndexParams::default()),
2629 Self::Float => PayloadSchemaParams::Float(FloatIndexParams::default()),
2630 Self::Geo => PayloadSchemaParams::Geo(GeoIndexParams::default()),
2631 Self::Text => PayloadSchemaParams::Text(TextIndexParams::default()),
2632 Self::Bool => PayloadSchemaParams::Bool(BoolIndexParams::default()),
2633 Self::Datetime => PayloadSchemaParams::Datetime(DatetimeIndexParams::default()),
2634 Self::Uuid => PayloadSchemaParams::Uuid(UuidIndexParams::default()),
2635 }
2636 }
2637}
2638
2639#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Hash, Eq)]
2641#[serde(untagged, rename_all = "snake_case")]
2642
2643pub enum PayloadSchemaParams {
2644 Keyword(KeywordIndexParams),
2645 Integer(IntegerIndexParams),
2646 Float(FloatIndexParams),
2647 Geo(GeoIndexParams),
2648 Text(TextIndexParams),
2649 Bool(BoolIndexParams),
2650 Datetime(DatetimeIndexParams),
2651 Uuid(UuidIndexParams),
2652}
2653
2654impl PayloadSchemaParams {
2655 pub fn name(&self) -> &'static str {
2657 self.kind().name()
2658 }
2659
2660 pub fn kind(&self) -> PayloadSchemaType {
2661 match self {
2662 PayloadSchemaParams::Keyword(_) => PayloadSchemaType::Keyword,
2663 PayloadSchemaParams::Integer(_) => PayloadSchemaType::Integer,
2664 PayloadSchemaParams::Float(_) => PayloadSchemaType::Float,
2665 PayloadSchemaParams::Geo(_) => PayloadSchemaType::Geo,
2666 PayloadSchemaParams::Text(_) => PayloadSchemaType::Text,
2667 PayloadSchemaParams::Bool(_) => PayloadSchemaType::Bool,
2668 PayloadSchemaParams::Datetime(_) => PayloadSchemaType::Datetime,
2669 PayloadSchemaParams::Uuid(_) => PayloadSchemaType::Uuid,
2670 }
2671 }
2672
2673 pub fn tenant_optimization(&self) -> bool {
2674 match self {
2675 PayloadSchemaParams::Keyword(keyword) => keyword.is_tenant.unwrap_or_default(),
2676 PayloadSchemaParams::Integer(integer) => integer.is_principal.unwrap_or_default(),
2677 PayloadSchemaParams::Float(float) => float.is_principal.unwrap_or_default(),
2678 PayloadSchemaParams::Datetime(datetime) => datetime.is_principal.unwrap_or_default(),
2679 PayloadSchemaParams::Uuid(uuid) => uuid.is_tenant.unwrap_or_default(),
2680 PayloadSchemaParams::Geo(_)
2681 | PayloadSchemaParams::Text(_)
2682 | PayloadSchemaParams::Bool(_) => false,
2683 }
2684 }
2685
2686 pub fn is_on_disk(&self) -> bool {
2687 self.memory_placement().is_on_disk()
2688 }
2689
2690 pub fn memory_placement(&self) -> Memory {
2695 let (memory, on_disk) = match self {
2696 PayloadSchemaParams::Keyword(i) => (i.memory, i.on_disk),
2697 PayloadSchemaParams::Integer(i) => (i.memory, i.on_disk),
2698 PayloadSchemaParams::Float(i) => (i.memory, i.on_disk),
2699 PayloadSchemaParams::Datetime(i) => (i.memory, i.on_disk),
2700 PayloadSchemaParams::Uuid(i) => (i.memory, i.on_disk),
2701 PayloadSchemaParams::Text(i) => (i.memory, i.on_disk),
2702 PayloadSchemaParams::Geo(i) => (i.memory, i.on_disk),
2703 PayloadSchemaParams::Bool(i) => (i.memory, i.on_disk),
2704 };
2705 Memory::resolve(memory, on_disk.map(Memory::from_on_disk_heap)).unwrap_or(Memory::Pinned)
2706 }
2707
2708 pub fn enable_hnsw(&self) -> bool {
2709 match self {
2710 PayloadSchemaParams::Keyword(params) => params.enable_hnsw.unwrap_or(true),
2711 PayloadSchemaParams::Integer(params) => params.enable_hnsw.unwrap_or(true),
2712 PayloadSchemaParams::Float(params) => params.enable_hnsw.unwrap_or(true),
2713 PayloadSchemaParams::Datetime(params) => params.enable_hnsw.unwrap_or(true),
2714 PayloadSchemaParams::Uuid(params) => params.enable_hnsw.unwrap_or(true),
2715 PayloadSchemaParams::Text(params) => params.enable_hnsw.unwrap_or(true),
2716 PayloadSchemaParams::Geo(params) => params.enable_hnsw.unwrap_or(true),
2717 PayloadSchemaParams::Bool(params) => params.enable_hnsw.unwrap_or(true),
2718 }
2719 }
2720}
2721
2722impl Validate for PayloadSchemaParams {
2723 fn validate(&self) -> Result<(), ValidationErrors> {
2724 match self {
2725 PayloadSchemaParams::Keyword(_) => Ok(()),
2726 PayloadSchemaParams::Integer(integer_index_params) => integer_index_params.validate(),
2727 PayloadSchemaParams::Float(_) => Ok(()),
2728 PayloadSchemaParams::Geo(_) => Ok(()),
2729 PayloadSchemaParams::Text(_) => Ok(()),
2730 PayloadSchemaParams::Bool(_) => Ok(()),
2731 PayloadSchemaParams::Datetime(_) => Ok(()),
2732 PayloadSchemaParams::Uuid(_) => Ok(()),
2733 }
2734 }
2735}
2736
2737#[derive(Clone, Debug, Eq, Deserialize, Serialize, JsonSchema)]
2738#[serde(untagged, rename_all = "snake_case")]
2739pub enum PayloadFieldSchema {
2740 FieldType(PayloadSchemaType),
2741 FieldParams(PayloadSchemaParams),
2742}
2743
2744impl PartialEq for PayloadFieldSchema {
2745 fn eq(&self, other: &Self) -> bool {
2746 match (self, other) {
2747 (Self::FieldType(this), Self::FieldType(other)) => this == other,
2748 (Self::FieldParams(this), Self::FieldParams(other)) => this == other,
2749 (Self::FieldType(this), Self::FieldParams(other)) => &this.expand() == other,
2750 (Self::FieldParams(this), Self::FieldType(other)) => this == &other.expand(),
2751 }
2752 }
2753}
2754
2755impl hash::Hash for PayloadFieldSchema {
2756 fn hash<H: hash::Hasher>(&self, state: &mut H) {
2757 match self {
2758 PayloadFieldSchema::FieldType(default) => default.expand().hash(state),
2759 PayloadFieldSchema::FieldParams(params) => params.hash(state),
2760 }
2761 }
2762}
2763
2764impl Validate for PayloadFieldSchema {
2765 fn validate(&self) -> Result<(), ValidationErrors> {
2766 match self {
2767 PayloadFieldSchema::FieldType(_) => Ok(()), PayloadFieldSchema::FieldParams(payload_schema_params) => {
2769 payload_schema_params.validate()
2770 }
2771 }
2772 }
2773}
2774
2775impl Display for PayloadFieldSchema {
2776 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2777 match self {
2778 PayloadFieldSchema::FieldType(t) => write!(f, "{}", t.name()),
2779 PayloadFieldSchema::FieldParams(params) => match params {
2780 PayloadSchemaParams::Float(_)
2781 | PayloadSchemaParams::Geo(_)
2782 | PayloadSchemaParams::Bool(_)
2783 | PayloadSchemaParams::Datetime(_)
2784 | PayloadSchemaParams::Uuid(_) => write!(f, "{}", params.name()),
2785 PayloadSchemaParams::Keyword(keyword_params) => {
2786 if keyword_params.prefix.unwrap_or_default() {
2787 write!(f, "keyword (with prefix: true)")
2788 } else {
2789 write!(f, "keyword")
2790 }
2791 }
2792 PayloadSchemaParams::Integer(integer_params) => {
2793 let range = integer_params.range.unwrap_or(true);
2794 let lookup = integer_params.lookup.unwrap_or(true);
2795 if range && lookup {
2796 write!(f, "integer")
2797 } else {
2798 write!(f, "integer (with range: {range}, lookup: {lookup})")
2799 }
2800 }
2801 PayloadSchemaParams::Text(text_params) => {
2802 if text_params.phrase_matching.unwrap_or_default() {
2803 write!(f, "text (with phrase_matching: true)")
2804 } else {
2805 write!(f, "text")
2806 }
2807 }
2808 },
2809 }
2810 }
2811}
2812
2813impl TryFrom<&PayloadFieldSchema> for TextIndexParams {
2814 type Error = OperationError;
2815
2816 fn try_from(schema: &PayloadFieldSchema) -> Result<Self, Self::Error> {
2821 let expanded = schema.expand();
2822 let PayloadSchemaParams::Text(config) = expanded.as_ref() else {
2823 return Err(OperationError::service_error(
2824 "expected a text payload schema for a full-text index",
2825 ));
2826 };
2827 Ok(config.clone())
2828 }
2829}
2830
2831impl PayloadFieldSchema {
2832 pub fn expand(&self) -> Cow<'_, PayloadSchemaParams> {
2833 match self {
2834 PayloadFieldSchema::FieldType(t) => Cow::Owned(t.expand()),
2835 PayloadFieldSchema::FieldParams(p) => Cow::Borrowed(p),
2836 }
2837 }
2838
2839 pub fn name(&self) -> &'static str {
2841 match self {
2842 PayloadFieldSchema::FieldType(field_type) => field_type.name(),
2843 PayloadFieldSchema::FieldParams(field_params) => field_params.name(),
2844 }
2845 }
2846
2847 pub fn is_tenant(&self) -> bool {
2848 match self {
2849 PayloadFieldSchema::FieldType(_) => false,
2850 PayloadFieldSchema::FieldParams(params) => params.tenant_optimization(),
2851 }
2852 }
2853
2854 pub fn is_on_disk(&self) -> bool {
2855 match self {
2856 PayloadFieldSchema::FieldType(_) => false,
2857 PayloadFieldSchema::FieldParams(params) => params.is_on_disk(),
2858 }
2859 }
2860
2861 pub fn memory_placement(&self) -> Memory {
2864 match self {
2865 PayloadFieldSchema::FieldType(_) => Memory::Pinned,
2866 PayloadFieldSchema::FieldParams(params) => params.memory_placement(),
2867 }
2868 }
2869
2870 pub fn kind(&self) -> PayloadSchemaType {
2871 match self {
2872 PayloadFieldSchema::FieldType(t) => *t,
2873 PayloadFieldSchema::FieldParams(p) => p.kind(),
2874 }
2875 }
2876
2877 pub fn supports_match(&self) -> bool {
2879 match self {
2880 PayloadFieldSchema::FieldType(payload_schema_type) => match payload_schema_type {
2881 PayloadSchemaType::Keyword => true,
2882 PayloadSchemaType::Integer => true,
2883 PayloadSchemaType::Uuid => true,
2884 PayloadSchemaType::Bool => true,
2885 PayloadSchemaType::Float => false,
2886 PayloadSchemaType::Geo => false,
2887 PayloadSchemaType::Text => false,
2888 PayloadSchemaType::Datetime => false,
2889 },
2890 PayloadFieldSchema::FieldParams(payload_schema_params) => match payload_schema_params {
2891 PayloadSchemaParams::Keyword(_) => true,
2892 PayloadSchemaParams::Integer(integer_index_params) => {
2893 integer_index_params.lookup == Some(true)
2894 }
2895 PayloadSchemaParams::Uuid(_) => true,
2896 PayloadSchemaParams::Bool(_) => true,
2897 PayloadSchemaParams::Float(_) => false,
2898 PayloadSchemaParams::Geo(_) => false,
2899 PayloadSchemaParams::Text(_) => false,
2900 PayloadSchemaParams::Datetime(_) => false,
2901 },
2902 }
2903 }
2904
2905 pub fn enable_hnsw(&self) -> bool {
2906 match self {
2907 PayloadFieldSchema::FieldType(_) => true,
2908 PayloadFieldSchema::FieldParams(p) => p.enable_hnsw(),
2909 }
2910 }
2911}
2912
2913impl From<PayloadSchemaType> for PayloadFieldSchema {
2914 fn from(payload_schema_type: PayloadSchemaType) -> Self {
2915 PayloadFieldSchema::FieldType(payload_schema_type)
2916 }
2917}
2918
2919impl TryFrom<PayloadIndexInfo> for PayloadFieldSchema {
2920 type Error = String;
2921
2922 fn try_from(index_info: PayloadIndexInfo) -> Result<Self, Self::Error> {
2923 let PayloadIndexInfo {
2924 data_type,
2925 params,
2926 points: _,
2927 } = index_info;
2928
2929 match params {
2930 None => Ok(PayloadFieldSchema::FieldType(data_type)),
2931
2932 Some(params) if data_type == params.kind() => {
2933 Ok(PayloadFieldSchema::FieldParams(params))
2934 }
2935
2936 Some(params) => Err(format!(
2937 "payload field with type {data_type:?} has parameters of type {:?}",
2938 params.kind(),
2939 )),
2940 }
2941 }
2942}
2943
2944#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2945#[serde(untagged)]
2946pub enum ValueVariants {
2947 String(String),
2948 Integer(IntPayloadType),
2949 Bool(bool),
2950}
2951
2952impl ValueVariants {
2953 pub fn to_value(&self) -> Value {
2954 match self {
2955 ValueVariants::String(keyword) => Value::String(keyword.clone()),
2956 &ValueVariants::Integer(integer) => Value::Number(integer.into()),
2957 &ValueVariants::Bool(flag) => Value::Bool(flag),
2958 }
2959 }
2960}
2961
2962#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
2963#[serde(untagged)]
2964pub enum AnyVariants {
2965 Strings(IndexSet<String, FnvBuildHasher>),
2966 Integers(IndexSet<IntPayloadType, FnvBuildHasher>),
2967}
2968
2969impl Hash for AnyVariants {
2970 fn hash<H: Hasher>(&self, state: &mut H) {
2971 mem::discriminant(self).hash(state);
2972 match self {
2973 AnyVariants::Strings(index_set) => {
2974 for item in index_set {
2975 item.hash(state);
2976 }
2977 }
2978 AnyVariants::Integers(index_set) => {
2979 for item in index_set {
2980 item.hash(state);
2981 }
2982 }
2983 }
2984 }
2985}
2986
2987impl AnyVariants {
2988 pub fn len(&self) -> usize {
2989 match self {
2990 AnyVariants::Strings(index_set) => index_set.len(),
2991 AnyVariants::Integers(index_set) => index_set.len(),
2992 }
2993 }
2994
2995 pub fn is_empty(&self) -> bool {
2996 match self {
2997 AnyVariants::Strings(index_set) => index_set.is_empty(),
2998 AnyVariants::Integers(index_set) => index_set.is_empty(),
2999 }
3000 }
3001}
3002
3003#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3005#[serde(rename_all = "snake_case")]
3006pub struct MatchValue {
3007 pub value: ValueVariants,
3008}
3009
3010#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3012#[serde(rename_all = "snake_case")]
3013pub struct MatchText {
3014 pub text: String,
3015}
3016
3017#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3019#[serde(rename_all = "snake_case")]
3020pub struct MatchTextAny {
3021 pub text_any: String,
3022}
3023
3024impl<S: Into<String>> From<S> for MatchText {
3025 fn from(text: S) -> Self {
3026 MatchText { text: text.into() }
3027 }
3028}
3029
3030#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3032#[serde(rename_all = "snake_case")]
3033pub struct MatchPhrase {
3034 pub phrase: String,
3035}
3036
3037impl<S: Into<String>> From<S> for MatchPhrase {
3038 fn from(text: S) -> Self {
3039 MatchPhrase {
3040 phrase: text.into(),
3041 }
3042 }
3043}
3044
3045#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3051#[serde(rename_all = "snake_case")]
3052pub struct MatchPrefix {
3053 pub prefix: String,
3054}
3055
3056impl<S: Into<String>> From<S> for MatchPrefix {
3057 fn from(prefix: S) -> Self {
3058 MatchPrefix {
3059 prefix: prefix.into(),
3060 }
3061 }
3062}
3063
3064#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3066#[serde(rename_all = "snake_case")]
3067pub struct MatchAny {
3068 pub any: AnyVariants,
3069}
3070
3071#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3073#[serde(rename_all = "snake_case")]
3074pub struct MatchExcept {
3075 pub except: AnyVariants,
3076}
3077
3078#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
3080#[serde(untagged, rename_all = "snake_case")]
3081pub enum MatchInterface {
3082 Value(MatchValue),
3083 Text(MatchText),
3084 TextAny(MatchTextAny),
3085 Phrase(MatchPhrase),
3086 Prefix(MatchPrefix),
3087 Any(MatchAny),
3088 Except(MatchExcept),
3089}
3090
3091#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3093#[serde(untagged, from = "MatchInterface")]
3094pub enum Match {
3095 Value(MatchValue),
3096 Text(MatchText),
3097 TextAny(MatchTextAny),
3098 Phrase(MatchPhrase),
3099 Prefix(MatchPrefix),
3100 Any(MatchAny),
3101 Except(MatchExcept),
3102}
3103
3104impl Match {
3105 pub fn new_value(value: ValueVariants) -> Self {
3106 Self::Value(MatchValue { value })
3107 }
3108
3109 pub fn new_text(text: &str) -> Self {
3110 Self::Text(MatchText { text: text.into() })
3111 }
3112
3113 pub fn new_prefix(prefix: &str) -> Self {
3114 Self::Prefix(MatchPrefix {
3115 prefix: prefix.into(),
3116 })
3117 }
3118
3119 pub fn new_any(any: AnyVariants) -> Self {
3120 Self::Any(MatchAny { any })
3121 }
3122
3123 pub fn new_except(except: AnyVariants) -> Self {
3124 Self::Except(MatchExcept { except })
3125 }
3126}
3127
3128impl From<AnyVariants> for Match {
3129 fn from(any: AnyVariants) -> Self {
3130 Self::Any(MatchAny { any })
3131 }
3132}
3133
3134impl From<MatchInterface> for Match {
3135 fn from(value: MatchInterface) -> Self {
3136 match value {
3137 MatchInterface::Value(value) => Self::Value(MatchValue { value: value.value }),
3138 MatchInterface::Text(text) => Self::Text(MatchText { text: text.text }),
3139 MatchInterface::TextAny(text_any) => Self::TextAny(MatchTextAny {
3140 text_any: text_any.text_any,
3141 }),
3142 MatchInterface::Any(any) => Self::Any(MatchAny { any: any.any }),
3143 MatchInterface::Except(except) => Self::Except(MatchExcept {
3144 except: except.except,
3145 }),
3146 MatchInterface::Phrase(MatchPhrase { phrase }) => Self::Phrase(MatchPhrase { phrase }),
3147 MatchInterface::Prefix(MatchPrefix { prefix }) => Self::Prefix(MatchPrefix { prefix }),
3148 }
3149 }
3150}
3151
3152impl From<bool> for Match {
3153 fn from(flag: bool) -> Self {
3154 Self::Value(MatchValue {
3155 value: ValueVariants::Bool(flag),
3156 })
3157 }
3158}
3159
3160impl From<String> for Match {
3161 fn from(keyword: String) -> Self {
3162 Self::Value(MatchValue {
3163 value: ValueVariants::String(keyword),
3164 })
3165 }
3166}
3167
3168impl From<EcoString> for Match {
3169 fn from(keyword: EcoString) -> Self {
3170 Self::Value(MatchValue {
3171 value: ValueVariants::String(keyword.into()),
3172 })
3173 }
3174}
3175
3176impl From<IntPayloadType> for Match {
3177 fn from(integer: IntPayloadType) -> Self {
3178 Self::Value(MatchValue {
3179 value: ValueVariants::Integer(integer),
3180 })
3181 }
3182}
3183
3184impl From<Vec<String>> for Match {
3185 fn from(keywords: Vec<String>) -> Self {
3186 let keywords: IndexSet<String, FnvBuildHasher> = keywords.into_iter().collect();
3187 Self::Any(MatchAny {
3188 any: AnyVariants::Strings(keywords),
3189 })
3190 }
3191}
3192
3193impl From<ValueVariants> for Match {
3194 fn from(value: ValueVariants) -> Self {
3195 Self::Value(MatchValue { value })
3196 }
3197}
3198
3199impl From<Vec<String>> for MatchExcept {
3200 fn from(keywords: Vec<String>) -> Self {
3201 let keywords: IndexSet<String, FnvBuildHasher> = keywords.into_iter().collect();
3202 MatchExcept {
3203 except: AnyVariants::Strings(keywords),
3204 }
3205 }
3206}
3207
3208impl From<Vec<IntPayloadType>> for Match {
3209 fn from(integers: Vec<IntPayloadType>) -> Self {
3210 let integers: IndexSet<_, FnvBuildHasher> = integers.into_iter().collect();
3211 Self::Any(MatchAny {
3212 any: AnyVariants::Integers(integers),
3213 })
3214 }
3215}
3216
3217impl From<Vec<IntPayloadType>> for MatchExcept {
3218 fn from(integers: Vec<IntPayloadType>) -> Self {
3219 let integers: IndexSet<_, FnvBuildHasher> = integers.into_iter().collect();
3220 MatchExcept {
3221 except: AnyVariants::Integers(integers),
3222 }
3223 }
3224}
3225
3226#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, JsonSchema)]
3227#[serde(untagged)]
3228pub enum RangeInterface {
3229 Float(Range<OrderedFloat<FloatPayloadType>>),
3230 DateTime(Range<DateTimePayloadType>),
3231}
3232
3233impl Hash for RangeInterface {
3234 fn hash<H: hash::Hasher>(&self, state: &mut H) {
3235 match self {
3236 RangeInterface::Float(range) => {
3237 let Range { lt, gt, gte, lte } = range;
3238 lt.hash(state);
3239 gt.hash(state);
3240 gte.hash(state);
3241 lte.hash(state);
3242 }
3243 RangeInterface::DateTime(range) => {
3244 let Range { lt, gt, gte, lte } = range;
3245 lt.hash(state);
3246 gt.hash(state);
3247 gte.hash(state);
3248 lte.hash(state);
3249 }
3250 }
3251 }
3252}
3253
3254#[derive(serde::Deserialize)]
3255#[serde(untagged)]
3256enum RangeInterfaceUntagged {
3257 Float(Range<OrderedFloatPayloadType>),
3258 DateTime(Range<DateTimePayloadType>),
3259}
3260
3261impl<'de> serde::Deserialize<'de> for RangeInterface {
3262 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3266 where
3267 D: serde::Deserializer<'de>,
3268 {
3269 if !deserializer.is_human_readable() {
3270 return RangeInterfaceUntagged::deserialize(deserializer).map(|parsed| match parsed {
3271 RangeInterfaceUntagged::Float(r) => RangeInterface::Float(r),
3272 RangeInterfaceUntagged::DateTime(r) => RangeInterface::DateTime(r),
3273 });
3274 }
3275
3276 let value = serde_json::Value::deserialize(deserializer)?;
3277
3278 if let Some(obj) = value.as_object() {
3280 let keys = ["lt", "gt", "lte", "gte"];
3281 let has_string_bound = keys
3282 .iter()
3283 .any(|k| obj.get(*k).is_some_and(|v| v.is_string()));
3284
3285 if has_string_bound {
3286 return serde_json::from_value::<Range<DateTimePayloadType>>(value)
3287 .map(RangeInterface::DateTime)
3288 .map_err(serde::de::Error::custom);
3289 }
3290 }
3291
3292 let parsed = serde_json::from_value::<RangeInterfaceUntagged>(value)
3294 .map_err(serde::de::Error::custom)?;
3295
3296 Ok(match parsed {
3297 RangeInterfaceUntagged::Float(r) => RangeInterface::Float(r),
3298 RangeInterfaceUntagged::DateTime(r) => RangeInterface::DateTime(r),
3299 })
3300 }
3301}
3302
3303type OrderedFloatPayloadType = OrderedFloat<FloatPayloadType>;
3304
3305#[macro_rules_attribute::macro_rules_derive(crate::segment::common::macros::schemars_rename_generics)]
3307#[derive_args(< OrderedFloatPayloadType > => "Range", < DateTimePayloadType > => "DatetimeRange")]
3308#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
3309#[serde(rename_all = "snake_case")]
3310pub struct Range<T> {
3311 pub lt: Option<T>,
3313 pub gt: Option<T>,
3315 pub gte: Option<T>,
3317 pub lte: Option<T>,
3319}
3320
3321impl<T: Copy> Range<T> {
3322 pub fn map<U, F: Fn(T) -> U>(&self, f: F) -> Range<U> {
3324 let Self { lt, gt, gte, lte } = self;
3325 Range {
3326 lt: lt.map(&f),
3327 gt: gt.map(&f),
3328 gte: gte.map(&f),
3329 lte: lte.map(&f),
3330 }
3331 }
3332}
3333
3334impl<T: Copy + PartialOrd> Range<T> {
3335 pub fn check_range(&self, number: T) -> bool {
3336 let Self { lt, gt, gte, lte } = self;
3337 lt.is_none_or(|x| number < x)
3338 && gt.is_none_or(|x| number > x)
3339 && lte.is_none_or(|x| number <= x)
3340 && gte.is_none_or(|x| number >= x)
3341 }
3342}
3343
3344#[derive(Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq, Hash)]
3346#[serde(rename_all = "snake_case")]
3347pub struct ValuesCount {
3348 pub lt: Option<usize>,
3350 pub gt: Option<usize>,
3352 pub gte: Option<usize>,
3354 pub lte: Option<usize>,
3356}
3357
3358impl ValuesCount {
3359 pub fn check_count(&self, count: usize) -> bool {
3360 let Self { lt, gt, gte, lte } = self;
3361 lt.is_none_or(|x| count < x)
3362 && gt.is_none_or(|x| count > x)
3363 && lte.is_none_or(|x| count <= x)
3364 && gte.is_none_or(|x| count >= x)
3365 }
3366
3367 pub fn check_count_from(&self, value: &Value) -> bool {
3368 let count = match value {
3369 Value::Null => 0,
3370 Value::Array(array) => array.len(),
3371 Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Object(_) => 1,
3372 };
3373
3374 self.check_count(count)
3375 }
3376}
3377
3378#[cfg(test)]
3379impl From<std::ops::Range<usize>> for ValuesCount {
3380 fn from(range: std::ops::Range<usize>) -> Self {
3381 Self {
3382 gte: Some(range.start),
3383 lt: Some(range.end),
3384 gt: None,
3385 lte: None,
3386 }
3387 }
3388}
3389
3390pub trait CheckGeoPoint {
3391 fn check_point(&self, point: &GeoPoint) -> bool;
3392}
3393
3394#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema)]
3398#[serde(rename_all = "snake_case")]
3399pub struct GeoBoundingBox {
3400 pub top_left: GeoPoint,
3402 pub bottom_right: GeoPoint,
3404}
3405
3406impl CheckGeoPoint for GeoBoundingBox {
3407 fn check_point(&self, point: &GeoPoint) -> bool {
3408 let longitude_check = if self.top_left.lon > self.bottom_right.lon {
3409 point.lon > self.top_left.lon || point.lon < self.bottom_right.lon
3411 } else {
3412 self.top_left.lon < point.lon && point.lon < self.bottom_right.lon
3413 };
3414
3415 let latitude_check = self.bottom_right.lat < point.lat && point.lat < self.top_left.lat;
3416
3417 longitude_check && latitude_check
3418 }
3419}
3420
3421#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize, JsonSchema)]
3425#[serde(rename_all = "snake_case")]
3426pub struct GeoRadius {
3427 pub center: GeoPoint,
3429 pub radius: OrderedFloat<f64>,
3431}
3432
3433impl Hash for GeoRadius {
3434 fn hash<H: hash::Hasher>(&self, state: &mut H) {
3435 let GeoRadius { center, radius } = self;
3436 center.hash(state);
3437 OrderedFloat(*radius).hash(state);
3439 }
3440}
3441
3442impl CheckGeoPoint for GeoRadius {
3443 fn check_point(&self, point: &GeoPoint) -> bool {
3444 let query_center = Point::from(self.center);
3445 Haversine.distance(query_center, Point::from(*point)) < self.radius.0
3446 }
3447}
3448
3449#[derive(Deserialize)]
3450pub struct GeoPolygonShadow {
3451 pub exterior: GeoLineString,
3452 pub interiors: Option<Vec<GeoLineString>>,
3453}
3454
3455pub struct PolygonWrapper {
3456 pub polygon: Polygon,
3457}
3458
3459impl CheckGeoPoint for PolygonWrapper {
3460 fn check_point(&self, point: &GeoPoint) -> bool {
3461 let point_new = Point::new(point.lon.0, point.lat.0);
3462 self.polygon.contains(&point_new)
3463 }
3464}
3465
3466#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3470#[serde(try_from = "GeoPolygonShadow", rename_all = "snake_case")]
3471pub struct GeoPolygon {
3472 pub exterior: GeoLineString,
3476 pub interiors: Option<Vec<GeoLineString>>,
3480}
3481
3482impl GeoPolygon {
3483 pub fn validate_line_string(line: &GeoLineString) -> OperationResult<()> {
3484 if line.points.len() <= 3 {
3485 return Err(OperationError::validation_error(format!(
3486 "polygon invalid, the size must be at least 4, got {}",
3487 line.points.len()
3488 )));
3489 }
3490
3491 if let (Some(first), Some(last)) = (line.points.first(), line.points.last())
3492 && ((first.lat - last.lat).abs() > f64::EPSILON
3493 || (first.lon - last.lon).abs() > f64::EPSILON)
3494 {
3495 return Err(OperationError::validation_error(
3496 "polygon invalid, the first and the last points should be the same to form a closed line",
3497 ));
3498 }
3499
3500 Ok(())
3501 }
3502
3503 pub fn convert(&self) -> PolygonWrapper {
3505 let exterior_line: LineString = LineString(
3506 self.exterior
3507 .points
3508 .iter()
3509 .map(|p| Coord {
3510 x: p.lon.0,
3511 y: p.lat.0,
3512 })
3513 .collect(),
3514 );
3515
3516 let interior_lines: Vec<LineString> = match &self.interiors {
3518 None => vec![],
3519 Some(interiors) => interiors
3520 .iter()
3521 .map(|interior_points| {
3522 interior_points
3523 .points
3524 .iter()
3525 .map(|p| Coord {
3526 x: p.lon.0,
3527 y: p.lat.0,
3528 })
3529 .collect()
3530 })
3531 .map(LineString)
3532 .collect(),
3533 };
3534 PolygonWrapper {
3535 polygon: Polygon::new(exterior_line, interior_lines),
3536 }
3537 }
3538}
3539
3540impl TryFrom<GeoPolygonShadow> for GeoPolygon {
3541 type Error = OperationError;
3542
3543 fn try_from(value: GeoPolygonShadow) -> OperationResult<Self> {
3544 let GeoPolygonShadow {
3545 exterior,
3546 interiors,
3547 } = value;
3548 Self::validate_line_string(&exterior)?;
3549
3550 if let Some(interiors) = &interiors {
3551 for interior in interiors {
3552 Self::validate_line_string(interior)?;
3553 }
3554 }
3555
3556 Ok(GeoPolygon {
3557 exterior,
3558 interiors,
3559 })
3560 }
3561}
3562
3563#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Hash)]
3565#[validate(schema(function = "validate_field_condition"))]
3566#[serde(rename_all = "snake_case")]
3567pub struct FieldCondition {
3568 pub key: PayloadKeyType,
3570 #[serde(skip_serializing_if = "Option::is_none")]
3572 pub r#match: Option<Match>,
3573 #[serde(skip_serializing_if = "Option::is_none")]
3575 pub range: Option<RangeInterface>,
3576 #[serde(skip_serializing_if = "Option::is_none")]
3578 pub geo_bounding_box: Option<GeoBoundingBox>,
3579 #[serde(skip_serializing_if = "Option::is_none")]
3581 pub geo_radius: Option<GeoRadius>,
3582 #[serde(skip_serializing_if = "Option::is_none")]
3584 pub geo_polygon: Option<GeoPolygon>,
3585 #[serde(skip_serializing_if = "Option::is_none")]
3587 pub values_count: Option<ValuesCount>,
3588 #[serde(skip_serializing_if = "Option::is_none")]
3590 pub is_empty: Option<bool>,
3591 #[serde(skip_serializing_if = "Option::is_none")]
3593 pub is_null: Option<bool>,
3594}
3595
3596impl FieldCondition {
3597 pub fn new_match(key: PayloadKeyType, r#match: Match) -> Self {
3598 Self {
3599 key,
3600 r#match: Some(r#match),
3601 range: None,
3602 geo_bounding_box: None,
3603 geo_radius: None,
3604 geo_polygon: None,
3605 values_count: None,
3606 is_empty: None,
3607 is_null: None,
3608 }
3609 }
3610
3611 pub fn new_range(key: PayloadKeyType, range: Range<OrderedFloat<FloatPayloadType>>) -> Self {
3612 Self {
3613 key,
3614 r#match: None,
3615 range: Some(RangeInterface::Float(range)),
3616 geo_bounding_box: None,
3617 geo_radius: None,
3618 geo_polygon: None,
3619 values_count: None,
3620 is_empty: None,
3621 is_null: None,
3622 }
3623 }
3624
3625 pub fn new_datetime_range(
3626 key: PayloadKeyType,
3627 datetime_range: Range<DateTimePayloadType>,
3628 ) -> Self {
3629 Self {
3630 key,
3631 r#match: None,
3632 range: Some(RangeInterface::DateTime(datetime_range)),
3633 geo_bounding_box: None,
3634 geo_radius: None,
3635 geo_polygon: None,
3636 values_count: None,
3637 is_empty: None,
3638 is_null: None,
3639 }
3640 }
3641
3642 pub fn new_geo_bounding_box(key: PayloadKeyType, geo_bounding_box: GeoBoundingBox) -> Self {
3643 Self {
3644 key,
3645 r#match: None,
3646 range: None,
3647 geo_bounding_box: Some(geo_bounding_box),
3648 geo_radius: None,
3649 geo_polygon: None,
3650 values_count: None,
3651 is_empty: None,
3652 is_null: None,
3653 }
3654 }
3655
3656 pub fn new_geo_radius(key: PayloadKeyType, geo_radius: GeoRadius) -> Self {
3657 Self {
3658 key,
3659 r#match: None,
3660 range: None,
3661 geo_bounding_box: None,
3662 geo_radius: Some(geo_radius),
3663 geo_polygon: None,
3664 values_count: None,
3665 is_empty: None,
3666 is_null: None,
3667 }
3668 }
3669
3670 pub fn new_geo_polygon(key: PayloadKeyType, geo_polygon: GeoPolygon) -> Self {
3671 Self {
3672 key,
3673 r#match: None,
3674 range: None,
3675 geo_bounding_box: None,
3676 geo_radius: None,
3677 geo_polygon: Some(geo_polygon),
3678 values_count: None,
3679 is_empty: None,
3680 is_null: None,
3681 }
3682 }
3683
3684 pub fn new_values_count(key: PayloadKeyType, values_count: ValuesCount) -> Self {
3685 Self {
3686 key,
3687 r#match: None,
3688 range: None,
3689 geo_bounding_box: None,
3690 geo_radius: None,
3691 geo_polygon: None,
3692 values_count: Some(values_count),
3693 is_empty: None,
3694 is_null: None,
3695 }
3696 }
3697
3698 pub fn new_is_empty(key: PayloadKeyType, is_empty: bool) -> Self {
3699 Self {
3700 key,
3701 r#match: None,
3702 range: None,
3703 geo_bounding_box: None,
3704 geo_radius: None,
3705 geo_polygon: None,
3706 values_count: None,
3707 is_empty: Some(is_empty),
3708 is_null: None,
3709 }
3710 }
3711
3712 pub fn new_is_null(key: PayloadKeyType, is_null: bool) -> Self {
3713 Self {
3714 key,
3715 r#match: None,
3716 range: None,
3717 geo_bounding_box: None,
3718 geo_radius: None,
3719 geo_polygon: None,
3720 values_count: None,
3721 is_empty: None,
3722 is_null: Some(is_null),
3723 }
3724 }
3725
3726 pub fn all_fields_none(&self) -> bool {
3727 matches!(
3728 self,
3729 FieldCondition {
3730 r#match: None,
3731 range: None,
3732 geo_bounding_box: None,
3733 geo_radius: None,
3734 geo_polygon: None,
3735 values_count: None,
3736 key: _,
3737 is_empty: None,
3738 is_null: None,
3739 }
3740 )
3741 }
3742
3743 fn input_size(&self) -> usize {
3744 if self.r#match.is_none() {
3745 return 0;
3746 }
3747
3748 match self.r#match.as_ref().unwrap() {
3749 Match::Any(match_any) => match_any.any.len(),
3750 Match::Except(match_except) => match_except.except.len(),
3751 Match::Value(_) => 0,
3752 Match::Text(_) => 0,
3753 Match::Phrase(_) => 0,
3754 Match::TextAny(_) => 0,
3755 Match::Prefix(_) => 0,
3756 }
3757 }
3758}
3759
3760pub fn validate_field_condition(field_condition: &FieldCondition) -> Result<(), ValidationError> {
3761 if field_condition.all_fields_none() {
3762 Err(ValidationError::new(
3763 "At least one field condition must be specified",
3764 ))
3765 } else {
3766 Ok(())
3767 }
3768}
3769
3770#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3772pub struct PayloadField {
3773 pub key: PayloadKeyType,
3775}
3776
3777#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3779pub struct IsEmptyCondition {
3780 pub is_empty: PayloadField,
3781}
3782
3783#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3785pub struct IsNullCondition {
3786 pub is_null: PayloadField,
3787}
3788
3789impl From<JsonPath> for IsNullCondition {
3790 fn from(key: PayloadKeyType) -> Self {
3791 IsNullCondition {
3792 is_null: PayloadField { key },
3793 }
3794 }
3795}
3796
3797impl From<JsonPath> for IsEmptyCondition {
3798 fn from(key: PayloadKeyType) -> Self {
3799 IsEmptyCondition {
3800 is_empty: PayloadField { key },
3801 }
3802 }
3803}
3804
3805#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
3807pub struct HasIdCondition {
3808 #[schemars(schema_with = "HashSet::<PointIdType>::json_schema")]
3809 pub has_id: MaybeArc<AHashSet<PointIdType>>,
3810}
3811
3812impl Hash for HasIdCondition {
3813 fn hash<H: hash::Hasher>(&self, state: &mut H) {
3814 unordered_hash_unique(state, self.has_id.iter());
3815 }
3816}
3817
3818#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3820pub struct HasVectorCondition {
3821 pub has_vector: VectorNameBuf,
3822}
3823
3824impl From<VectorNameBuf> for HasVectorCondition {
3825 fn from(vector: VectorNameBuf) -> Self {
3826 HasVectorCondition { has_vector: vector }
3827 }
3828}
3829
3830const HAS_ID_CONDITION_ARC_THRESHOLD: usize = 1_000;
3834
3835impl From<AHashSet<PointIdType>> for HasIdCondition {
3836 fn from(has_id: AHashSet<PointIdType>) -> Self {
3837 if has_id.len() > HAS_ID_CONDITION_ARC_THRESHOLD {
3838 HasIdCondition {
3839 has_id: MaybeArc::arc(has_id),
3840 }
3841 } else {
3842 HasIdCondition {
3843 has_id: MaybeArc::no_arc(has_id),
3844 }
3845 }
3846 }
3847}
3848
3849impl FromIterator<PointIdType> for HasIdCondition {
3850 fn from_iter<T: IntoIterator<Item = PointIdType>>(iter: T) -> Self {
3851 let items: AHashSet<_> = iter.into_iter().collect();
3852 Self::from(items)
3854 }
3855}
3856
3857#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash)]
3870pub struct Slice {
3871 pub total: NonZeroU32,
3873 pub index: u32,
3875}
3876
3877impl Slice {
3878 pub fn check(&self, point_id: PointIdType) -> bool {
3880 let Self { total, index } = self;
3881 slice_point_id_hash(point_id) % u64::from(total.get()) == u64::from(*index)
3882 }
3883}
3884
3885pub fn slice_point_id_hash(point_id: ExtendedPointId) -> u64 {
3894 let mut hasher = siphasher::sip::SipHasher24::new();
3895 match point_id {
3896 ExtendedPointId::NumId(num) => hasher.write(&num.to_le_bytes()),
3897 ExtendedPointId::Uuid(uuid) => hasher.write(uuid.as_bytes()),
3898 }
3899 hasher.finish()
3900}
3901
3902#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Eq, Hash, Validate)]
3905#[validate(schema(function = "validate_slice_condition"))]
3906pub struct SliceCondition {
3907 pub slice: Slice,
3908}
3909
3910pub fn validate_slice_condition(condition: &SliceCondition) -> Result<(), ValidationError> {
3911 let SliceCondition { slice } = condition;
3912 let Slice { total, index } = slice;
3913 if index < &total.get() {
3914 Ok(())
3915 } else {
3916 Err(ValidationError::new(
3917 "Slice index must be less than the total number of slices",
3918 ))
3919 }
3920}
3921
3922#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Validate, Hash)]
3924pub struct Nested {
3925 pub key: PayloadKeyType,
3926 #[validate(nested)]
3927 pub filter: Filter,
3928}
3929
3930#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Validate, Hash)]
3931pub struct NestedCondition {
3932 #[validate(nested)]
3933 pub nested: Nested,
3934}
3935
3936impl NestedCondition {
3938 pub fn new(nested: Nested) -> Self {
3939 Self { nested }
3940 }
3941
3942 pub fn raw_key(&self) -> &PayloadKeyType {
3944 &self.nested.key
3945 }
3946
3947 pub fn array_key(&self) -> PayloadKeyType {
3949 self.raw_key().array_key()
3950 }
3951
3952 pub fn filter(&self) -> &Filter {
3953 &self.nested.filter
3954 }
3955}
3956
3957#[derive(Clone, Debug, Serialize, JsonSchema, PartialEq, Eq, Hash)]
3958#[serde(untagged)]
3959#[serde(
3960 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"
3961)]
3962#[allow(clippy::large_enum_variant)]
3963pub enum Condition {
3964 Field(FieldCondition),
3966 IsEmpty(IsEmptyCondition),
3968 IsNull(IsNullCondition),
3970 HasId(HasIdCondition),
3972 HasVector(HasVectorCondition),
3974 Slice(SliceCondition),
3976 Nested(NestedCondition),
3978 Filter(Filter),
3980
3981 #[serde(skip)]
3982 CustomIdChecker(CustomIdChecker),
3983}
3984
3985#[derive(Deserialize)]
3986#[serde(untagged)]
3987#[serde(
3988 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"
3989)]
3990#[expect(clippy::large_enum_variant, dead_code)]
3991enum ConditionUntagged {
3992 Field(FieldCondition),
3993 IsEmpty(IsEmptyCondition),
3994 IsNull(IsNullCondition),
3995 HasId(HasIdCondition),
3996 HasVector(HasVectorCondition),
3997 Slice(SliceCondition),
3998 Nested(NestedCondition),
3999 Filter(Filter),
4000
4001 #[serde(skip)]
4002 CustomIdChecker(CustomIdChecker),
4003}
4004
4005impl From<ConditionUntagged> for Condition {
4006 fn from(condition: ConditionUntagged) -> Self {
4007 match condition {
4008 ConditionUntagged::Field(condition) => Condition::Field(condition),
4009 ConditionUntagged::IsEmpty(condition) => Condition::IsEmpty(condition),
4010 ConditionUntagged::IsNull(condition) => Condition::IsNull(condition),
4011 ConditionUntagged::HasId(condition) => Condition::HasId(condition),
4012 ConditionUntagged::HasVector(condition) => Condition::HasVector(condition),
4013 ConditionUntagged::Slice(condition) => Condition::Slice(condition),
4014 ConditionUntagged::Nested(condition) => Condition::Nested(condition),
4015 ConditionUntagged::Filter(condition) => Condition::Filter(condition),
4016 ConditionUntagged::CustomIdChecker(condition) => Condition::CustomIdChecker(condition),
4017 }
4018 }
4019}
4020
4021impl<'de> serde::Deserialize<'de> for Condition {
4022 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4027 where
4028 D: serde::Deserializer<'de>,
4029 {
4030 let value = serde_value::Value::deserialize(deserializer)?;
4036
4037 if let serde_value::Value::Map(obj) = &value
4040 && let Some(key) = obj.get(&serde_value::Value::String("key".into()))
4041 {
4042 if !matches!(key, serde_value::Value::String(_)) {
4045 return Err(serde::de::Error::custom(
4046 "Filter condition 'key' must be a string",
4047 ));
4048 }
4049 return value
4050 .deserialize_into()
4051 .map(Condition::Field)
4052 .map_err(serde::de::Error::custom);
4053 }
4054
4055 value
4057 .deserialize_into::<ConditionUntagged>()
4058 .map(Condition::from)
4059 .map_err(serde::de::Error::custom)
4060 }
4061}
4062
4063impl Condition {
4064 pub fn new_custom(checker: Arc<dyn CustomIdCheckerCondition + Send + Sync + 'static>) -> Self {
4065 Condition::CustomIdChecker(CustomIdChecker(checker))
4066 }
4067}
4068
4069#[derive(Debug, Clone)]
4070pub struct CustomIdChecker(pub Arc<dyn CustomIdCheckerCondition + Send + Sync + 'static>);
4071
4072impl Hash for CustomIdChecker {
4073 fn hash<H: hash::Hasher>(&self, state: &mut H) {
4074 std::ptr::hash(Arc::as_ptr(&self.0), state);
4079 }
4080}
4081
4082impl PartialEq for CustomIdChecker {
4083 fn eq(&self, other: &Self) -> bool {
4084 Arc::ptr_eq(&self.0, &other.0)
4089 }
4090}
4091
4092impl Eq for CustomIdChecker {}
4093
4094impl Condition {
4095 pub fn new_nested(key: JsonPath, filter: Filter) -> Self {
4096 Self::Nested(NestedCondition {
4097 nested: Nested { key, filter },
4098 })
4099 }
4100
4101 pub fn size_estimation(&self) -> usize {
4102 match self {
4103 Condition::Field(field_condition) => field_condition.input_size(),
4104 Condition::HasId(has_id_condition) => has_id_condition.has_id.len(),
4105 Condition::Filter(filter) => filter.max_condition_input_size(),
4106 Condition::Nested(nested) => nested.filter().max_condition_input_size(),
4107 Condition::IsEmpty(_)
4108 | Condition::IsNull(_)
4109 | Condition::HasVector(_)
4110 | Condition::Slice(_)
4111 | Condition::CustomIdChecker(_) => 0,
4112 }
4113 }
4114
4115 pub fn sub_conditions_count(&self) -> usize {
4116 match self {
4117 Condition::Nested(nested_condition) => {
4118 nested_condition.filter().total_conditions_count()
4119 }
4120 Condition::Filter(filter) => filter.total_conditions_count(),
4121 Condition::Field(_)
4122 | Condition::IsEmpty(_)
4123 | Condition::IsNull(_)
4124 | Condition::CustomIdChecker(_)
4125 | Condition::HasId(_)
4126 | Condition::HasVector(_)
4127 | Condition::Slice(_) => 1,
4128 }
4129 }
4130
4131 pub fn targeted_key(&self) -> Option<PayloadKeyType> {
4132 match self {
4133 Condition::Field(field_condition) => Some(field_condition.key.clone()),
4134 Condition::IsEmpty(is_empty_condition) => Some(is_empty_condition.is_empty.key.clone()),
4135 Condition::IsNull(is_null_condition) => Some(is_null_condition.is_null.key.clone()),
4136 Condition::Nested(nested_condition) => Some(nested_condition.array_key()),
4137 Condition::Filter(filter) => filter.iter_conditions().find_map(|c| c.targeted_key()),
4138 Condition::HasId(_)
4139 | Condition::HasVector(_)
4140 | Condition::Slice(_)
4141 | Condition::CustomIdChecker(_) => None,
4142 }
4143 }
4144}
4145
4146impl Validate for Condition {
4148 fn validate(&self) -> Result<(), ValidationErrors> {
4149 match self {
4150 Condition::HasId(_)
4151 | Condition::IsEmpty(_)
4152 | Condition::IsNull(_)
4153 | Condition::HasVector(_) => Ok(()),
4154 Condition::Field(field_condition) => field_condition.validate(),
4155 Condition::Slice(slice_condition) => slice_condition.validate(),
4156 Condition::Nested(nested_condition) => nested_condition.validate(),
4157 Condition::Filter(filter) => filter.validate(),
4158 Condition::CustomIdChecker(_) => Ok(()),
4159 }
4160 }
4161}
4162
4163pub trait CustomIdCheckerCondition: fmt::Debug {
4164 fn estimate_cardinality(&self, points: usize) -> CardinalityEstimation;
4165 fn check(&self, point_id: ExtendedPointId) -> bool;
4166}
4167
4168#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Hash)]
4170#[serde(untagged, rename_all = "snake_case")]
4171#[serde(
4172 expecting = "Expected a boolean, an array of strings, or an object with an include/exclude field"
4173)]
4174pub enum WithPayloadInterface {
4175 Bool(bool),
4178 Fields(Vec<JsonPath>),
4180 Selector(PayloadSelector),
4182}
4183
4184impl From<bool> for WithPayloadInterface {
4185 fn from(b: bool) -> Self {
4186 WithPayloadInterface::Bool(b)
4187 }
4188}
4189
4190impl Default for WithPayloadInterface {
4191 fn default() -> Self {
4192 WithPayloadInterface::Bool(false)
4193 }
4194}
4195
4196#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
4198#[serde(untagged, rename_all = "snake_case")]
4199#[serde(expecting = "Expected a boolean, or an array of strings")]
4200pub enum WithVector {
4201 Bool(bool),
4204 Selector(Vec<VectorNameBuf>),
4206}
4207
4208impl WithVector {
4209 pub fn is_enabled(&self) -> bool {
4210 match self {
4211 WithVector::Bool(b) => *b,
4212 WithVector::Selector(_) => true,
4213 }
4214 }
4215
4216 pub fn merge(&self, other: &WithVector) -> WithVector {
4218 match (self, other) {
4219 (WithVector::Bool(true), _) => WithVector::Bool(true),
4221 (_, WithVector::Bool(true)) => WithVector::Bool(true),
4222
4223 (WithVector::Bool(false), WithVector::Bool(false)) => WithVector::Bool(false),
4225
4226 (WithVector::Selector(s1), WithVector::Selector(s2)) => {
4228 WithVector::Selector(s1.iter().chain(s2).unique().cloned().collect())
4229 }
4230
4231 (WithVector::Bool(false), WithVector::Selector(s)) => WithVector::Selector(s.clone()),
4233 (WithVector::Selector(s), WithVector::Bool(false)) => WithVector::Selector(s.clone()),
4234 }
4235 }
4236}
4237
4238impl From<bool> for WithVector {
4239 fn from(b: bool) -> Self {
4240 WithVector::Bool(b)
4241 }
4242}
4243
4244impl From<VectorNameBuf> for WithVector {
4245 fn from(name: VectorNameBuf) -> Self {
4246 WithVector::Selector(vec![name])
4247 }
4248}
4249
4250impl Default for WithVector {
4251 fn default() -> Self {
4252 WithVector::Bool(false)
4253 }
4254}
4255
4256impl WithPayloadInterface {
4257 pub fn is_required(&self) -> bool {
4258 match self {
4259 WithPayloadInterface::Bool(b) => *b,
4260 WithPayloadInterface::Fields(_) | WithPayloadInterface::Selector(_) => true,
4261 }
4262 }
4263}
4264
4265impl From<bool> for WithPayload {
4266 fn from(x: bool) -> Self {
4267 WithPayload {
4268 enable: x,
4269 payload_selector: None,
4270 }
4271 }
4272}
4273
4274impl From<WithPayloadInterface> for WithPayload {
4275 fn from(interface: WithPayloadInterface) -> Self {
4276 match interface {
4277 WithPayloadInterface::Bool(enable) => WithPayload {
4278 enable,
4279 payload_selector: None,
4280 },
4281 WithPayloadInterface::Fields(fields) => WithPayload {
4282 enable: true,
4283 payload_selector: Some(PayloadSelector::new_include(fields)),
4284 },
4285 WithPayloadInterface::Selector(selector) => WithPayload {
4286 enable: true,
4287 payload_selector: Some(selector),
4288 },
4289 }
4290 }
4291}
4292
4293impl From<&WithPayloadInterface> for WithPayload {
4294 fn from(interface: &WithPayloadInterface) -> Self {
4295 WithPayload::from(interface.clone())
4296 }
4297}
4298
4299#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
4300#[serde(deny_unknown_fields, rename_all = "snake_case")]
4301pub struct PayloadSelectorInclude {
4302 pub include: Vec<PayloadKeyType>,
4304}
4305
4306impl PayloadSelectorInclude {
4307 pub fn new(include: Vec<PayloadKeyType>) -> Self {
4308 Self { include }
4309 }
4310}
4311
4312#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
4313#[serde(deny_unknown_fields, rename_all = "snake_case")]
4314pub struct PayloadSelectorExclude {
4315 pub exclude: Vec<PayloadKeyType>,
4317}
4318
4319impl PayloadSelectorExclude {
4320 pub fn new(exclude: Vec<PayloadKeyType>) -> Self {
4321 Self { exclude }
4322 }
4323}
4324
4325#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
4327#[serde(untagged, rename_all = "snake_case")]
4328pub enum PayloadSelector {
4329 Include(PayloadSelectorInclude),
4331 Exclude(PayloadSelectorExclude),
4333}
4334
4335impl From<PayloadSelectorExclude> for WithPayloadInterface {
4336 fn from(selector: PayloadSelectorExclude) -> Self {
4337 WithPayloadInterface::Selector(PayloadSelector::Exclude(selector))
4338 }
4339}
4340
4341impl From<PayloadSelectorInclude> for WithPayloadInterface {
4342 fn from(selector: PayloadSelectorInclude) -> Self {
4343 WithPayloadInterface::Selector(PayloadSelector::Include(selector))
4344 }
4345}
4346
4347impl PayloadSelector {
4348 pub fn new_include(vecs_payload_key_type: Vec<PayloadKeyType>) -> Self {
4349 PayloadSelector::Include(PayloadSelectorInclude {
4350 include: vecs_payload_key_type,
4351 })
4352 }
4353
4354 pub fn new_exclude(vecs_payload_key_type: Vec<PayloadKeyType>) -> Self {
4355 PayloadSelector::Exclude(PayloadSelectorExclude {
4356 exclude: vecs_payload_key_type,
4357 })
4358 }
4359
4360 pub fn process(&self, x: Payload) -> Payload {
4362 match self {
4363 PayloadSelector::Include(selector) => JsonPath::value_filter(&x.0, |key, _| {
4364 selector
4365 .include
4366 .iter()
4367 .any(|pattern| pattern.check_include_pattern(key))
4368 })
4369 .into(),
4370 PayloadSelector::Exclude(selector) => JsonPath::value_filter(&x.0, |key, _| {
4371 selector
4372 .exclude
4373 .iter()
4374 .all(|pattern| !pattern.check_exclude_pattern(key))
4375 })
4376 .into(),
4377 }
4378 }
4379}
4380
4381#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Default, PartialEq, Eq)]
4382#[serde(deny_unknown_fields, rename_all = "snake_case")]
4383pub struct WithPayload {
4384 pub enable: bool,
4386 pub payload_selector: Option<PayloadSelector>,
4388}
4389
4390#[derive(
4391 Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Default, Hash,
4392)]
4393#[serde(rename_all = "snake_case")]
4394pub struct MinShould {
4395 #[validate(nested)]
4396 pub conditions: Vec<Condition>,
4397 #[validate(range(min = 1, message = "min_count must be greater than 0"))]
4401 pub min_count: usize,
4402}
4403
4404#[derive(
4405 Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Default, Hash,
4406)]
4407#[serde(deny_unknown_fields, rename_all = "snake_case")]
4408pub struct Filter {
4409 #[validate(nested)]
4411 #[serde(
4412 default,
4413 with = "MaybeOneOrMany",
4414 skip_serializing_if = "Option::is_none"
4415 )]
4416 #[schemars(with = "MaybeOneOrMany<Condition>")]
4417 pub should: Option<Vec<Condition>>,
4418 #[validate(nested)]
4420 #[serde(skip_serializing_if = "Option::is_none")]
4421 pub min_should: Option<MinShould>,
4422 #[validate(nested)]
4424 #[serde(
4425 default,
4426 with = "MaybeOneOrMany",
4427 skip_serializing_if = "Option::is_none"
4428 )]
4429 #[schemars(with = "MaybeOneOrMany<Condition>")]
4430 pub must: Option<Vec<Condition>>,
4431 #[validate(nested)]
4433 #[serde(
4434 default,
4435 with = "MaybeOneOrMany",
4436 skip_serializing_if = "Option::is_none"
4437 )]
4438 #[schemars(with = "MaybeOneOrMany<Condition>")]
4439 pub must_not: Option<Vec<Condition>>,
4440}
4441
4442impl Filter {
4443 pub fn new() -> Self {
4444 Filter {
4445 should: None,
4446 min_should: None,
4447 must: None,
4448 must_not: None,
4449 }
4450 }
4451
4452 pub fn new_should(condition: Condition) -> Self {
4453 Filter {
4454 should: Some(vec![condition]),
4455 min_should: None,
4456 must: None,
4457 must_not: None,
4458 }
4459 }
4460
4461 pub fn new_min_should(min_should: MinShould) -> Self {
4462 Filter {
4463 should: None,
4464 min_should: Some(min_should),
4465 must: None,
4466 must_not: None,
4467 }
4468 }
4469
4470 pub fn new_must(condition: Condition) -> Self {
4471 Filter {
4472 should: None,
4473 min_should: None,
4474 must: Some(vec![condition]),
4475 must_not: None,
4476 }
4477 }
4478
4479 pub fn new_must_not(condition: Condition) -> Self {
4480 Filter {
4481 should: None,
4482 min_should: None,
4483 must: None,
4484 must_not: Some(vec![condition]),
4485 }
4486 }
4487
4488 pub fn with_point_ids(self, ids: impl IntoIterator<Item = PointIdType>) -> Filter {
4490 let has_id_condition: HasIdCondition = ids.into_iter().collect();
4491
4492 let Filter {
4493 should,
4494 min_should,
4495 must,
4496 must_not,
4497 } = self;
4498
4499 let new_must = match must {
4500 Some(mut must) => {
4501 must.push(Condition::HasId(has_id_condition));
4502 Some(must)
4503 }
4504 None => Some(vec![Condition::HasId(has_id_condition)]),
4505 };
4506
4507 Filter {
4508 should,
4509 min_should,
4510 must: new_must,
4511 must_not,
4512 }
4513 }
4514
4515 pub fn merge(&self, other: &Filter) -> Filter {
4516 self.clone().merge_owned(other.clone())
4517 }
4518
4519 pub fn merge_owned(self, other: Filter) -> Filter {
4520 let merge_component = |this, other| -> Option<Vec<Condition>> {
4521 match (this, other) {
4522 (None, None) => None,
4523 (Some(this), None) => Some(this),
4524 (None, Some(other)) => Some(other),
4525 (Some(mut this), Some(mut other)) => {
4526 this.append(&mut other);
4527 Some(this)
4528 }
4529 }
4530 };
4531 Filter {
4532 should: merge_component(self.should, other.should),
4533 min_should: {
4534 match (self.min_should, other.min_should) {
4535 (None, None) => None,
4536 (Some(this), None) => Some(this),
4537 (None, Some(other)) => Some(other),
4538 (Some(mut this), Some(mut other)) => {
4539 this.conditions.append(&mut other.conditions);
4540
4541 this.min_count = this.min_count.max(other.min_count);
4543
4544 Some(this)
4545 }
4546 }
4547 },
4548 must: merge_component(self.must, other.must),
4549 must_not: merge_component(self.must_not, other.must_not),
4550 }
4551 }
4552
4553 pub fn merge_opts(this: Option<Self>, other: Option<Self>) -> Option<Self> {
4554 match (this, other) {
4555 (None, None) => None,
4556 (Some(this), None) => Some(this),
4557 (None, Some(other)) => Some(other),
4558 (Some(this), Some(other)) => Some(this.merge_owned(other)),
4559 }
4560 }
4561
4562 pub fn iter_conditions(&self) -> impl Iterator<Item = &Condition> {
4563 self.must
4564 .iter()
4565 .flatten()
4566 .chain(self.must_not.iter().flatten())
4567 .chain(self.should.iter().flatten())
4568 .chain(self.min_should.iter().flat_map(|i| &i.conditions))
4569 }
4570
4571 pub fn total_conditions_count(&self) -> usize {
4573 fn count_all_conditions(field: Option<&Vec<Condition>>) -> usize {
4574 field
4575 .map(|i| i.iter().map(|j| j.sub_conditions_count()).sum::<usize>())
4576 .unwrap_or(0)
4577 }
4578
4579 count_all_conditions(self.should.as_ref())
4580 + count_all_conditions(self.min_should.as_ref().map(|i| &i.conditions))
4581 + count_all_conditions(self.must.as_ref())
4582 + count_all_conditions(self.must_not.as_ref())
4583 }
4584
4585 pub fn max_condition_input_size(&self) -> usize {
4587 self.iter_conditions()
4588 .map(|i| i.size_estimation())
4589 .max()
4590 .unwrap_or(0)
4591 }
4592}
4593
4594#[derive(Debug, Clone, Copy, Eq, PartialEq)]
4595pub enum SnapshotFormat {
4596 Ancient,
4613 Regular,
4639 Streamable,
4659}
4660
4661#[cfg(test)]
4662pub(crate) mod test_utils {
4663 use super::{GeoLineString, GeoPoint, GeoPolygon};
4664
4665 pub fn build_polygon(exterior_points: Vec<(f64, f64)>) -> GeoPolygon {
4666 let exterior_line = GeoLineString {
4667 points: exterior_points
4668 .into_iter()
4669 .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4670 .collect(),
4671 };
4672
4673 GeoPolygon {
4674 exterior: exterior_line,
4675 interiors: None,
4676 }
4677 }
4678
4679 pub fn build_polygon_with_interiors(
4680 exterior_points: Vec<(f64, f64)>,
4681 interiors_points: Vec<Vec<(f64, f64)>>,
4682 ) -> GeoPolygon {
4683 let exterior_line = GeoLineString {
4684 points: exterior_points
4685 .into_iter()
4686 .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4687 .collect(),
4688 };
4689
4690 let interior_lines = Some(
4691 interiors_points
4692 .into_iter()
4693 .map(|points| GeoLineString {
4694 points: points
4695 .into_iter()
4696 .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4697 .collect(),
4698 })
4699 .collect(),
4700 );
4701
4702 GeoPolygon {
4703 exterior: exterior_line,
4704 interiors: interior_lines,
4705 }
4706 }
4707}
4708
4709#[cfg(test)]
4710mod tests {
4711 #![expect(clippy::wildcard_enum_match_arm, reason = "test code")]
4712
4713 use std::assert_matches;
4714
4715 use itertools::Itertools;
4716 use rstest::rstest;
4717 use serde_json;
4718 use validator::Validate;
4719
4720 use super::test_utils::build_polygon_with_interiors;
4721 use super::*;
4722
4723 #[test]
4724 fn test_memory_legacy_mapping() {
4725 assert_eq!(Memory::from_on_disk(true), Memory::Cold);
4727 assert_eq!(Memory::from_on_disk(false), Memory::Cached);
4728 assert_eq!(Memory::from_on_disk_heap(true), Memory::Cold);
4730 assert_eq!(Memory::from_on_disk_heap(false), Memory::Pinned);
4731
4732 assert!(Memory::Cold.is_on_disk());
4733 assert!(!Memory::Cached.is_on_disk());
4734 assert!(!Memory::Pinned.is_on_disk());
4735
4736 assert!(!Memory::Cold.is_heap());
4737 assert!(!Memory::Cached.is_heap());
4738 assert!(Memory::Pinned.is_heap());
4739
4740 assert!(!Memory::Cold.populate_on_open());
4741 assert!(Memory::Cached.populate_on_open());
4742 assert!(Memory::Pinned.populate_on_open());
4743 }
4744
4745 #[test]
4746 fn test_memory_resolve_precedence() {
4747 assert_eq!(
4749 Memory::resolve(Some(Memory::Cached), Some(Memory::Cold)),
4750 Some(Memory::Cached),
4751 );
4752 assert_eq!(
4754 Memory::resolve(None, Some(Memory::Cold)),
4755 Some(Memory::Cold),
4756 );
4757 assert_eq!(
4758 Memory::resolve(Some(Memory::Pinned), None),
4759 Some(Memory::Pinned)
4760 );
4761 assert_eq!(Memory::resolve(None, None), None);
4762 }
4763
4764 #[test]
4765 fn test_hnsw_memory_placement() {
4766 let mut config = HnswConfig::default();
4767 assert_eq!(config.memory_placement(), Memory::Cached);
4768
4769 config.on_disk = Some(true);
4770 assert_eq!(config.memory_placement(), Memory::Cold);
4771
4772 config.memory = Some(Memory::Pinned);
4774 assert_eq!(config.memory_placement(), Memory::Pinned);
4775 }
4776
4777 #[test]
4778 fn test_hnsw_memory_mismatch_no_rebuild_for_same_placement() {
4779 let legacy = HnswConfig {
4782 on_disk: Some(true),
4783 ..HnswConfig::default()
4784 };
4785 let explicit = HnswConfig {
4786 on_disk: Some(true),
4787 memory: Some(Memory::Cold),
4788 ..HnswConfig::default()
4789 };
4790 assert!(!legacy.mismatch_requires_rebuild(&explicit));
4791
4792 let cached = HnswConfig {
4794 memory: Some(Memory::Cached),
4795 ..legacy
4796 };
4797 assert!(legacy.mismatch_requires_rebuild(&cached));
4798 }
4799
4800 #[test]
4801 fn test_quantization_memory_placement() {
4802 let mut scalar = ScalarQuantizationConfig {
4803 r#type: ScalarType::Int8,
4804 quantile: None,
4805 always_ram: None,
4806 memory: None,
4807 };
4808 assert_eq!(scalar.memory_placement(), None);
4810 scalar.always_ram = Some(false);
4812 assert_eq!(scalar.memory_placement(), None);
4813 scalar.always_ram = Some(true);
4815 assert_eq!(scalar.memory_placement(), Some(Memory::Pinned));
4816 scalar.memory = Some(Memory::Cached);
4818 assert_eq!(scalar.memory_placement(), Some(Memory::Cached));
4819 }
4820
4821 #[test]
4822 fn test_storage_type_memory_mapping() {
4823 assert_eq!(
4824 VectorStorageType::appendable_from_memory(Memory::Cold),
4825 VectorStorageType::ChunkedMmap,
4826 );
4827 assert_eq!(
4828 VectorStorageType::appendable_from_memory(Memory::Cached),
4829 VectorStorageType::InRamChunkedMmap,
4830 );
4831 assert_eq!(
4832 VectorStorageType::immutable_from_memory(Memory::Cold),
4833 VectorStorageType::Mmap,
4834 );
4835 assert_eq!(
4836 VectorStorageType::immutable_from_memory(Memory::Cached),
4837 VectorStorageType::InRamMmap,
4838 );
4839
4840 assert_eq!(
4841 PayloadStorageType::from_memory(Memory::Cold),
4842 PayloadStorageType::Mmap
4843 );
4844 assert_eq!(
4845 PayloadStorageType::from_memory(Memory::Cached),
4846 PayloadStorageType::InRamMmap,
4847 );
4848 assert_eq!(PayloadStorageType::Mmap.memory(), Memory::Cold);
4849 assert_eq!(PayloadStorageType::InRamMmap.memory(), Memory::Cached);
4850 }
4851
4852 #[test]
4853 fn test_payload_field_index_memory_placement() {
4854 let mut params = KeywordIndexParams::default();
4855 assert_eq!(
4857 PayloadSchemaParams::Keyword(params.clone()).memory_placement(),
4858 Memory::Pinned,
4859 );
4860 params.on_disk = Some(true);
4861 assert_eq!(
4862 PayloadSchemaParams::Keyword(params.clone()).memory_placement(),
4863 Memory::Cold,
4864 );
4865 params.memory = Some(Memory::Cached);
4867 assert_eq!(
4868 PayloadSchemaParams::Keyword(params).memory_placement(),
4869 Memory::Cached,
4870 );
4871 }
4872
4873 #[test]
4874 fn test_search_params_rejects_zero_hnsw_ef() {
4875 let params = SearchParams {
4876 hnsw_ef: Some(0),
4877 ..Default::default()
4878 };
4879
4880 let err = params.validate().unwrap_err().to_string();
4881 assert!(err.contains("hnsw_ef"), "error was: {err}");
4882
4883 let params = SearchParams {
4884 hnsw_ef: Some(1),
4885 ..Default::default()
4886 };
4887 params.validate().unwrap();
4888 }
4889
4890 fn match_condition(key: &str, value: &str) -> Condition {
4891 Condition::Field(FieldCondition::new_match(
4892 JsonPath::new(key),
4893 value.to_string().into(),
4894 ))
4895 }
4896
4897 #[test]
4898 fn test_idf_params_json_shapes() {
4899 let params: IdfParams = serde_json::from_str(r#""global""#).unwrap();
4901 assert_eq!(params, IdfParams::Scope(IdfScope::Global));
4902 assert_eq!(params.corpus(), None);
4903 params.validate().unwrap();
4904
4905 let params: IdfParams = serde_json::from_str(
4907 r#"{"corpus": {"must": [{"key": "tenant", "match": {"value": "acme"}}]}}"#,
4908 )
4909 .unwrap();
4910 let expected_corpus = Filter::new_must(match_condition("tenant", "acme"));
4911 assert_eq!(params.corpus(), Some(&expected_corpus));
4912 params.validate().unwrap();
4913
4914 for params in [
4916 IdfParams::Scope(IdfScope::Global),
4917 IdfParams::Corpus(IdfCorpusParams {
4918 corpus: expected_corpus,
4919 }),
4920 ] {
4921 let json = serde_json::to_string(¶ms).unwrap();
4922 let restored: IdfParams = serde_json::from_str(&json).unwrap();
4923 assert_eq!(params, restored);
4924 }
4925 }
4926
4927 #[test]
4928 fn test_idf_corpus_accepts_any_filter() {
4929 let corpus_params = |corpus: Filter| IdfParams::Corpus(IdfCorpusParams { corpus });
4930
4931 corpus_params(Filter::new_must(match_condition("tenant", "acme")))
4933 .validate()
4934 .unwrap();
4935 corpus_params(Filter::new_should(match_condition("tenant", "acme")))
4936 .validate()
4937 .unwrap();
4938 corpus_params(Filter::new_must_not(match_condition("tenant", "acme")))
4939 .validate()
4940 .unwrap();
4941 corpus_params(Filter::new_must(Condition::Field(
4942 FieldCondition::new_range(
4943 JsonPath::new("year"),
4944 Range {
4945 lt: None,
4946 gt: None,
4947 gte: Some(OrderedFloat(2024.0)),
4948 lte: None,
4949 },
4950 ),
4951 )))
4952 .validate()
4953 .unwrap();
4954 }
4955
4956 #[test]
4957 #[ignore]
4958 fn test_rmp_vs_cbor_deserialize() {
4959 let payload = payload_json! {"payload_key": "payload_value"};
4960 let raw = rmp_serde::to_vec(&payload).unwrap();
4961 let de_record: Payload = serde_cbor::from_slice(&raw).unwrap();
4962 eprintln!("payload = {payload:#?}");
4963 eprintln!("de_record = {de_record:#?}");
4964 }
4965
4966 #[rstest]
4967 #[case::rfc_3339("2020-03-01T00:00:00Z")]
4968 #[case::rfc_3339_custom_tz("2020-03-01T00:00:00-09:00")]
4969 #[case::rfc_3339_custom_tz_no_colon("2020-03-01 00:00:00-0900")]
4970 #[case::rfc_3339_custom_tz_no_colon_and_t("2020-03-01T00:00:00-0900")]
4971 #[case::rfc_3339_custom_tz_no_minutes("2020-03-01 00:00:00-09")]
4972 #[case::rfc_3339_and_decimals("2020-03-01T00:00:00.123456Z")]
4973 #[case::without_z("2020-03-01T00:00:00")]
4974 #[case::without_z_and_decimals("2020-03-01T00:00:00.12")]
4975 #[case::space_sep_without_z("2020-03-01 00:00:00")]
4976 #[case::space_sep_without_z_and_decimals("2020-03-01 00:00:00.123456")]
4977 #[case::t_sep_without_seconds("2020-03-01T00:00")]
4978 #[case::space_sep_without_seconds("2020-03-01 00:00")]
4979 #[case::date_only("2020-03-01")]
4980 fn test_datetime_deserialization(#[case] datetime: &str) {
4981 let datetime = DateTimePayloadType::from_str(datetime).unwrap();
4982 let serialized = serde_json::to_string(&datetime).unwrap();
4983 let deserialized: DateTimePayloadType = serde_json::from_str(&serialized).unwrap();
4984 assert_eq!(datetime, deserialized);
4985 }
4986
4987 #[test]
4988 fn test_datetime_deserialization_equivalency() {
4989 let datetime_str = "2020-03-01T01:02:03.123456Z";
4990 let datetime_str_no_z = "2020-03-01T01:02:03.123456";
4991 let datetime = DateTimePayloadType::from_str(datetime_str).unwrap();
4992 let datetime_no_z = DateTimePayloadType::from_str(datetime_str_no_z).unwrap();
4993
4994 assert_eq!(datetime.timestamp(), datetime_no_z.timestamp());
4996 }
4997
4998 #[test]
4999 fn test_invalid_datetime_range_returns_clear_rfc3339_error() {
5000 let json = r#"{
5001 "key": "created_at",
5002 "range": {
5003 "gte": "2014-01-01T00:00:00BAD"
5004 }
5005 }"#;
5006
5007 let err = serde_json::from_str::<Condition>(json)
5008 .unwrap_err()
5009 .to_string();
5010
5011 assert!(err.contains("RFC3339"), "err was: {err}");
5012 assert!(err.contains("2014-01-01T00:00:00BAD"), "err was: {err}");
5013 assert!(err.contains("Example"), "err was: {err}");
5014 }
5015
5016 #[test]
5017 fn test_condition_non_string_key_returns_clear_error() {
5018 for json in [
5019 r#"{"key": null, "match": {"value": "A"}}"#,
5020 r#"{"key": 42, "match": {"value": "A"}}"#,
5021 r#"{"key": ["a"], "match": {"value": "A"}}"#,
5022 ] {
5023 let err = serde_json::from_str::<Condition>(json)
5024 .unwrap_err()
5025 .to_string();
5026
5027 assert!(
5028 err.contains("Filter condition 'key' must be a string"),
5029 "err was: {err}"
5030 );
5031 }
5032 }
5033
5034 #[test]
5037 fn test_datetime_payload_type_binary_roundtrip() {
5038 let original = DateTimePayloadType::from_str("2024-06-15T12:30:45Z").unwrap();
5039
5040 let binary = rmp_serde::to_vec(&original).expect("serialize");
5042 let restored: DateTimePayloadType = rmp_serde::from_slice(&binary).expect("deserialize");
5043
5044 assert_eq!(original, restored);
5045 }
5046
5047 #[test]
5050 fn test_range_interface_datetime_binary_roundtrip() {
5051 let dt_gte = DateTimePayloadType::from_str("2024-01-01T00:00:00Z").unwrap();
5052 let dt_lte = DateTimePayloadType::from_str("2024-12-31T23:59:59Z").unwrap();
5053
5054 let range = RangeInterface::DateTime(Range {
5055 lt: None,
5056 gt: None,
5057 gte: Some(dt_gte),
5058 lte: Some(dt_lte),
5059 });
5060
5061 let binary = rmp_serde::to_vec(&range).expect("serialize");
5063 let restored: RangeInterface = rmp_serde::from_slice(&binary).expect("deserialize");
5064
5065 assert_eq!(range, restored);
5066 }
5067
5068 #[test]
5071 fn test_condition_json_fallback_to_untagged() {
5072 let is_empty_json = r#"{"is_empty": {"key": "optional_field"}}"#;
5074 let condition: Condition = serde_json::from_str(is_empty_json).unwrap();
5075 assert_matches!(condition, Condition::IsEmpty(_));
5076
5077 let has_id_json = r#"{"has_id": [1, 2, 3]}"#;
5079 let condition: Condition = serde_json::from_str(has_id_json).unwrap();
5080 assert_matches!(condition, Condition::HasId(_));
5081
5082 let nested_json = r#"{"nested": {"key": "items", "filter": {"must": []}}}"#;
5084 let condition: Condition = serde_json::from_str(nested_json).unwrap();
5085 assert_matches!(condition, Condition::Nested(_));
5086
5087 let slice_json = r#"{"slice": {"total": 8, "index": 3}}"#;
5089 let condition: Condition = serde_json::from_str(slice_json).unwrap();
5090 assert_matches!(condition, Condition::Slice(_));
5091 }
5092
5093 #[test]
5094 fn test_slice_condition_serde_and_validation() {
5095 let condition: Condition =
5096 serde_json::from_str(r#"{"slice": {"total": 8, "index": 3}}"#).unwrap();
5097 let Condition::Slice(slice_condition) = &condition else {
5098 panic!("expected slice condition, got {condition:?}");
5099 };
5100 assert_eq!(slice_condition.slice.total.get(), 8);
5101 assert_eq!(slice_condition.slice.index, 3);
5102 condition.validate().unwrap();
5103
5104 let serialized = serde_json::to_value(&condition).unwrap();
5105 assert_eq!(
5106 serialized,
5107 serde_json::json!({"slice": {"total": 8, "index": 3}}),
5108 );
5109
5110 serde_json::from_str::<SliceCondition>(r#"{"slice": {"total": 0, "index": 0}}"#)
5112 .unwrap_err();
5113
5114 let condition: Condition =
5116 serde_json::from_str(r#"{"slice": {"total": 4, "index": 4}}"#).unwrap();
5117 condition.validate().unwrap_err();
5118 }
5119
5120 #[test]
5126 fn test_slice_point_id_hash_contract() {
5127 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
5130 assert_eq!(
5131 slice_point_id_hash(ExtendedPointId::NumId(0)),
5132 0xe849_e8bb_6ffe_2567,
5133 );
5134 assert_eq!(
5135 slice_point_id_hash(ExtendedPointId::NumId(42)),
5136 0x0fc2_553f_0761_9dd3,
5137 );
5138 assert_eq!(
5139 slice_point_id_hash(ExtendedPointId::Uuid(uuid)),
5140 0xf5d0_ca21_ba34_d504,
5141 );
5142 }
5143
5144 #[test]
5145 fn test_slice_disjoint_and_exhaustive() {
5146 let numeric_ids = (0..1000_u64).map(ExtendedPointId::NumId);
5147 let uuid_ids = (0..1000_u128).map(|seed| {
5148 ExtendedPointId::Uuid(Uuid::from_u128(
5149 seed.wrapping_mul(0x0123_4567_89ab_cdef_fedc_ba98_7654_3210),
5150 ))
5151 });
5152
5153 for point_id in numeric_ids.chain(uuid_ids) {
5154 for total in [1, 2, 5, 10] {
5156 let total = NonZeroU32::new(total).unwrap();
5157 (0..total.get())
5158 .filter(|&index| Slice { total, index }.check(point_id))
5159 .exactly_one()
5160 .unwrap();
5161 }
5162
5163 let fine_total = NonZeroU32::new(10).unwrap();
5166 let fine_index = (0..fine_total.get())
5167 .filter(|&index| {
5168 Slice {
5169 total: fine_total,
5170 index,
5171 }
5172 .check(point_id)
5173 })
5174 .exactly_one()
5175 .unwrap();
5176 assert!(
5177 Slice {
5178 total: NonZeroU32::new(5).unwrap(),
5179 index: fine_index % 5,
5180 }
5181 .check(point_id)
5182 );
5183 }
5184 }
5185
5186 #[test]
5187 fn test_datetime_wrapper_transcoding() {
5188 let expected = DateTimeWrapper(chrono::Utc::now());
5189 let transcoded = DateTimeWrapper::from_str(&expected.to_string()).unwrap();
5190 assert_eq!(expected, transcoded);
5191 }
5192
5193 #[test]
5194 fn test_timezone_ordering() {
5195 let datetimes = [
5196 "2000-06-08 00:18:53+0900",
5197 "2000-06-07 07:25:34-1100",
5198 "2000-07-10T00:18:53+0100",
5199 "2000-07-11 00:25:34-01:00",
5200 "2000-07-11 00:25:35-01",
5201 ];
5202
5203 let sorted_datetimes: Vec<_> = datetimes
5204 .iter()
5205 .enumerate()
5206 .map(|(i, s)| (i, DateTimePayloadType::from_str(s).unwrap()))
5207 .sorted_by_key(|(_, dt)| dt.timestamp())
5208 .collect();
5209
5210 sorted_datetimes
5211 .array_windows()
5212 .for_each(|[(i1, dt1), (i2, dt2)]| {
5213 assert!(
5214 i1 < i2,
5215 "i1: {}, dt1: {}, ts1: {}\ni2: {}, dt2: {}, ts2: {}",
5216 i1,
5217 dt1.0,
5218 dt1.timestamp(),
5219 i2,
5220 dt2.0,
5221 dt2.timestamp()
5222 );
5223 });
5224 }
5225
5226 #[test]
5227 fn test_geo_radius_check_point() {
5228 let radius = GeoRadius {
5229 center: GeoPoint::new_unchecked(0.0, 0.0),
5230 radius: OrderedFloat(80000.0),
5231 };
5232
5233 let inside_result = radius.check_point(&GeoPoint::new_unchecked(0.5, 0.5));
5234 assert!(inside_result);
5235
5236 let outside_result = radius.check_point(&GeoPoint::new_unchecked(1.5, 1.5));
5237 assert!(!outside_result);
5238 }
5239
5240 #[test]
5241 fn test_geo_boundingbox_check_point() {
5242 let bounding_box = GeoBoundingBox {
5243 top_left: GeoPoint::new_unchecked(-1.0, 1.0),
5244 bottom_right: GeoPoint::new_unchecked(1.0, -1.0),
5245 };
5246
5247 let inside_result = bounding_box.check_point(&GeoPoint::new_unchecked(-0.5, 0.5));
5249 assert!(inside_result);
5250
5251 let outside_result = bounding_box.check_point(&GeoPoint::new_unchecked(1.5, 1.5));
5253 assert!(!outside_result);
5254 }
5255
5256 #[test]
5257 fn test_geo_boundingbox_antimeridian_check_point() {
5258 let bounding_box = GeoBoundingBox {
5260 top_left: GeoPoint::new_unchecked(167.0, 74.071028),
5261 bottom_right: GeoPoint::new_unchecked(-66.885417, 18.7763),
5262 };
5263
5264 let inside_result =
5266 bounding_box.check_point(&GeoPoint::new_unchecked(-73.991516, 40.75798));
5267 assert!(inside_result);
5268
5269 let outside_result = bounding_box.check_point(&GeoPoint::new_unchecked(13.41053, 52.52437));
5271 assert!(!outside_result);
5272 }
5273
5274 #[test]
5275 fn test_geo_polygon_check_point() {
5276 let test_cases = [
5277 (
5279 vec![
5281 (-1.0, -1.0),
5282 (1.0, -1.0),
5283 (1.0, 1.0),
5284 (-1.0, 1.0),
5285 (-1.0, -1.0),
5286 ],
5287 vec![vec![]],
5289 vec![((0.5, 0.5), true), ((1.5, 1.5), false), ((1.0, 0.0), false)],
5291 ),
5292 (
5294 vec![
5296 (-1.0, -1.0),
5297 (1.0, 1.0),
5298 (1.0, -1.0),
5299 (-1.0, 1.0),
5300 (-1.0, -1.0),
5301 ],
5302 vec![vec![]],
5304 vec![((0.5, 0.0), true), ((0.0, 0.5), false), ((0.0, 0.0), false)],
5306 ),
5307 (
5309 vec![
5311 (-1.0, -1.0),
5312 (1.5, -1.0),
5313 (1.5, 1.5),
5314 (-1.0, 1.5),
5315 (-1.0, -1.0),
5316 ],
5317 vec![vec![
5319 (-0.5, -0.5),
5320 (-0.5, 0.5),
5321 (0.5, 0.5),
5322 (0.5, -0.5),
5323 (-0.5, -0.5),
5324 ]],
5325 vec![((0.6, 0.6), true), ((0.0, 0.0), false), ((0.5, 0.5), false)],
5327 ),
5328 ];
5329
5330 for (exterior, interiors, points) in test_cases {
5331 let polygon = build_polygon_with_interiors(exterior, interiors);
5332
5333 for ((lon, lat), expected_result) in points {
5334 let inside_result = polygon
5335 .convert()
5336 .check_point(&GeoPoint::new_unchecked(lon, lat));
5337 assert_eq!(inside_result, expected_result);
5338 }
5339 }
5340 }
5341
5342 #[test]
5343 fn test_serialize_query() {
5344 let filter = Filter {
5345 must: Some(vec![Condition::Field(FieldCondition::new_match(
5346 JsonPath::new("hello"),
5347 "world".to_owned().into(),
5348 ))]),
5349 must_not: None,
5350 should: None,
5351 min_should: None,
5352 };
5353 let json = serde_json::to_string_pretty(&filter).unwrap();
5354 eprintln!("{json}")
5355 }
5356
5357 #[test]
5358 fn test_deny_unknown_fields() {
5359 let query1 = r#"
5360 {
5361 "wrong": "query"
5362 }
5363 "#;
5364 let filter: Result<Filter, _> = serde_json::from_str(query1);
5365
5366 assert!(filter.is_err())
5367 }
5368
5369 #[test]
5370 fn test_parse_match_query() {
5371 let query = r#"
5372 {
5373 "key": "hello",
5374 "match": { "value": 42 }
5375 }
5376 "#;
5377 let condition: FieldCondition = serde_json::from_str(query).unwrap();
5378 assert_eq!(
5379 condition.r#match.unwrap(),
5380 Match::Value(MatchValue {
5381 value: ValueVariants::Integer(42)
5382 })
5383 );
5384
5385 let query = r#"
5386 {
5387 "key": "hello",
5388 "match": { "value": true }
5389 }
5390 "#;
5391 let condition: FieldCondition = serde_json::from_str(query).unwrap();
5392 assert_eq!(
5393 condition.r#match.unwrap(),
5394 Match::Value(MatchValue {
5395 value: ValueVariants::Bool(true)
5396 })
5397 );
5398
5399 let query = r#"
5400 {
5401 "key": "hello",
5402 "match": { "value": "world" }
5403 }
5404 "#;
5405
5406 let condition: FieldCondition = serde_json::from_str(query).unwrap();
5407 assert_eq!(
5408 condition.r#match.unwrap(),
5409 Match::Value(MatchValue {
5410 value: ValueVariants::String("world".to_owned())
5411 })
5412 );
5413 }
5414
5415 #[test]
5416 fn test_parse_match_any() {
5417 let query = r#"
5418 {
5419 "should": [
5420 {
5421 "key": "Jason",
5422 "match": {
5423 "any": [
5424 "Bourne",
5425 "Momoa",
5426 "Statham"
5427 ]
5428 }
5429 }
5430 ]
5431 }
5432 "#;
5433
5434 let filter: Filter = serde_json::from_str(query).unwrap();
5435 let should = filter.should.unwrap();
5436
5437 assert_eq!(should.len(), 1);
5438 let Some(Condition::Field(c)) = should.first() else {
5439 panic!("Condition::Field expected")
5440 };
5441
5442 assert_eq!(c.key.to_string(), "Jason");
5443
5444 let Match::Any(m) = c.r#match.as_ref().unwrap() else {
5445 panic!("Match::Any expected")
5446 };
5447 if let AnyVariants::Strings(kws) = &m.any {
5448 assert_eq!(kws.len(), 3);
5449 let expect: IndexSet<_, FnvBuildHasher> = ["Bourne", "Momoa", "Statham"]
5450 .into_iter()
5451 .map(|i| i.to_string())
5452 .collect();
5453 assert_eq!(kws, &expect);
5454 } else {
5455 panic!("AnyVariants::Keywords expected");
5456 }
5457 }
5458
5459 #[test]
5460 fn test_parse_match_any_mixed_types() {
5461 let query = r#"
5462 {
5463 "should": [
5464 {
5465 "key": "Jason",
5466 "match": {
5467 "any": [
5468 "Bourne",
5469 42
5470 ]
5471 }
5472 }
5473 ]
5474 }
5475 "#;
5476
5477 let result: Result<Filter, _> = serde_json::from_str(query);
5478 assert!(result.is_err());
5479 }
5480
5481 #[test]
5482 fn test_parse_nested_match_query() {
5483 let query = r#"
5484 {
5485 "key": "hello.nested",
5486 "match": { "value": 42 }
5487 }
5488 "#;
5489 let condition: FieldCondition = serde_json::from_str(query).unwrap();
5490 assert_eq!(
5491 condition.r#match.unwrap(),
5492 Match::Value(MatchValue {
5493 value: ValueVariants::Integer(42)
5494 })
5495 );
5496
5497 let query = r#"
5498 {
5499 "key": "hello.nested",
5500 "match": { "value": true }
5501 }
5502 "#;
5503 let condition: FieldCondition = serde_json::from_str(query).unwrap();
5504 assert_eq!(
5505 condition.r#match.unwrap(),
5506 Match::Value(MatchValue {
5507 value: ValueVariants::Bool(true)
5508 })
5509 );
5510
5511 let query = r#"
5512 {
5513 "key": "hello.nested",
5514 "match": { "value": "world" }
5515 }
5516 "#;
5517
5518 let condition: FieldCondition = serde_json::from_str(query).unwrap();
5519 assert_eq!(
5520 condition.r#match.unwrap(),
5521 Match::Value(MatchValue {
5522 value: ValueVariants::String("world".to_owned())
5523 })
5524 );
5525 }
5526
5527 #[test]
5528 fn test_parse_empty_query() {
5529 let query = r#"
5530 {
5531 "should": [
5532 {
5533 "is_empty" : {
5534 "key" : "Jason"
5535 }
5536 }
5537 ]
5538 }
5539 "#;
5540
5541 let filter: Filter = serde_json::from_str(query).unwrap();
5542 let should = filter.should.unwrap();
5543
5544 assert_eq!(should.len(), 1);
5545 let Some(Condition::IsEmpty(c)) = should.first() else {
5546 panic!("Condition::IsEmpty expected")
5547 };
5548
5549 assert_eq!(c.is_empty.key.to_string(), "Jason");
5550 }
5551
5552 #[test]
5553 fn test_parse_null_query() {
5554 let query = r#"
5555 {
5556 "should": [
5557 {
5558 "is_null" : {
5559 "key" : "Jason"
5560 }
5561 }
5562 ]
5563 }
5564 "#;
5565
5566 let filter: Filter = serde_json::from_str(query).unwrap();
5567 let should = filter.should.unwrap();
5568
5569 assert_eq!(should.len(), 1);
5570 let Some(Condition::IsNull(c)) = should.first() else {
5571 panic!("Condition::IsNull expected")
5572 };
5573
5574 assert_eq!(c.is_null.key.to_string(), "Jason");
5575 }
5576
5577 #[test]
5578 fn test_parse_nested_filter_query() {
5579 let query = r#"
5580 {
5581 "must": [
5582 {
5583 "nested": {
5584 "key": "country.cities",
5585 "filter": {
5586 "must": [
5587 {
5588 "key": "population",
5589 "range": {
5590 "gte": 8
5591 }
5592 },
5593 {
5594 "key": "sightseeing",
5595 "values_count": {
5596 "lt": 3
5597 }
5598 }
5599 ]
5600 }
5601 }
5602 }
5603 ]
5604 }
5605 "#;
5606 let filter: Filter = serde_json::from_str(query).unwrap();
5607 let musts = filter.must.unwrap();
5608 assert_eq!(musts.len(), 1);
5609 match musts.first() {
5610 Some(Condition::Nested(nested_condition)) => {
5611 assert_eq!(nested_condition.raw_key().to_string(), "country.cities");
5612 assert_eq!(nested_condition.array_key().to_string(), "country.cities[]");
5613 let nested_musts = nested_condition.filter().must.as_ref().unwrap();
5614 assert_eq!(nested_musts.len(), 2);
5615 let first_must = nested_musts.first().unwrap();
5616 match first_must {
5617 Condition::Field(c) => {
5618 assert_eq!(c.key.to_string(), "population");
5619 assert!(c.range.is_some());
5620 }
5621 _ => panic!("Condition::Field expected"),
5622 }
5623
5624 let second_must = nested_musts.get(1).unwrap();
5625 match second_must {
5626 Condition::Field(c) => {
5627 assert_eq!(c.key.to_string(), "sightseeing");
5628 assert!(c.values_count.is_some());
5629 }
5630 _ => panic!("Condition::Field expected"),
5631 }
5632 }
5633 o => panic!("Condition::Nested expected but got {o:?}"),
5634 };
5635 }
5636
5637 #[test]
5638 fn test_parse_single_nested_filter_query() {
5639 let query = r#"
5640 {
5641 "must": {
5642 "nested": {
5643 "key": "country.cities",
5644 "filter": {
5645 "must": {
5646 "key": "population",
5647 "range": {
5648 "gte": 8
5649 }
5650 }
5651 }
5652 }
5653 }
5654 }
5655 "#;
5656 let filter: Filter = serde_json::from_str(query).unwrap();
5657 let musts = filter.must.unwrap();
5658 assert_eq!(musts.len(), 1);
5659
5660 let first_must = musts.first().unwrap();
5661 let Condition::Nested(nested_condition) = first_must else {
5662 panic!("Condition::Nested expected but got {first_must:?}")
5663 };
5664
5665 assert_eq!(nested_condition.raw_key().to_string(), "country.cities");
5666 assert_eq!(nested_condition.array_key().to_string(), "country.cities[]");
5667
5668 let nested_must = nested_condition.filter().must.as_ref().unwrap();
5669 assert_eq!(nested_must.len(), 1);
5670
5671 let must = nested_must.first().unwrap();
5672 let Condition::Field(c) = must else {
5673 panic!("Condition::Field expected, got {must:?}")
5674 };
5675
5676 assert_eq!(c.key.to_string(), "population");
5677 assert!(c.range.is_some());
5678 }
5679
5680 #[test]
5681 fn test_payload_query_parse() {
5682 let query1 = r#"
5683 {
5684 "must": [
5685 {
5686 "key": "hello",
5687 "match": {
5688 "value": 42
5689 }
5690 },
5691 {
5692 "must_not": [
5693 {
5694 "has_id": [1, 2, 3, 4]
5695 },
5696 {
5697 "key": "geo_field",
5698 "geo_bounding_box": {
5699 "top_left": {
5700 "lon": 13.410146,
5701 "lat": 52.519289
5702 },
5703 "bottom_right": {
5704 "lon": 13.432683,
5705 "lat": 52.505582
5706 }
5707 }
5708 }
5709 ]
5710 }
5711 ]
5712 }
5713 "#;
5714
5715 let filter: Filter = serde_json::from_str(query1).unwrap();
5716 eprintln!("{filter:?}");
5717 let must = filter.must.unwrap();
5718 let _must_not = filter.must_not;
5719 assert_eq!(must.len(), 2);
5720 match must.get(1) {
5721 Some(Condition::Filter(f)) => {
5722 let must_not = &f.must_not;
5723 match must_not {
5724 Some(v) => assert_eq!(v.len(), 2),
5725 None => panic!("Filter expected"),
5726 }
5727 }
5728 _ => panic!("Condition expected"),
5729 }
5730 }
5731
5732 #[test]
5733 fn test_nested_payload_query_parse() {
5734 let query1 = r#"
5735 {
5736 "must": [
5737 {
5738 "key": "hello.nested.world",
5739 "match": {
5740 "value": 42
5741 }
5742 },
5743 {
5744 "key": "foo.nested.bar",
5745 "match": {
5746 "value": 1
5747 }
5748 }
5749 ]
5750 }
5751 "#;
5752
5753 let filter: Filter = serde_json::from_str(query1).unwrap();
5754 let must = filter.must.unwrap();
5755 assert_eq!(must.len(), 2);
5756 }
5757
5758 #[test]
5759 fn test_min_should_query_parse() {
5760 let query1 = r#"
5761 {
5762 "min_should": {
5763 "conditions": [
5764 {
5765 "key": "hello.nested.world",
5766 "match": {
5767 "value": 42
5768 }
5769 },
5770 {
5771 "key": "foo.nested.bar",
5772 "match": {
5773 "value": 1
5774 }
5775 }
5776 ],
5777 "min_count": 2
5778 }
5779 }
5780 "#;
5781
5782 let filter: Filter = serde_json::from_str(query1).unwrap();
5783 let min_should = filter.min_should.unwrap();
5784 assert_eq!(min_should.conditions.len(), 2);
5785 }
5786
5787 #[test]
5788 fn test_min_should_nested_parse() {
5789 let query1 = r#"
5790 {
5791 "must": [
5792 {
5793 "min_should": {
5794 "conditions": [
5795 {
5796 "key": "hello.nested.world",
5797 "match": {
5798 "value": 42
5799 }
5800 },
5801 {
5802 "key": "foo.nested.bar",
5803 "match": {
5804 "value": 1
5805 }
5806 }
5807 ],
5808 "min_count": 2
5809 }
5810 }
5811 ]
5812 }
5813 "#;
5814
5815 let filter: Filter = serde_json::from_str(query1).unwrap();
5816 let must = filter.must.unwrap();
5817 assert_eq!(must.len(), 1);
5818
5819 match must.first() {
5820 Some(Condition::Filter(f)) => {
5821 let min_should = &f.min_should;
5822 match min_should {
5823 Some(v) => assert_eq!(v.conditions.len(), 2),
5824 None => panic!("Filter expected"),
5825 }
5826 }
5827 _ => panic!("Condition expected"),
5828 }
5829 }
5830
5831 #[test]
5832 fn test_min_should_min_count_validation() {
5833 let invalid = Filter::new_min_should(MinShould {
5837 conditions: vec![],
5838 min_count: 0,
5839 });
5840 assert!(invalid.validate().is_err());
5841
5842 let valid = Filter::new_min_should(MinShould {
5844 conditions: vec![],
5845 min_count: 1,
5846 });
5847 assert!(valid.validate().is_ok());
5848 }
5849
5850 #[test]
5851 fn test_geo_validation() {
5852 let query1 = r#"
5853 {
5854 "must": [
5855 {
5856 "key": "geo_field",
5857 "geo_bounding_box": {
5858 "top_left": {
5859 "lon": 1113.410146,
5860 "lat": 52.519289
5861 },
5862 "bottom_right": {
5863 "lon": 13.432683,
5864 "lat": 52.505582
5865 }
5866 }
5867 }
5868 ]
5869 }
5870 "#;
5871 let filter: Result<Filter, _> = serde_json::from_str(query1);
5872 assert!(filter.is_err());
5873
5874 let query2 = r#"
5875 {
5876 "must": [
5877 {
5878 "key": "geo_field",
5879 "geo_polygon": {
5880 "exterior": {},
5881 "interiors": []
5882 }
5883 }
5884 ]
5885 }
5886 "#;
5887 let filter: Result<Filter, _> = serde_json::from_str(query2);
5888 assert!(filter.is_err());
5889
5890 let query3 = r#"
5891 {
5892 "must": [
5893 {
5894 "key": "geo_field",
5895 "geo_polygon": {
5896 "exterior":{
5897 "points": [
5898 {"lon": -12.0, "lat": -34.0},
5899 {"lon": 11.0, "lat": -22.0},
5900 {"lon": -32.0, "lat": -14.0}
5901 ]
5902 },
5903 "interiors": []
5904 }
5905 }
5906 ]
5907 }
5908 "#;
5909 let filter: Result<Filter, _> = serde_json::from_str(query3);
5910 assert!(filter.is_err());
5911
5912 let query4 = r#"
5913 {
5914 "must": [
5915 {
5916 "key": "geo_field",
5917 "geo_polygon": {
5918 "exterior": {
5919 "points": [
5920 {"lon": -12.0, "lat": -34.0},
5921 {"lon": 11.0, "lat": -22.0},
5922 {"lon": -32.0, "lat": -14.0},
5923 {"lon": -12.0, "lat": -34.0}
5924 ]
5925 },
5926 "interiors": []
5927 }
5928 }
5929 ]
5930 }
5931 "#;
5932 let filter: Result<Filter, _> = serde_json::from_str(query4);
5933 assert!(filter.is_ok());
5934
5935 let query5 = r#"
5936 {
5937 "must": [
5938 {
5939 "key": "geo_field",
5940 "geo_polygon": {
5941 "exterior": {
5942 "points": [
5943 {"lon": -12.0, "lat": -34.0},
5944 {"lon": 11.0, "lat": -22.0},
5945 {"lon": -32.0, "lat": -14.0},
5946 {"lon": -12.0, "lat": -34.0}
5947 ]
5948 },
5949 "interiors": [
5950 {
5951 "points": [
5952 {"lon": -12.0, "lat": -34.0},
5953 {"lon": 11.0, "lat": -22.0},
5954 {"lon": -32.0, "lat": -14.0}
5955 ]
5956 }
5957 ]
5958 }
5959 }
5960 ]
5961 }
5962 "#;
5963 let filter: Result<Filter, _> = serde_json::from_str(query5);
5964 assert!(filter.is_err());
5965
5966 let query6 = r#"
5967 {
5968 "must": [
5969 {
5970 "key": "geo_field",
5971 "geo_polygon": {
5972 "exterior": {
5973 "points": [
5974 {"lon": -12.0, "lat": -34.0},
5975 {"lon": 11.0, "lat": -22.0},
5976 {"lon": -32.0, "lat": -14.0},
5977 {"lon": -12.0, "lat": -34.0}
5978 ]
5979 },
5980 "interiors": [
5981 {
5982 "points": [
5983 {"lon": -12.0, "lat": -34.0},
5984 {"lon": 11.0, "lat": -22.0},
5985 {"lon": -32.0, "lat": -14.0},
5986 {"lon": -12.0, "lat": -34.0}
5987 ]
5988 }
5989 ]
5990 }
5991 }
5992 ]
5993 }
5994 "#;
5995 let filter: Result<Filter, _> = serde_json::from_str(query6);
5996 assert!(filter.is_ok());
5997 }
5998
5999 #[test]
6000 fn test_payload_parsing() {
6001 let ft = PayloadFieldSchema::FieldType(PayloadSchemaType::Keyword);
6002 let ft_json = serde_json::to_string(&ft).unwrap();
6003 eprintln!("ft_json = {ft_json:?}");
6004
6005 let ft = PayloadFieldSchema::FieldParams(PayloadSchemaParams::Text(Default::default()));
6006 let ft_json = serde_json::to_string(&ft).unwrap();
6007 eprintln!("ft_json = {ft_json:?}");
6008
6009 let query = r#""keyword""#;
6010 let field_type: PayloadSchemaType = serde_json::from_str(query).unwrap();
6011 eprintln!("field_type = {field_type:?}");
6012 }
6013
6014 #[test]
6015 fn merge_filters() {
6016 let condition1 = Condition::Field(FieldCondition::new_match(
6017 JsonPath::new("summary"),
6018 Match::new_text("Berlin"),
6019 ));
6020 let mut this = Filter::new_must(condition1.clone());
6021 this.should = Some(vec![condition1.clone()]);
6022
6023 let condition2 = Condition::Field(FieldCondition::new_match(
6024 JsonPath::new("city"),
6025 Match::new_value(ValueVariants::String("Osaka".into())),
6026 ));
6027 let other = Filter::new_must(condition2.clone());
6028
6029 let merged = this.merge(&other);
6030
6031 assert!(merged.must.is_some());
6032 assert_eq!(merged.must.as_ref().unwrap().len(), 2);
6033 assert!(merged.must_not.is_none());
6034 assert!(merged.should.is_some());
6035 assert_eq!(merged.should.as_ref().unwrap().len(), 1);
6036
6037 assert!(merged.must.as_ref().unwrap().contains(&condition1));
6038 assert!(merged.must.as_ref().unwrap().contains(&condition2));
6039 assert!(merged.should.as_ref().unwrap().contains(&condition1));
6040 }
6041
6042 #[test]
6043 fn test_payload_selector_include() {
6044 let payload = payload_json! {
6045 "a": 1,
6046 "b": {
6047 "c": 123,
6048 "e": {
6049 "f": [1,2,3],
6050 "g": 7,
6051 "h": "text",
6052 "i": [
6053 {
6054 "j": 1,
6055 "k": 2
6056
6057 },
6058 {
6059 "j": 3,
6060 "k": 4
6061 }
6062 ]
6063 }
6064 }
6065 };
6066
6067 let selector =
6069 PayloadSelector::new_include(vec![JsonPath::new("a"), JsonPath::new("b.e.f")]);
6070 let payload = selector.process(payload);
6071
6072 let expected = payload_json! {
6073 "a": 1,
6074 "b": {
6075 "e": {
6076 "f": [1,2,3],
6077 }
6078 }
6079 };
6080 assert_eq!(payload, expected);
6081 }
6082
6083 #[test]
6084 fn test_payload_selector_array_include() {
6085 let payload = payload_json! {
6086 "a": 1,
6087 "b": {
6088 "c": 123,
6089 "f": [1,2,3,4,5],
6090 }
6091 };
6092
6093 let selector = PayloadSelector::new_include(vec![JsonPath::new("a"), JsonPath::new("a")]);
6095 let payload = selector.process(payload);
6096
6097 let expected = payload_json! {
6098 "a": 1
6099 };
6100 assert_eq!(payload, expected);
6101
6102 let selector = PayloadSelector::new_include(vec![JsonPath::new("b.f[0]")]);
6104 let payload = selector.process(payload);
6105
6106 let expected = payload_json! {};
6108 assert_eq!(payload, expected);
6109 }
6110
6111 #[test]
6112 fn test_payload_selector_no_implicit_array_include() {
6113 let payload = payload_json! {
6114 "a": 1,
6115 "b": {
6116 "c": [
6117 {
6118 "d": 1,
6119 "e": 2
6120 },
6121 {
6122 "d": 3,
6123 "e": 4
6124 }
6125 ],
6126 }
6127 };
6128
6129 let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c")]);
6130 let selected_payload = selector.process(payload.clone());
6131
6132 let expected = payload_json! {
6133 "b": {
6134 "c": [
6135 {
6136 "d": 1,
6137 "e": 2
6138 },
6139 {
6140 "d": 3,
6141 "e": 4
6142 }
6143 ]
6144 }
6145 };
6146 assert_eq!(selected_payload, expected);
6147
6148 let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c[].d")]);
6150 let selected_payload = selector.process(payload.clone());
6151
6152 let expected = payload_json! {
6153 "b": {
6154 "c": [
6155 {"d": 1},
6156 {"d": 3}
6157 ]
6158 }
6159 };
6160 assert_eq!(selected_payload, expected);
6161
6162 let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c.d")]);
6164 let selected_payload = selector.process(payload);
6165
6166 let expected = payload_json! {
6167 "b": {
6168 "c": []
6169 }
6170 };
6171 assert_eq!(selected_payload, expected);
6172 }
6173
6174 #[test]
6175 fn test_payload_selector_exclude() {
6176 let payload = payload_json! {
6177 "a": 1,
6178 "b": {
6179 "c": 123,
6180 "e": {
6181 "f": [1,2,3],
6182 "g": 7,
6183 "h": "text",
6184 "i": [
6185 {
6186 "j": 1,
6187 "k": 2
6188
6189 },
6190 {
6191 "j": 3,
6192 "k": 4
6193 }
6194 ]
6195 }
6196 }
6197 };
6198
6199 let selector =
6201 PayloadSelector::new_exclude(vec![JsonPath::new("a"), JsonPath::new("b.e.f")]);
6202 let payload = selector.process(payload);
6203
6204 let expected = payload_json! {
6206 "b": {
6207 "c": 123,
6208 "e": {
6209 "g": 7,
6210 "h": "text",
6211 "i": [
6212 {
6213 "j": 1,
6214 "k": 2
6215
6216 },
6217 {
6218 "j": 3,
6219 "k": 4
6220 }
6221 ]
6222 }
6223 }
6224 };
6225 assert_eq!(payload, expected);
6226 }
6227
6228 #[test]
6229 fn test_payload_selector_array_exclude() {
6230 let payload = payload_json! {
6231 "a": 1,
6232 "b": {
6233 "c": 123,
6234 "f": [1,2,3,4,5],
6235 }
6236 };
6237
6238 let selector = PayloadSelector::new_exclude(vec![JsonPath::new("a"), JsonPath::new("a")]);
6240 let payload = selector.process(payload);
6241
6242 let expected = payload_json! {
6244 "b": {
6245 "c": 123,
6246 "f": [1,2,3,4,5],
6247 }
6248 };
6249 assert_eq!(payload, expected);
6250
6251 let selector = PayloadSelector::new_exclude(vec![JsonPath::new("b.f[0]")]);
6253
6254 let payload = selector.process(payload);
6255
6256 let expected = payload_json! {
6258 "b": {
6259 "c": 123,
6260 "f": [1,2,3,4,5],
6261 }
6262 };
6263 assert_eq!(payload, expected);
6264 }
6265
6266 #[test]
6267 fn test_extended_point_id_cbor_roundtrip() {
6268 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
6269
6270 for point_id in [ExtendedPointId::Uuid(uuid), ExtendedPointId::NumId(42)] {
6271 let cbor_bytes = serde_cbor::to_vec(&point_id).unwrap();
6272 let deserialized: ExtendedPointId = serde_cbor::from_slice(&cbor_bytes).unwrap();
6273 assert_eq!(point_id, deserialized);
6274 }
6275 }
6276
6277 #[test]
6278 fn test_filter_with_match_and_has_id_uuid_cbor_roundtrip() {
6279 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
6280 let filter = Filter {
6281 should: None,
6282 min_should: None,
6283 must: Some(vec![Condition::Field(FieldCondition::new_match(
6284 crate::segment::json_path::JsonPath::new("org_id"),
6285 Match::new_value(ValueVariants::String("test_org".to_string())),
6286 ))]),
6287 must_not: Some(vec![Condition::HasId(HasIdCondition {
6288 has_id: [ExtendedPointId::Uuid(uuid)].into_iter().collect(),
6289 })]),
6290 };
6291
6292 let cbor_bytes = serde_cbor::to_vec(&filter).unwrap();
6293 let deserialized: Filter = serde_cbor::from_slice(&cbor_bytes).unwrap();
6294 assert_eq!(filter, deserialized);
6295 }
6296}
6297
6298fn shard_key_string_example() -> String {
6299 "region_1".to_string()
6300}
6301
6302fn shard_key_number_example() -> u64 {
6303 12
6304}
6305
6306#[derive(Deserialize, Serialize, JsonSchema, Debug, Clone, PartialEq, Eq, Hash)]
6307#[serde(untagged)]
6308pub enum ShardKey {
6309 #[schemars(
6310 schema_with = "String::json_schema",
6311 example = "shard_key_string_example"
6312 )]
6313 Keyword(EcoString),
6314 #[schemars(example = "shard_key_number_example")]
6315
6316 Number(u64),
6317}
6318
6319impl From<String> for ShardKey {
6320 fn from(s: String) -> Self {
6321 ShardKey::Keyword(EcoString::from(s))
6322 }
6323}
6324
6325impl From<EcoString> for ShardKey {
6326 fn from(s: EcoString) -> Self {
6327 ShardKey::Keyword(s)
6328 }
6329}
6330
6331impl From<&str> for ShardKey {
6332 fn from(s: &str) -> Self {
6333 ShardKey::Keyword(EcoString::from(s))
6334 }
6335}
6336
6337impl From<u64> for ShardKey {
6338 fn from(n: u64) -> Self {
6339 ShardKey::Number(n)
6340 }
6341}
6342
6343impl Display for ShardKey {
6344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6345 match self {
6346 ShardKey::Keyword(keyword) => write!(f, "\"{keyword}\""),
6347 ShardKey::Number(number) => write!(f, "{number}"),
6348 }
6349 }
6350}