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