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
1183impl Eq for StrictModeConfig {}
1184
1185impl Hash for StrictModeConfig {
1186 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1187 let Self {
1188 enabled,
1189 max_query_limit,
1190 max_timeout,
1191 unindexed_filtering_retrieve,
1192 unindexed_filtering_update,
1193 search_max_hnsw_ef,
1194 search_allow_exact,
1195 search_max_oversampling: _,
1197 upsert_max_batchsize,
1198 search_max_batchsize,
1199 max_collection_vector_size_bytes,
1200 read_rate_limit,
1201 write_rate_limit,
1202 max_collection_payload_size_bytes,
1203 max_points_count,
1204 filter_max_conditions,
1205 condition_max_size,
1206 multivector_config,
1207 sparse_config,
1208 max_payload_index_count,
1209 max_resident_memory_percent,
1210 } = self;
1211 enabled.hash(state);
1212 max_query_limit.hash(state);
1213 max_timeout.hash(state);
1214 unindexed_filtering_retrieve.hash(state);
1215 unindexed_filtering_update.hash(state);
1216 search_max_hnsw_ef.hash(state);
1217 search_allow_exact.hash(state);
1218 upsert_max_batchsize.hash(state);
1219 search_max_batchsize.hash(state);
1220 max_collection_vector_size_bytes.hash(state);
1221 read_rate_limit.hash(state);
1222 write_rate_limit.hash(state);
1223 max_collection_payload_size_bytes.hash(state);
1224 max_points_count.hash(state);
1225 filter_max_conditions.hash(state);
1226 condition_max_size.hash(state);
1227 multivector_config.hash(state);
1228 sparse_config.hash(state);
1229 max_payload_index_count.hash(state);
1230 max_resident_memory_percent.hash(state);
1231 }
1232}
1233
1234#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Default)]
1236pub struct StrictModeConfigOutput {
1237 #[serde(skip_serializing_if = "Option::is_none")]
1240 pub enabled: Option<bool>,
1241
1242 #[serde(skip_serializing_if = "Option::is_none")]
1244
1245 pub max_query_limit: Option<usize>,
1246
1247 #[serde(skip_serializing_if = "Option::is_none")]
1249
1250 pub max_timeout: Option<usize>,
1251
1252 #[serde(skip_serializing_if = "Option::is_none")]
1254 pub unindexed_filtering_retrieve: Option<bool>,
1255
1256 #[serde(skip_serializing_if = "Option::is_none")]
1258 pub unindexed_filtering_update: Option<bool>,
1259
1260 #[serde(skip_serializing_if = "Option::is_none")]
1263
1264 pub search_max_hnsw_ef: Option<usize>,
1265
1266 #[serde(skip_serializing_if = "Option::is_none")]
1268 pub search_allow_exact: Option<bool>,
1269
1270 #[serde(skip_serializing_if = "Option::is_none")]
1272
1273 pub search_max_oversampling: Option<f64>,
1274
1275 #[serde(skip_serializing_if = "Option::is_none")]
1277
1278 pub upsert_max_batchsize: Option<usize>,
1279 #[serde(skip_serializing_if = "Option::is_none")]
1281
1282 pub search_max_batchsize: Option<usize>,
1283
1284 #[serde(skip_serializing_if = "Option::is_none")]
1286
1287 pub max_collection_vector_size_bytes: Option<usize>,
1288
1289 #[serde(skip_serializing_if = "Option::is_none")]
1291
1292 pub read_rate_limit: Option<usize>,
1293
1294 #[serde(skip_serializing_if = "Option::is_none")]
1296
1297 pub write_rate_limit: Option<usize>,
1298
1299 #[serde(skip_serializing_if = "Option::is_none")]
1301
1302 pub max_collection_payload_size_bytes: Option<usize>,
1303
1304 #[serde(skip_serializing_if = "Option::is_none")]
1306
1307 pub max_points_count: Option<usize>,
1308
1309 #[serde(skip_serializing_if = "Option::is_none")]
1311
1312 pub filter_max_conditions: Option<usize>,
1313
1314 #[serde(skip_serializing_if = "Option::is_none")]
1316
1317 pub condition_max_size: Option<usize>,
1318
1319 #[serde(skip_serializing_if = "Option::is_none")]
1321 pub multivector_config: Option<StrictModeMultivectorConfigOutput>,
1322
1323 #[serde(skip_serializing_if = "Option::is_none")]
1325 pub sparse_config: Option<StrictModeSparseConfigOutput>,
1326
1327 #[serde(skip_serializing_if = "Option::is_none")]
1329 pub max_payload_index_count: Option<usize>,
1330
1331 #[serde(skip_serializing_if = "Option::is_none")]
1333
1334 pub max_resident_memory_percent: Option<u8>,
1335}
1336
1337impl From<StrictModeConfig> for StrictModeConfigOutput {
1338 fn from(config: StrictModeConfig) -> Self {
1339 let StrictModeConfig {
1340 enabled,
1341 max_query_limit,
1342 max_timeout,
1343 unindexed_filtering_retrieve,
1344 unindexed_filtering_update,
1345 search_max_hnsw_ef,
1346 search_allow_exact,
1347 search_max_oversampling,
1348 upsert_max_batchsize,
1349 search_max_batchsize,
1350 max_collection_vector_size_bytes,
1351 read_rate_limit,
1352 write_rate_limit,
1353 max_collection_payload_size_bytes,
1354 max_points_count,
1355 filter_max_conditions,
1356 condition_max_size,
1357 multivector_config,
1358 sparse_config,
1359 max_payload_index_count,
1360 max_resident_memory_percent,
1361 } = config;
1362
1363 Self {
1364 enabled,
1365 max_query_limit,
1366 max_timeout,
1367 unindexed_filtering_retrieve,
1368 unindexed_filtering_update,
1369 search_max_hnsw_ef,
1370 search_allow_exact,
1371 search_max_oversampling,
1372 upsert_max_batchsize,
1373 search_max_batchsize,
1374 max_collection_vector_size_bytes,
1375 read_rate_limit,
1376 write_rate_limit,
1377 max_collection_payload_size_bytes,
1378 max_points_count,
1379 filter_max_conditions,
1380 condition_max_size,
1381 multivector_config: multivector_config.map(StrictModeMultivectorConfigOutput::from),
1382 sparse_config: sparse_config.map(StrictModeSparseConfigOutput::from),
1383 max_payload_index_count,
1384 max_resident_memory_percent,
1385 }
1386 }
1387}
1388
1389pub const DEFAULT_HNSW_EF_CONSTRUCT: usize = 100;
1390
1391impl Default for HnswConfig {
1392 fn default() -> Self {
1393 HnswConfig {
1394 m: 16,
1395 ef_construct: DEFAULT_HNSW_EF_CONSTRUCT,
1396 full_scan_threshold: DEFAULT_FULL_SCAN_THRESHOLD,
1397 max_indexing_threads: 0,
1398 on_disk: Some(false),
1399 payload_m: None,
1400 inline_storage: None,
1401 }
1402 }
1403}
1404
1405impl Default for Indexes {
1406 fn default() -> Self {
1407 Indexes::Plain {}
1408 }
1409}
1410
1411#[derive( Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq)]
1413#[serde(tag = "type", content = "options", rename_all = "snake_case")]
1414pub enum PayloadStorageType {
1415 Mmap,
1417 InRamMmap,
1419}
1420
1421#[cfg(any(test, feature = "testing"))]
1422#[allow(clippy::derivable_impls)]
1423impl Default for PayloadStorageType {
1424 fn default() -> Self {
1425 PayloadStorageType::Mmap
1426 }
1427}
1428
1429impl PayloadStorageType {
1430 pub fn from_on_disk_payload(on_disk: bool) -> Self {
1433 if on_disk { Self::Mmap } else { Self::InRamMmap }
1434 }
1435
1436 pub fn is_on_disk(&self) -> bool {
1437 match self {
1438 PayloadStorageType::Mmap => true,
1439 PayloadStorageType::InRamMmap => false,
1440 }
1441 }
1442}
1443
1444#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, )]
1445#[serde(rename_all = "snake_case")]
1446pub struct SegmentConfig {
1447 #[serde(default)]
1448 pub vector_data: HashMap<VectorNameBuf, VectorDataConfig>,
1449 #[serde(default)]
1450 #[serde(skip_serializing_if = "HashMap::is_empty")]
1451 pub sparse_vector_data: HashMap<VectorNameBuf, SparseVectorDataConfig>,
1452 pub payload_storage_type: PayloadStorageType,
1454}
1455
1456impl SegmentConfig {
1457 pub fn quantization_config(&self, vector_name: &VectorName) -> Option<&QuantizationConfig> {
1463 self.vector_data
1464 .get(vector_name)
1465 .and_then(|v| v.quantization_config.as_ref())
1466 }
1467
1468 pub fn is_any_vector_indexed(&self) -> bool {
1470 self.vector_data
1471 .values()
1472 .any(|config| config.index.is_indexed())
1473 || self
1474 .sparse_vector_data
1475 .values()
1476 .any(|config| config.is_indexed())
1477 }
1478
1479 pub fn is_any_on_disk(&self) -> bool {
1481 self.vector_data
1482 .values()
1483 .any(|config| config.storage_type.is_on_disk())
1484 || self
1485 .sparse_vector_data
1486 .values()
1487 .any(|config| config.index.index_type.is_on_disk())
1488 }
1489
1490 pub fn is_appendable(&self) -> bool {
1491 self.vector_data
1492 .values()
1493 .map(|vector_config| vector_config.is_appendable())
1494 .chain(
1495 self.sparse_vector_data
1496 .values()
1497 .map(|sparse_vector_config| {
1498 sparse_vector_config.index.index_type.is_appendable()
1499 }),
1500 )
1501 .all(|v| v)
1502 }
1503
1504 pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
1505 let Self {
1512 vector_data: _,
1513 sparse_vector_data: _,
1514 payload_storage_type: _,
1515 } = self;
1516
1517 check_vectors_map_compatible(
1518 &self.vector_data,
1519 &other.vector_data,
1520 VectorDataConfig::check_compatible,
1521 )?;
1522
1523 check_vectors_map_compatible(
1524 &self.sparse_vector_data,
1525 &other.sparse_vector_data,
1526 SparseVectorDataConfig::check_compatible,
1527 )?;
1528
1529 Ok(())
1530 }
1531}
1532
1533fn check_vectors_map_compatible<C, F>(
1534 this: &HashMap<String, C>,
1535 other: &HashMap<String, C>,
1536 check: F,
1537) -> Result<(), String>
1538where
1539 F: Fn(&C, &C) -> Result<(), String>,
1540{
1541 if this.len() != other.len() {
1542 let expected_keys: Vec<String> = this.keys().map(|k| format!("{k:?}")).collect();
1543 let actual_keys: Vec<String> = other.keys().map(|k| format!("{k:?}")).collect();
1544 return Err(format!(
1545 "Incompatible configs: expected vector storages with keys {expected_keys:?}, but got {actual_keys:?}"
1546 ));
1547 }
1548
1549 for (vector_name, config) in this {
1550 let Some(other_config) = other.get(vector_name) else {
1551 return Err(format!(
1552 "Incompatible configs: expected vector storage with key {vector_name:?} not found in other config"
1553 ));
1554 };
1555
1556 check(config, other_config)
1557 .map_err(|err| format!("Incompatible config for vector {vector_name:?}: {err}"))?;
1558 }
1559
1560 Ok(())
1561}
1562
1563#[derive(Debug, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone)]
1565pub enum VectorStorageType {
1566 Memory,
1570 Mmap,
1574 ChunkedMmap,
1578 InRamChunkedMmap,
1583 InRamMmap,
1586 Empty,
1590}
1591
1592#[cfg(any(test, feature = "testing"))]
1593#[allow(clippy::derivable_impls)]
1594impl Default for VectorStorageType {
1595 fn default() -> Self {
1596 VectorStorageType::InRamChunkedMmap
1597 }
1598}
1599
1600#[derive(
1602 Default, Debug, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone, Hash,
1603)]
1604#[serde(rename_all = "snake_case")]
1605pub enum VectorStorageDatatype {
1606 #[default]
1608 Float32,
1609 Float16,
1611 Uint8,
1613}
1614
1615#[derive(
1616 Debug, Default, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone, Hash,
1617)]
1618#[serde(rename_all = "snake_case")]
1619pub struct MultiVectorConfig {
1620 pub comparator: MultiVectorComparator,
1622}
1623
1624impl MultiVectorConfig {
1625 fn check_compatible(&self, other: &Self) -> Result<(), String> {
1626 let Self { comparator } = self;
1628
1629 if *comparator != other.comparator {
1630 return Err(format!(
1631 "Incompatible configs: expected multi-vector comparator {comparator:?}, but got {other_comparator:?}",
1632 other_comparator = other.comparator
1633 ));
1634 }
1635
1636 Ok(())
1637 }
1638}
1639
1640#[derive(
1641 Debug, Default, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Copy, Clone, Hash,
1642)]
1643#[serde(rename_all = "snake_case")]
1644pub enum MultiVectorComparator {
1645 #[default]
1646 MaxSim,
1647}
1648
1649impl VectorStorageType {
1650 pub fn from_on_disk(on_disk: bool) -> Self {
1653 if on_disk {
1654 Self::ChunkedMmap
1655 } else {
1656 Self::InRamChunkedMmap
1657 }
1658 }
1659
1660 pub fn is_on_disk(&self) -> bool {
1662 match self {
1663 Self::Memory | Self::InRamChunkedMmap | Self::InRamMmap => false,
1664 Self::Mmap | Self::ChunkedMmap => true,
1665 Self::Empty => true,
1670 }
1671 }
1672
1673 pub fn is_empty(&self) -> bool {
1675 matches!(self, Self::Empty)
1676 }
1677}
1678
1679#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, )]
1681#[serde(rename_all = "snake_case")]
1682pub struct VectorDataConfig {
1683 pub size: usize,
1685 pub distance: Distance,
1687 pub storage_type: VectorStorageType,
1689 pub index: Indexes,
1691 pub quantization_config: Option<QuantizationConfig>,
1693 #[serde(default, skip_serializing_if = "Option::is_none")]
1695 pub multivector_config: Option<MultiVectorConfig>,
1696 #[serde(default, skip_serializing_if = "Option::is_none")]
1698 pub datatype: Option<VectorStorageDatatype>,
1699}
1700
1701impl VectorDataConfig {
1702 pub fn is_appendable(&self) -> bool {
1706 let is_index_appendable = match self.index {
1707 Indexes::Plain {} => true,
1708 Indexes::Hnsw(_) => false,
1709 };
1710 let is_storage_appendable = match self.storage_type {
1711 VectorStorageType::Memory => true,
1712 VectorStorageType::Mmap => false,
1713 VectorStorageType::ChunkedMmap => true,
1714 VectorStorageType::InRamChunkedMmap => true,
1715 VectorStorageType::InRamMmap => false,
1716 VectorStorageType::Empty => false,
1717 };
1718 is_index_appendable && is_storage_appendable
1719 }
1720
1721 pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
1722 let Self {
1727 size,
1728 distance,
1729 storage_type: _,
1730 index: _,
1731 quantization_config: _,
1732 multivector_config,
1733 datatype,
1734 } = self;
1735
1736 if *size != other.size {
1737 return Err(format!(
1738 "Incompatible configs: expected vector size {size}, but got {other_size}",
1739 other_size = other.size
1740 ));
1741 }
1742
1743 if *distance != other.distance {
1744 return Err(format!(
1745 "Incompatible configs: expected distance {distance:?}, but got {other_distance:?}",
1746 other_distance = other.distance
1747 ));
1748 }
1749
1750 let left_datatype = datatype.unwrap_or(VectorStorageDatatype::Float32);
1751 let right_datatype = other.datatype.unwrap_or(VectorStorageDatatype::Float32);
1752 if left_datatype != right_datatype {
1753 return Err(format!(
1754 "Incompatible configs: expected vector storage datatype {left_datatype:?}, but got {right_datatype:?}",
1755 ));
1756 }
1757
1758 match (multivector_config, &other.multivector_config) {
1759 (None, None) => {}
1760 (Some(this), Some(other)) => {
1761 MultiVectorConfig::check_compatible(this, other)?;
1762 }
1763 _ => {
1764 return Err(format!(
1765 "Incompatible configs: expected multivector config {this_multivector_config:?}, but got {other_multivector_config:?}",
1766 this_multivector_config = multivector_config,
1767 other_multivector_config = other.multivector_config
1768 ));
1769 }
1770 }
1771 Ok(())
1772 }
1773}
1774
1775#[derive(
1776 Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, JsonSchema,
1777)]
1778#[serde(rename_all = "snake_case")]
1779pub enum SparseVectorStorageType {
1780 #[default]
1782 Mmap,
1783 Empty,
1786}
1787
1788impl SparseVectorStorageType {
1789 pub fn is_on_disk(&self) -> bool {
1791 match self {
1792 Self::Mmap | Self::Empty => true,
1795 }
1796 }
1797}
1798
1799#[derive(
1801 Copy, Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Validate,
1802)]
1803#[serde(rename_all = "snake_case")]
1804pub struct SparseVectorDataConfig {
1805 pub index: SparseIndexConfig,
1807
1808 #[serde(default = "default_sparse_vector_storage_type_when_not_in_config")]
1810 pub storage_type: SparseVectorStorageType,
1811
1812 #[serde(default, skip_serializing_if = "Option::is_none")]
1815 pub modifier: Option<Modifier>,
1816}
1817
1818fn default_sparse_vector_storage_type_when_not_in_config() -> SparseVectorStorageType {
1820 SparseVectorStorageType::default()
1821}
1822
1823impl SparseVectorDataConfig {
1824 pub fn is_indexed(&self) -> bool {
1825 true
1826 }
1827
1828 pub fn check_compatible(&self, other: &Self) -> Result<(), String> {
1829 let Self {
1833 index: _,
1834 storage_type: _,
1835 modifier,
1836 } = self;
1837
1838 if modifier != &other.modifier {
1839 return Err(format!(
1840 "Incompatible configs: expected sparse vector modifier {modifier:?}, but got {other_modifier:?}",
1841 other_modifier = other.modifier
1842 ));
1843 }
1844
1845 Ok(())
1846 }
1847}
1848
1849pub const DEFAULT_FULL_SCAN_THRESHOLD: usize = 10_000;
1851
1852pub const DEFAULT_SPARSE_FULL_SCAN_THRESHOLD: usize = 5_000;
1853
1854#[derive(Debug, Deserialize, Serialize, Clone)]
1856#[serde(rename_all = "snake_case")]
1857pub struct SegmentState {
1858 #[serde(default)]
1859 pub initial_version: Option<SeqNumberType>,
1860 pub version: Option<SeqNumberType>,
1861 pub config: SegmentConfig,
1862}
1863
1864pub type RawGeoPoint = (f64, f64);
1865
1866#[derive(
1868 Debug,
1869 Deserialize,
1870 Serialize,
1871 JsonSchema,
1872 Clone,
1873 Copy,
1874 PartialEq,
1875 Eq,
1876 Hash,
1877 Default,
1878 PartialOrd,
1879 Ord,
1880 Pod,
1881 Zeroable,
1882)]
1883#[serde(try_from = "GeoPointShadow")]
1884#[repr(C)]
1885pub struct GeoPoint {
1886 pub lon: OrderedFloat<f64>,
1887 pub lat: OrderedFloat<f64>,
1888}
1889
1890#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
1892pub struct GeoLineString {
1893 pub points: Vec<GeoPoint>,
1894}
1895
1896#[derive(Deserialize)]
1897struct GeoPointShadow {
1898 pub lon: f64,
1899 pub lat: f64,
1900}
1901
1902#[derive(Debug)]
1903pub struct GeoPointValidationError {
1904 pub lon: f64,
1905 pub lat: f64,
1906}
1907
1908impl std::fmt::Display for GeoPointValidationError {
1910 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1911 write!(
1912 formatter,
1913 "Wrong format of GeoPoint payload: expected `lat` = {} within [-90;90] and `lon` = {} within [-180;180]",
1914 self.lat, self.lon,
1915 )
1916 }
1917}
1918
1919impl GeoPoint {
1920 pub fn validate(lon: f64, lat: f64) -> Result<(), GeoPointValidationError> {
1921 let max_lon = 180f64;
1922 let min_lon = -180f64;
1923 let max_lat = 90f64;
1924 let min_lat = -90f64;
1925
1926 if !(min_lon..=max_lon).contains(&lon) || !(min_lat..=max_lat).contains(&lat) {
1927 return Err(GeoPointValidationError { lon, lat });
1928 }
1929 Ok(())
1930 }
1931
1932 pub fn new(lon: f64, lat: f64) -> Result<Self, GeoPointValidationError> {
1933 Self::validate(lon, lat)?;
1934 Ok(Self::new_unchecked(lon, lat))
1935 }
1936
1937 pub const fn new_unchecked(lon: f64, lat: f64) -> Self {
1938 GeoPoint {
1939 lon: OrderedFloat(lon),
1940 lat: OrderedFloat(lat),
1941 }
1942 }
1943}
1944
1945impl TryFrom<GeoPointShadow> for GeoPoint {
1946 type Error = GeoPointValidationError;
1947
1948 fn try_from(value: GeoPointShadow) -> Result<Self, Self::Error> {
1949 let GeoPointShadow { lon, lat } = value;
1950 GeoPoint::validate(lon, lat)?;
1951
1952 Ok(Self::new_unchecked(lon, lat))
1953 }
1954}
1955
1956impl From<GeoPoint> for geo::Point {
1957 fn from(
1958 GeoPoint {
1959 lon: OrderedFloat(lon),
1960 lat: OrderedFloat(lat),
1961 }: GeoPoint,
1962 ) -> Self {
1963 Self::new(lon, lat)
1964 }
1965}
1966
1967impl From<RawGeoPoint> for GeoPoint {
1968 fn from((lon, lat): RawGeoPoint) -> Self {
1969 GeoPoint::new(lon, lat).expect("invalid GeoPoint coordinates")
1970 }
1971}
1972
1973impl From<GeoPoint> for RawGeoPoint {
1974 fn from(geo_point: GeoPoint) -> Self {
1975 (geo_point.lon.0, geo_point.lat.0)
1976 }
1977}
1978
1979pub trait PayloadContainer {
1980 fn get_value(&self, path: &JsonPath) -> MultiValue<&Value>;
1983
1984 fn get_value_cloned(&self, path: &JsonPath) -> MultiValue<Value> {
1985 self.get_value(path).into_iter().cloned().collect()
1986 }
1987}
1988
1989macro_rules! payload_json {
1993 ($($tt:tt)*) => {
1994 match ::serde_json::json!( { $($tt)* } ) {
1995 ::serde_json::Value::Object(map) => $crate::segment::types::Payload(map),
1996 _ => unreachable!(),
1997 }
1998 };
1999}
2000
2001#[allow(clippy::unnecessary_wraps)] fn payload_example() -> Option<Payload> {
2003 Some(payload_json! {
2004 "city": "London",
2005 "color": "green",
2006 })
2007}
2008
2009#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize, JsonSchema, Hash)]
2010#[schemars(example = "payload_example")]
2011pub struct Payload(pub Map<String, Value>);
2012
2013impl Payload {
2014 pub fn merge(&mut self, value: &Payload) {
2015 utils::merge_map(&mut self.0, &value.0)
2016 }
2017
2018 pub fn merge_by_key(&mut self, value: &Payload, key: &JsonPath) {
2019 JsonPath::value_set(Some(key), &mut self.0, &value.0);
2020 }
2021
2022 pub fn remove(&mut self, path: &JsonPath) -> Vec<Value> {
2023 path.value_remove(&mut self.0).to_vec()
2024 }
2025
2026 pub fn len(&self) -> usize {
2027 self.0.len()
2028 }
2029
2030 pub fn is_empty(&self) -> bool {
2031 self.0.is_empty()
2032 }
2033
2034 pub fn contains_key(&self, key: &str) -> bool {
2035 self.0.contains_key(key)
2036 }
2037
2038 pub fn keys(&self) -> impl Iterator<Item = &String> {
2039 self.0.keys()
2040 }
2041}
2042
2043impl PayloadContainer for Map<String, Value> {
2044 fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2045 path.value_get(self)
2046 }
2047}
2048
2049impl PayloadContainer for Payload {
2050 fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2051 path.value_get(&self.0)
2052 }
2053}
2054
2055impl PayloadContainer for OwnedPayloadRef<'_> {
2056 fn get_value(&self, path: &JsonPath) -> MultiValue<&Value> {
2057 path.value_get(self.as_ref())
2058 }
2059}
2060
2061impl Default for Payload {
2062 fn default() -> Self {
2063 Payload(Map::new())
2064 }
2065}
2066
2067impl IntoIterator for Payload {
2068 type Item = (String, Value);
2069 type IntoIter = serde_json::map::IntoIter;
2070
2071 fn into_iter(self) -> serde_json::map::IntoIter {
2072 self.0.into_iter()
2073 }
2074}
2075
2076impl From<Map<String, Value>> for Payload {
2077 fn from(value: serde_json::Map<String, Value>) -> Self {
2078 Payload(value)
2079 }
2080}
2081
2082#[derive(Clone, Debug)]
2083pub enum OwnedPayloadRef<'a> {
2084 Ref(&'a Map<String, Value>),
2085 Owned(Rc<Map<String, Value>>),
2086}
2087
2088impl Deref for OwnedPayloadRef<'_> {
2089 type Target = Map<String, Value>;
2090
2091 fn deref(&self) -> &Self::Target {
2092 match self {
2093 OwnedPayloadRef::Ref(reference) => reference,
2094 OwnedPayloadRef::Owned(owned) => owned.deref(),
2095 }
2096 }
2097}
2098
2099impl AsRef<Map<String, Value>> for OwnedPayloadRef<'_> {
2100 fn as_ref(&self) -> &Map<String, Value> {
2101 match self {
2102 OwnedPayloadRef::Ref(reference) => reference,
2103 OwnedPayloadRef::Owned(owned) => owned.deref(),
2104 }
2105 }
2106}
2107
2108impl From<Payload> for OwnedPayloadRef<'_> {
2109 fn from(payload: Payload) -> Self {
2110 OwnedPayloadRef::Owned(Rc::new(payload.0))
2111 }
2112}
2113
2114impl From<Map<String, Value>> for OwnedPayloadRef<'_> {
2115 fn from(payload: Map<String, Value>) -> Self {
2116 OwnedPayloadRef::Owned(Rc::new(payload))
2117 }
2118}
2119
2120impl<'a> From<&'a Payload> for OwnedPayloadRef<'a> {
2121 fn from(payload: &'a Payload) -> Self {
2122 OwnedPayloadRef::Ref(&payload.0)
2123 }
2124}
2125
2126impl<'a> From<&'a Map<String, Value>> for OwnedPayloadRef<'a> {
2127 fn from(payload: &'a Map<String, Value>) -> Self {
2128 OwnedPayloadRef::Ref(payload)
2129 }
2130}
2131
2132#[derive(Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Clone)]
2143#[serde(untagged, rename_all = "snake_case")]
2144pub enum PayloadVariant<T> {
2145 List(Vec<T>),
2146 Value(T),
2147}
2148
2149#[derive(
2151 Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq, Hash, Eq, EnumIter,
2152)]
2153#[serde(rename_all = "snake_case")]
2154pub enum PayloadSchemaType {
2155 Keyword,
2156 Integer,
2157 Float,
2158 Geo,
2159 Text,
2160 Bool,
2161 Datetime,
2162 Uuid,
2163}
2164
2165impl PayloadSchemaType {
2166 pub fn name(&self) -> &'static str {
2168 serde_variant::to_variant_name(&self).unwrap_or("unknown")
2169 }
2170
2171 pub fn expand(&self) -> PayloadSchemaParams {
2172 match self {
2173 Self::Keyword => PayloadSchemaParams::Keyword(KeywordIndexParams::default()),
2174 Self::Integer => PayloadSchemaParams::Integer(IntegerIndexParams::default()),
2175 Self::Float => PayloadSchemaParams::Float(FloatIndexParams::default()),
2176 Self::Geo => PayloadSchemaParams::Geo(GeoIndexParams::default()),
2177 Self::Text => PayloadSchemaParams::Text(TextIndexParams::default()),
2178 Self::Bool => PayloadSchemaParams::Bool(BoolIndexParams::default()),
2179 Self::Datetime => PayloadSchemaParams::Datetime(DatetimeIndexParams::default()),
2180 Self::Uuid => PayloadSchemaParams::Uuid(UuidIndexParams::default()),
2181 }
2182 }
2183}
2184
2185#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Hash, Eq)]
2187#[serde(untagged, rename_all = "snake_case")]
2188
2189pub enum PayloadSchemaParams {
2190 Keyword(KeywordIndexParams),
2191 Integer(IntegerIndexParams),
2192 Float(FloatIndexParams),
2193 Geo(GeoIndexParams),
2194 Text(TextIndexParams),
2195 Bool(BoolIndexParams),
2196 Datetime(DatetimeIndexParams),
2197 Uuid(UuidIndexParams),
2198}
2199
2200impl PayloadSchemaParams {
2201 pub fn name(&self) -> &'static str {
2203 self.kind().name()
2204 }
2205
2206 pub fn kind(&self) -> PayloadSchemaType {
2207 match self {
2208 PayloadSchemaParams::Keyword(_) => PayloadSchemaType::Keyword,
2209 PayloadSchemaParams::Integer(_) => PayloadSchemaType::Integer,
2210 PayloadSchemaParams::Float(_) => PayloadSchemaType::Float,
2211 PayloadSchemaParams::Geo(_) => PayloadSchemaType::Geo,
2212 PayloadSchemaParams::Text(_) => PayloadSchemaType::Text,
2213 PayloadSchemaParams::Bool(_) => PayloadSchemaType::Bool,
2214 PayloadSchemaParams::Datetime(_) => PayloadSchemaType::Datetime,
2215 PayloadSchemaParams::Uuid(_) => PayloadSchemaType::Uuid,
2216 }
2217 }
2218
2219 pub fn tenant_optimization(&self) -> bool {
2220 match self {
2221 PayloadSchemaParams::Keyword(keyword) => keyword.is_tenant.unwrap_or_default(),
2222 PayloadSchemaParams::Integer(integer) => integer.is_principal.unwrap_or_default(),
2223 PayloadSchemaParams::Float(float) => float.is_principal.unwrap_or_default(),
2224 PayloadSchemaParams::Datetime(datetime) => datetime.is_principal.unwrap_or_default(),
2225 PayloadSchemaParams::Uuid(uuid) => uuid.is_tenant.unwrap_or_default(),
2226 PayloadSchemaParams::Geo(_)
2227 | PayloadSchemaParams::Text(_)
2228 | PayloadSchemaParams::Bool(_) => false,
2229 }
2230 }
2231
2232 pub fn is_on_disk(&self) -> bool {
2233 match self {
2234 PayloadSchemaParams::Keyword(i) => i.on_disk.unwrap_or_default(),
2235 PayloadSchemaParams::Integer(i) => i.on_disk.unwrap_or_default(),
2236 PayloadSchemaParams::Float(i) => i.on_disk.unwrap_or_default(),
2237 PayloadSchemaParams::Datetime(i) => i.on_disk.unwrap_or_default(),
2238 PayloadSchemaParams::Uuid(i) => i.on_disk.unwrap_or_default(),
2239 PayloadSchemaParams::Text(i) => i.on_disk.unwrap_or_default(),
2240 PayloadSchemaParams::Geo(i) => i.on_disk.unwrap_or_default(),
2241 PayloadSchemaParams::Bool(i) => i.on_disk.unwrap_or_default(),
2242 }
2243 }
2244
2245 pub fn enable_hnsw(&self) -> bool {
2246 match self {
2247 PayloadSchemaParams::Keyword(params) => params.enable_hnsw.unwrap_or(true),
2248 PayloadSchemaParams::Integer(params) => params.enable_hnsw.unwrap_or(true),
2249 PayloadSchemaParams::Float(params) => params.enable_hnsw.unwrap_or(true),
2250 PayloadSchemaParams::Datetime(params) => params.enable_hnsw.unwrap_or(true),
2251 PayloadSchemaParams::Uuid(params) => params.enable_hnsw.unwrap_or(true),
2252 PayloadSchemaParams::Text(params) => params.enable_hnsw.unwrap_or(true),
2253 PayloadSchemaParams::Geo(params) => params.enable_hnsw.unwrap_or(true),
2254 PayloadSchemaParams::Bool(params) => params.enable_hnsw.unwrap_or(true),
2255 }
2256 }
2257}
2258
2259impl Validate for PayloadSchemaParams {
2260 fn validate(&self) -> Result<(), ValidationErrors> {
2261 match self {
2262 PayloadSchemaParams::Keyword(_) => Ok(()),
2263 PayloadSchemaParams::Integer(integer_index_params) => integer_index_params.validate(),
2264 PayloadSchemaParams::Float(_) => Ok(()),
2265 PayloadSchemaParams::Geo(_) => Ok(()),
2266 PayloadSchemaParams::Text(_) => Ok(()),
2267 PayloadSchemaParams::Bool(_) => Ok(()),
2268 PayloadSchemaParams::Datetime(_) => Ok(()),
2269 PayloadSchemaParams::Uuid(_) => Ok(()),
2270 }
2271 }
2272}
2273
2274#[derive(Clone, Debug, Eq, Deserialize, Serialize, JsonSchema)]
2275#[serde(untagged, rename_all = "snake_case")]
2276pub enum PayloadFieldSchema {
2277 FieldType(PayloadSchemaType),
2278 FieldParams(PayloadSchemaParams),
2279}
2280
2281impl PartialEq for PayloadFieldSchema {
2282 fn eq(&self, other: &Self) -> bool {
2283 match (self, other) {
2284 (Self::FieldType(this), Self::FieldType(other)) => this == other,
2285 (Self::FieldParams(this), Self::FieldParams(other)) => this == other,
2286 (Self::FieldType(this), Self::FieldParams(other)) => &this.expand() == other,
2287 (Self::FieldParams(this), Self::FieldType(other)) => this == &other.expand(),
2288 }
2289 }
2290}
2291
2292impl hash::Hash for PayloadFieldSchema {
2293 fn hash<H: hash::Hasher>(&self, state: &mut H) {
2294 match self {
2295 PayloadFieldSchema::FieldType(default) => default.expand().hash(state),
2296 PayloadFieldSchema::FieldParams(params) => params.hash(state),
2297 }
2298 }
2299}
2300
2301impl Validate for PayloadFieldSchema {
2302 fn validate(&self) -> Result<(), ValidationErrors> {
2303 match self {
2304 PayloadFieldSchema::FieldType(_) => Ok(()), PayloadFieldSchema::FieldParams(payload_schema_params) => {
2306 payload_schema_params.validate()
2307 }
2308 }
2309 }
2310}
2311
2312impl Display for PayloadFieldSchema {
2313 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2314 match self {
2315 PayloadFieldSchema::FieldType(t) => write!(f, "{}", t.name()),
2316 PayloadFieldSchema::FieldParams(params) => match params {
2317 PayloadSchemaParams::Keyword(_)
2318 | PayloadSchemaParams::Float(_)
2319 | PayloadSchemaParams::Geo(_)
2320 | PayloadSchemaParams::Bool(_)
2321 | PayloadSchemaParams::Datetime(_)
2322 | PayloadSchemaParams::Uuid(_) => write!(f, "{}", params.name()),
2323 PayloadSchemaParams::Integer(integer_params) => {
2324 let range = integer_params.range.unwrap_or(true);
2325 let lookup = integer_params.lookup.unwrap_or(true);
2326 if range && lookup {
2327 write!(f, "integer")
2328 } else {
2329 write!(f, "integer (with range: {range}, lookup: {lookup})")
2330 }
2331 }
2332 PayloadSchemaParams::Text(text_params) => {
2333 if text_params.phrase_matching.unwrap_or_default() {
2334 write!(f, "text (with phrase_matching: true)")
2335 } else {
2336 write!(f, "text")
2337 }
2338 }
2339 },
2340 }
2341 }
2342}
2343
2344impl PayloadFieldSchema {
2345 pub fn expand(&self) -> Cow<'_, PayloadSchemaParams> {
2346 match self {
2347 PayloadFieldSchema::FieldType(t) => Cow::Owned(t.expand()),
2348 PayloadFieldSchema::FieldParams(p) => Cow::Borrowed(p),
2349 }
2350 }
2351
2352 pub fn name(&self) -> &'static str {
2354 match self {
2355 PayloadFieldSchema::FieldType(field_type) => field_type.name(),
2356 PayloadFieldSchema::FieldParams(field_params) => field_params.name(),
2357 }
2358 }
2359
2360 pub fn is_tenant(&self) -> bool {
2361 match self {
2362 PayloadFieldSchema::FieldType(_) => false,
2363 PayloadFieldSchema::FieldParams(params) => params.tenant_optimization(),
2364 }
2365 }
2366
2367 pub fn is_on_disk(&self) -> bool {
2368 match self {
2369 PayloadFieldSchema::FieldType(_) => false,
2370 PayloadFieldSchema::FieldParams(params) => params.is_on_disk(),
2371 }
2372 }
2373
2374 pub fn kind(&self) -> PayloadSchemaType {
2375 match self {
2376 PayloadFieldSchema::FieldType(t) => *t,
2377 PayloadFieldSchema::FieldParams(p) => p.kind(),
2378 }
2379 }
2380
2381 pub fn supports_match(&self) -> bool {
2383 match self {
2384 PayloadFieldSchema::FieldType(payload_schema_type) => match payload_schema_type {
2385 PayloadSchemaType::Keyword => true,
2386 PayloadSchemaType::Integer => true,
2387 PayloadSchemaType::Uuid => true,
2388 PayloadSchemaType::Bool => true,
2389 PayloadSchemaType::Float => false,
2390 PayloadSchemaType::Geo => false,
2391 PayloadSchemaType::Text => false,
2392 PayloadSchemaType::Datetime => false,
2393 },
2394 PayloadFieldSchema::FieldParams(payload_schema_params) => match payload_schema_params {
2395 PayloadSchemaParams::Keyword(_) => true,
2396 PayloadSchemaParams::Integer(integer_index_params) => {
2397 integer_index_params.lookup == Some(true)
2398 }
2399 PayloadSchemaParams::Uuid(_) => true,
2400 PayloadSchemaParams::Bool(_) => true,
2401 PayloadSchemaParams::Float(_) => false,
2402 PayloadSchemaParams::Geo(_) => false,
2403 PayloadSchemaParams::Text(_) => false,
2404 PayloadSchemaParams::Datetime(_) => false,
2405 },
2406 }
2407 }
2408
2409 pub fn enable_hnsw(&self) -> bool {
2410 match self {
2411 PayloadFieldSchema::FieldType(_) => true,
2412 PayloadFieldSchema::FieldParams(p) => p.enable_hnsw(),
2413 }
2414 }
2415}
2416
2417impl From<PayloadSchemaType> for PayloadFieldSchema {
2418 fn from(payload_schema_type: PayloadSchemaType) -> Self {
2419 PayloadFieldSchema::FieldType(payload_schema_type)
2420 }
2421}
2422
2423impl TryFrom<PayloadIndexInfo> for PayloadFieldSchema {
2424 type Error = String;
2425
2426 fn try_from(index_info: PayloadIndexInfo) -> Result<Self, Self::Error> {
2427 let PayloadIndexInfo {
2428 data_type,
2429 params,
2430 points: _,
2431 } = index_info;
2432
2433 match params {
2434 None => Ok(PayloadFieldSchema::FieldType(data_type)),
2435
2436 Some(params) if data_type == params.kind() => {
2437 Ok(PayloadFieldSchema::FieldParams(params))
2438 }
2439
2440 Some(params) => Err(format!(
2441 "payload field with type {data_type:?} has parameters of type {:?}",
2442 params.kind(),
2443 )),
2444 }
2445 }
2446}
2447
2448#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2449#[serde(untagged)]
2450pub enum ValueVariants {
2451 String(String),
2452 Integer(IntPayloadType),
2453 Bool(bool),
2454}
2455
2456impl ValueVariants {
2457 pub fn to_value(&self) -> Value {
2458 match self {
2459 ValueVariants::String(keyword) => Value::String(keyword.clone()),
2460 &ValueVariants::Integer(integer) => Value::Number(integer.into()),
2461 &ValueVariants::Bool(flag) => Value::Bool(flag),
2462 }
2463 }
2464}
2465
2466#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
2467#[serde(untagged)]
2468pub enum AnyVariants {
2469 Strings(IndexSet<String, FnvBuildHasher>),
2470 Integers(IndexSet<IntPayloadType, FnvBuildHasher>),
2471}
2472
2473impl Hash for AnyVariants {
2474 fn hash<H: Hasher>(&self, state: &mut H) {
2475 mem::discriminant(self).hash(state);
2476 match self {
2477 AnyVariants::Strings(index_set) => {
2478 for item in index_set {
2479 item.hash(state);
2480 }
2481 }
2482 AnyVariants::Integers(index_set) => {
2483 for item in index_set {
2484 item.hash(state);
2485 }
2486 }
2487 }
2488 }
2489}
2490
2491impl AnyVariants {
2492 pub fn len(&self) -> usize {
2493 match self {
2494 AnyVariants::Strings(index_set) => index_set.len(),
2495 AnyVariants::Integers(index_set) => index_set.len(),
2496 }
2497 }
2498
2499 pub fn is_empty(&self) -> bool {
2500 match self {
2501 AnyVariants::Strings(index_set) => index_set.is_empty(),
2502 AnyVariants::Integers(index_set) => index_set.is_empty(),
2503 }
2504 }
2505}
2506
2507#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2509#[serde(rename_all = "snake_case")]
2510pub struct MatchValue {
2511 pub value: ValueVariants,
2512}
2513
2514#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2516#[serde(rename_all = "snake_case")]
2517pub struct MatchText {
2518 pub text: String,
2519}
2520
2521#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2523#[serde(rename_all = "snake_case")]
2524pub struct MatchTextAny {
2525 pub text_any: String,
2526}
2527
2528impl<S: Into<String>> From<S> for MatchText {
2529 fn from(text: S) -> Self {
2530 MatchText { text: text.into() }
2531 }
2532}
2533
2534#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2536#[serde(rename_all = "snake_case")]
2537pub struct MatchPhrase {
2538 pub phrase: String,
2539}
2540
2541impl<S: Into<String>> From<S> for MatchPhrase {
2542 fn from(text: S) -> Self {
2543 MatchPhrase {
2544 phrase: text.into(),
2545 }
2546 }
2547}
2548
2549#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2551#[serde(rename_all = "snake_case")]
2552pub struct MatchAny {
2553 pub any: AnyVariants,
2554}
2555
2556#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2558#[serde(rename_all = "snake_case")]
2559pub struct MatchExcept {
2560 pub except: AnyVariants,
2561}
2562
2563#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
2565#[serde(untagged, rename_all = "snake_case")]
2566pub enum MatchInterface {
2567 Value(MatchValue),
2568 Text(MatchText),
2569 TextAny(MatchTextAny),
2570 Phrase(MatchPhrase),
2571 Any(MatchAny),
2572 Except(MatchExcept),
2573}
2574
2575#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2577#[serde(untagged, from = "MatchInterface")]
2578pub enum Match {
2579 Value(MatchValue),
2580 Text(MatchText),
2581 TextAny(MatchTextAny),
2582 Phrase(MatchPhrase),
2583 Any(MatchAny),
2584 Except(MatchExcept),
2585}
2586
2587impl Match {
2588 pub fn new_value(value: ValueVariants) -> Self {
2589 Self::Value(MatchValue { value })
2590 }
2591
2592 pub fn new_text(text: &str) -> Self {
2593 Self::Text(MatchText { text: text.into() })
2594 }
2595
2596 pub fn new_any(any: AnyVariants) -> Self {
2597 Self::Any(MatchAny { any })
2598 }
2599
2600 pub fn new_except(except: AnyVariants) -> Self {
2601 Self::Except(MatchExcept { except })
2602 }
2603}
2604
2605impl From<AnyVariants> for Match {
2606 fn from(any: AnyVariants) -> Self {
2607 Self::Any(MatchAny { any })
2608 }
2609}
2610
2611impl From<MatchInterface> for Match {
2612 fn from(value: MatchInterface) -> Self {
2613 match value {
2614 MatchInterface::Value(value) => Self::Value(MatchValue { value: value.value }),
2615 MatchInterface::Text(text) => Self::Text(MatchText { text: text.text }),
2616 MatchInterface::TextAny(text_any) => Self::TextAny(MatchTextAny {
2617 text_any: text_any.text_any,
2618 }),
2619 MatchInterface::Any(any) => Self::Any(MatchAny { any: any.any }),
2620 MatchInterface::Except(except) => Self::Except(MatchExcept {
2621 except: except.except,
2622 }),
2623 MatchInterface::Phrase(MatchPhrase { phrase }) => Self::Phrase(MatchPhrase { phrase }),
2624 }
2625 }
2626}
2627
2628impl From<bool> for Match {
2629 fn from(flag: bool) -> Self {
2630 Self::Value(MatchValue {
2631 value: ValueVariants::Bool(flag),
2632 })
2633 }
2634}
2635
2636impl From<String> for Match {
2637 fn from(keyword: String) -> Self {
2638 Self::Value(MatchValue {
2639 value: ValueVariants::String(keyword),
2640 })
2641 }
2642}
2643
2644impl From<EcoString> for Match {
2645 fn from(keyword: EcoString) -> Self {
2646 Self::Value(MatchValue {
2647 value: ValueVariants::String(keyword.into()),
2648 })
2649 }
2650}
2651
2652impl From<IntPayloadType> for Match {
2653 fn from(integer: IntPayloadType) -> Self {
2654 Self::Value(MatchValue {
2655 value: ValueVariants::Integer(integer),
2656 })
2657 }
2658}
2659
2660impl From<Vec<String>> for Match {
2661 fn from(keywords: Vec<String>) -> Self {
2662 let keywords: IndexSet<String, FnvBuildHasher> = keywords.into_iter().collect();
2663 Self::Any(MatchAny {
2664 any: AnyVariants::Strings(keywords),
2665 })
2666 }
2667}
2668
2669impl From<ValueVariants> for Match {
2670 fn from(value: ValueVariants) -> Self {
2671 Self::Value(MatchValue { value })
2672 }
2673}
2674
2675impl From<Vec<String>> for MatchExcept {
2676 fn from(keywords: Vec<String>) -> Self {
2677 let keywords: IndexSet<String, FnvBuildHasher> = keywords.into_iter().collect();
2678 MatchExcept {
2679 except: AnyVariants::Strings(keywords),
2680 }
2681 }
2682}
2683
2684impl From<Vec<IntPayloadType>> for Match {
2685 fn from(integers: Vec<IntPayloadType>) -> Self {
2686 let integers: IndexSet<_, FnvBuildHasher> = integers.into_iter().collect();
2687 Self::Any(MatchAny {
2688 any: AnyVariants::Integers(integers),
2689 })
2690 }
2691}
2692
2693impl From<Vec<IntPayloadType>> for MatchExcept {
2694 fn from(integers: Vec<IntPayloadType>) -> Self {
2695 let integers: IndexSet<_, FnvBuildHasher> = integers.into_iter().collect();
2696 MatchExcept {
2697 except: AnyVariants::Integers(integers),
2698 }
2699 }
2700}
2701
2702#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, JsonSchema)]
2703#[serde(untagged)]
2704pub enum RangeInterface {
2705 Float(Range<OrderedFloat<FloatPayloadType>>),
2706 DateTime(Range<DateTimePayloadType>),
2707}
2708
2709impl Hash for RangeInterface {
2710 fn hash<H: hash::Hasher>(&self, state: &mut H) {
2711 match self {
2712 RangeInterface::Float(range) => {
2713 let Range { lt, gt, gte, lte } = range;
2714 lt.hash(state);
2715 gt.hash(state);
2716 gte.hash(state);
2717 lte.hash(state);
2718 }
2719 RangeInterface::DateTime(range) => {
2720 let Range { lt, gt, gte, lte } = range;
2721 lt.hash(state);
2722 gt.hash(state);
2723 gte.hash(state);
2724 lte.hash(state);
2725 }
2726 }
2727 }
2728}
2729
2730#[derive(serde::Deserialize)]
2731#[serde(untagged)]
2732enum RangeInterfaceUntagged {
2733 Float(Range<OrderedFloatPayloadType>),
2734 DateTime(Range<DateTimePayloadType>),
2735}
2736
2737impl<'de> serde::Deserialize<'de> for RangeInterface {
2738 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2742 where
2743 D: serde::Deserializer<'de>,
2744 {
2745 if !deserializer.is_human_readable() {
2746 return RangeInterfaceUntagged::deserialize(deserializer).map(|parsed| match parsed {
2747 RangeInterfaceUntagged::Float(r) => RangeInterface::Float(r),
2748 RangeInterfaceUntagged::DateTime(r) => RangeInterface::DateTime(r),
2749 });
2750 }
2751
2752 let value = serde_json::Value::deserialize(deserializer)?;
2753
2754 if let Some(obj) = value.as_object() {
2756 let keys = ["lt", "gt", "lte", "gte"];
2757 let has_string_bound = keys
2758 .iter()
2759 .any(|k| obj.get(*k).is_some_and(|v| v.is_string()));
2760
2761 if has_string_bound {
2762 return serde_json::from_value::<Range<DateTimePayloadType>>(value)
2763 .map(RangeInterface::DateTime)
2764 .map_err(serde::de::Error::custom);
2765 }
2766 }
2767
2768 let parsed = serde_json::from_value::<RangeInterfaceUntagged>(value)
2770 .map_err(serde::de::Error::custom)?;
2771
2772 Ok(match parsed {
2773 RangeInterfaceUntagged::Float(r) => RangeInterface::Float(r),
2774 RangeInterfaceUntagged::DateTime(r) => RangeInterface::DateTime(r),
2775 })
2776 }
2777}
2778
2779type OrderedFloatPayloadType = OrderedFloat<FloatPayloadType>;
2780
2781#[macro_rules_attribute::macro_rules_derive(crate::segment::common::macros::schemars_rename_generics)]
2783#[derive_args(< OrderedFloatPayloadType > => "Range", < DateTimePayloadType > => "DatetimeRange")]
2784#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
2785#[serde(rename_all = "snake_case")]
2786pub struct Range<T> {
2787 pub lt: Option<T>,
2789 pub gt: Option<T>,
2791 pub gte: Option<T>,
2793 pub lte: Option<T>,
2795}
2796
2797impl<T: Copy> Range<T> {
2798 pub fn map<U, F: Fn(T) -> U>(&self, f: F) -> Range<U> {
2800 let Self { lt, gt, gte, lte } = self;
2801 Range {
2802 lt: lt.map(&f),
2803 gt: gt.map(&f),
2804 gte: gte.map(&f),
2805 lte: lte.map(&f),
2806 }
2807 }
2808}
2809
2810impl<T: Copy + PartialOrd> Range<T> {
2811 pub fn check_range(&self, number: T) -> bool {
2812 let Self { lt, gt, gte, lte } = self;
2813 lt.is_none_or(|x| number < x)
2814 && gt.is_none_or(|x| number > x)
2815 && lte.is_none_or(|x| number <= x)
2816 && gte.is_none_or(|x| number >= x)
2817 }
2818}
2819
2820#[derive(Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq, Hash)]
2822#[serde(rename_all = "snake_case")]
2823pub struct ValuesCount {
2824 pub lt: Option<usize>,
2826 pub gt: Option<usize>,
2828 pub gte: Option<usize>,
2830 pub lte: Option<usize>,
2832}
2833
2834impl ValuesCount {
2835 pub fn check_count(&self, count: usize) -> bool {
2836 let Self { lt, gt, gte, lte } = self;
2837 lt.is_none_or(|x| count < x)
2838 && gt.is_none_or(|x| count > x)
2839 && lte.is_none_or(|x| count <= x)
2840 && gte.is_none_or(|x| count >= x)
2841 }
2842
2843 pub fn check_count_from(&self, value: &Value) -> bool {
2844 let count = match value {
2845 Value::Null => 0,
2846 Value::Array(array) => array.len(),
2847 Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Object(_) => 1,
2848 };
2849
2850 self.check_count(count)
2851 }
2852}
2853
2854#[cfg(test)]
2855impl From<std::ops::Range<usize>> for ValuesCount {
2856 fn from(range: std::ops::Range<usize>) -> Self {
2857 Self {
2858 gte: Some(range.start),
2859 lt: Some(range.end),
2860 gt: None,
2861 lte: None,
2862 }
2863 }
2864}
2865
2866#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, JsonSchema)]
2870#[serde(rename_all = "snake_case")]
2871pub struct GeoBoundingBox {
2872 pub top_left: GeoPoint,
2874 pub bottom_right: GeoPoint,
2876}
2877
2878impl GeoBoundingBox {
2879 pub fn check_point(&self, point: &GeoPoint) -> bool {
2880 let longitude_check = if self.top_left.lon > self.bottom_right.lon {
2881 point.lon > self.top_left.lon || point.lon < self.bottom_right.lon
2883 } else {
2884 self.top_left.lon < point.lon && point.lon < self.bottom_right.lon
2885 };
2886
2887 let latitude_check = self.bottom_right.lat < point.lat && point.lat < self.top_left.lat;
2888
2889 longitude_check && latitude_check
2890 }
2891}
2892
2893#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize, JsonSchema)]
2897#[serde(rename_all = "snake_case")]
2898pub struct GeoRadius {
2899 pub center: GeoPoint,
2901 pub radius: OrderedFloat<f64>,
2903}
2904
2905impl Hash for GeoRadius {
2906 fn hash<H: hash::Hasher>(&self, state: &mut H) {
2907 let GeoRadius { center, radius } = self;
2908 center.hash(state);
2909 OrderedFloat(*radius).hash(state);
2911 }
2912}
2913
2914impl GeoRadius {
2915 pub fn check_point(&self, point: &GeoPoint) -> bool {
2916 let query_center = Point::from(self.center);
2917 Haversine.distance(query_center, Point::from(*point)) < self.radius.0
2918 }
2919}
2920
2921#[derive(Deserialize)]
2922pub struct GeoPolygonShadow {
2923 pub exterior: GeoLineString,
2924 pub interiors: Option<Vec<GeoLineString>>,
2925}
2926
2927pub struct PolygonWrapper {
2928 pub polygon: Polygon,
2929}
2930
2931impl PolygonWrapper {
2932 pub fn check_point(&self, point: &GeoPoint) -> bool {
2933 let point_new = Point::new(point.lon.0, point.lat.0);
2934 self.polygon.contains(&point_new)
2935 }
2936}
2937
2938#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
2942#[serde(try_from = "GeoPolygonShadow", rename_all = "snake_case")]
2943pub struct GeoPolygon {
2944 pub exterior: GeoLineString,
2948 pub interiors: Option<Vec<GeoLineString>>,
2952}
2953
2954impl GeoPolygon {
2955 pub fn validate_line_string(line: &GeoLineString) -> OperationResult<()> {
2956 if line.points.len() <= 3 {
2957 return Err(OperationError::validation_error(format!(
2958 "polygon invalid, the size must be at least 4, got {}",
2959 line.points.len()
2960 )));
2961 }
2962
2963 if let (Some(first), Some(last)) = (line.points.first(), line.points.last())
2964 && ((first.lat - last.lat).abs() > f64::EPSILON
2965 || (first.lon - last.lon).abs() > f64::EPSILON)
2966 {
2967 return Err(OperationError::validation_error(
2968 "polygon invalid, the first and the last points should be the same to form a closed line",
2969 ));
2970 }
2971
2972 Ok(())
2973 }
2974
2975 pub fn convert(&self) -> PolygonWrapper {
2977 let exterior_line: LineString = LineString(
2978 self.exterior
2979 .points
2980 .iter()
2981 .map(|p| Coord {
2982 x: p.lon.0,
2983 y: p.lat.0,
2984 })
2985 .collect(),
2986 );
2987
2988 let interior_lines: Vec<LineString> = match &self.interiors {
2990 None => vec![],
2991 Some(interiors) => interiors
2992 .iter()
2993 .map(|interior_points| {
2994 interior_points
2995 .points
2996 .iter()
2997 .map(|p| Coord {
2998 x: p.lon.0,
2999 y: p.lat.0,
3000 })
3001 .collect()
3002 })
3003 .map(LineString)
3004 .collect(),
3005 };
3006 PolygonWrapper {
3007 polygon: Polygon::new(exterior_line, interior_lines),
3008 }
3009 }
3010}
3011
3012impl TryFrom<GeoPolygonShadow> for GeoPolygon {
3013 type Error = OperationError;
3014
3015 fn try_from(value: GeoPolygonShadow) -> OperationResult<Self> {
3016 let GeoPolygonShadow {
3017 exterior,
3018 interiors,
3019 } = value;
3020 Self::validate_line_string(&exterior)?;
3021
3022 if let Some(interiors) = &interiors {
3023 for interior in interiors {
3024 Self::validate_line_string(interior)?;
3025 }
3026 }
3027
3028 Ok(GeoPolygon {
3029 exterior,
3030 interiors,
3031 })
3032 }
3033}
3034
3035#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Hash)]
3037#[validate(schema(function = "validate_field_condition"))]
3038#[serde(rename_all = "snake_case")]
3039pub struct FieldCondition {
3040 pub key: PayloadKeyType,
3042 #[serde(skip_serializing_if = "Option::is_none")]
3044 pub r#match: Option<Match>,
3045 #[serde(skip_serializing_if = "Option::is_none")]
3047 pub range: Option<RangeInterface>,
3048 #[serde(skip_serializing_if = "Option::is_none")]
3050 pub geo_bounding_box: Option<GeoBoundingBox>,
3051 #[serde(skip_serializing_if = "Option::is_none")]
3053 pub geo_radius: Option<GeoRadius>,
3054 #[serde(skip_serializing_if = "Option::is_none")]
3056 pub geo_polygon: Option<GeoPolygon>,
3057 #[serde(skip_serializing_if = "Option::is_none")]
3059 pub values_count: Option<ValuesCount>,
3060 #[serde(skip_serializing_if = "Option::is_none")]
3062 pub is_empty: Option<bool>,
3063 #[serde(skip_serializing_if = "Option::is_none")]
3065 pub is_null: Option<bool>,
3066}
3067
3068impl FieldCondition {
3069 pub fn new_match(key: PayloadKeyType, r#match: Match) -> Self {
3070 Self {
3071 key,
3072 r#match: Some(r#match),
3073 range: None,
3074 geo_bounding_box: None,
3075 geo_radius: None,
3076 geo_polygon: None,
3077 values_count: None,
3078 is_empty: None,
3079 is_null: None,
3080 }
3081 }
3082
3083 pub fn new_range(key: PayloadKeyType, range: Range<OrderedFloat<FloatPayloadType>>) -> Self {
3084 Self {
3085 key,
3086 r#match: None,
3087 range: Some(RangeInterface::Float(range)),
3088 geo_bounding_box: None,
3089 geo_radius: None,
3090 geo_polygon: None,
3091 values_count: None,
3092 is_empty: None,
3093 is_null: None,
3094 }
3095 }
3096
3097 pub fn new_datetime_range(
3098 key: PayloadKeyType,
3099 datetime_range: Range<DateTimePayloadType>,
3100 ) -> Self {
3101 Self {
3102 key,
3103 r#match: None,
3104 range: Some(RangeInterface::DateTime(datetime_range)),
3105 geo_bounding_box: None,
3106 geo_radius: None,
3107 geo_polygon: None,
3108 values_count: None,
3109 is_empty: None,
3110 is_null: None,
3111 }
3112 }
3113
3114 pub fn new_geo_bounding_box(key: PayloadKeyType, geo_bounding_box: GeoBoundingBox) -> Self {
3115 Self {
3116 key,
3117 r#match: None,
3118 range: None,
3119 geo_bounding_box: Some(geo_bounding_box),
3120 geo_radius: None,
3121 geo_polygon: None,
3122 values_count: None,
3123 is_empty: None,
3124 is_null: None,
3125 }
3126 }
3127
3128 pub fn new_geo_radius(key: PayloadKeyType, geo_radius: GeoRadius) -> Self {
3129 Self {
3130 key,
3131 r#match: None,
3132 range: None,
3133 geo_bounding_box: None,
3134 geo_radius: Some(geo_radius),
3135 geo_polygon: None,
3136 values_count: None,
3137 is_empty: None,
3138 is_null: None,
3139 }
3140 }
3141
3142 pub fn new_geo_polygon(key: PayloadKeyType, geo_polygon: GeoPolygon) -> Self {
3143 Self {
3144 key,
3145 r#match: None,
3146 range: None,
3147 geo_bounding_box: None,
3148 geo_radius: None,
3149 geo_polygon: Some(geo_polygon),
3150 values_count: None,
3151 is_empty: None,
3152 is_null: None,
3153 }
3154 }
3155
3156 pub fn new_values_count(key: PayloadKeyType, values_count: ValuesCount) -> Self {
3157 Self {
3158 key,
3159 r#match: None,
3160 range: None,
3161 geo_bounding_box: None,
3162 geo_radius: None,
3163 geo_polygon: None,
3164 values_count: Some(values_count),
3165 is_empty: None,
3166 is_null: None,
3167 }
3168 }
3169
3170 pub fn new_is_empty(key: PayloadKeyType, is_empty: bool) -> Self {
3171 Self {
3172 key,
3173 r#match: None,
3174 range: None,
3175 geo_bounding_box: None,
3176 geo_radius: None,
3177 geo_polygon: None,
3178 values_count: None,
3179 is_empty: Some(is_empty),
3180 is_null: None,
3181 }
3182 }
3183
3184 pub fn new_is_null(key: PayloadKeyType, is_null: bool) -> Self {
3185 Self {
3186 key,
3187 r#match: None,
3188 range: None,
3189 geo_bounding_box: None,
3190 geo_radius: None,
3191 geo_polygon: None,
3192 values_count: None,
3193 is_empty: None,
3194 is_null: Some(is_null),
3195 }
3196 }
3197
3198 pub fn all_fields_none(&self) -> bool {
3199 matches!(
3200 self,
3201 FieldCondition {
3202 r#match: None,
3203 range: None,
3204 geo_bounding_box: None,
3205 geo_radius: None,
3206 geo_polygon: None,
3207 values_count: None,
3208 key: _,
3209 is_empty: None,
3210 is_null: None,
3211 }
3212 )
3213 }
3214
3215 fn input_size(&self) -> usize {
3216 if self.r#match.is_none() {
3217 return 0;
3218 }
3219
3220 match self.r#match.as_ref().unwrap() {
3221 Match::Any(match_any) => match_any.any.len(),
3222 Match::Except(match_except) => match_except.except.len(),
3223 Match::Value(_) => 0,
3224 Match::Text(_) => 0,
3225 Match::Phrase(_) => 0,
3226 Match::TextAny(_) => 0,
3227 }
3228 }
3229}
3230
3231pub fn validate_field_condition(field_condition: &FieldCondition) -> Result<(), ValidationError> {
3232 if field_condition.all_fields_none() {
3233 Err(ValidationError::new(
3234 "At least one field condition must be specified",
3235 ))
3236 } else {
3237 Ok(())
3238 }
3239}
3240
3241#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3243pub struct PayloadField {
3244 pub key: PayloadKeyType,
3246}
3247
3248#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3250pub struct IsEmptyCondition {
3251 pub is_empty: PayloadField,
3252}
3253
3254#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3256pub struct IsNullCondition {
3257 pub is_null: PayloadField,
3258}
3259
3260impl From<JsonPath> for IsNullCondition {
3261 fn from(key: PayloadKeyType) -> Self {
3262 IsNullCondition {
3263 is_null: PayloadField { key },
3264 }
3265 }
3266}
3267
3268impl From<JsonPath> for IsEmptyCondition {
3269 fn from(key: PayloadKeyType) -> Self {
3270 IsEmptyCondition {
3271 is_empty: PayloadField { key },
3272 }
3273 }
3274}
3275
3276#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
3278pub struct HasIdCondition {
3279 #[schemars(schema_with = "HashSet::<PointIdType>::json_schema")]
3280 pub has_id: MaybeArc<AHashSet<PointIdType>>,
3281}
3282
3283impl Hash for HasIdCondition {
3284 fn hash<H: hash::Hasher>(&self, state: &mut H) {
3285 unordered_hash_unique(state, self.has_id.iter());
3286 }
3287}
3288
3289#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3291pub struct HasVectorCondition {
3292 pub has_vector: VectorNameBuf,
3293}
3294
3295impl From<VectorNameBuf> for HasVectorCondition {
3296 fn from(vector: VectorNameBuf) -> Self {
3297 HasVectorCondition { has_vector: vector }
3298 }
3299}
3300
3301const HAS_ID_CONDITION_ARC_THRESHOLD: usize = 1_000;
3305
3306impl From<AHashSet<PointIdType>> for HasIdCondition {
3307 fn from(has_id: AHashSet<PointIdType>) -> Self {
3308 if has_id.len() > HAS_ID_CONDITION_ARC_THRESHOLD {
3309 HasIdCondition {
3310 has_id: MaybeArc::arc(has_id),
3311 }
3312 } else {
3313 HasIdCondition {
3314 has_id: MaybeArc::no_arc(has_id),
3315 }
3316 }
3317 }
3318}
3319
3320impl FromIterator<PointIdType> for HasIdCondition {
3321 fn from_iter<T: IntoIterator<Item = PointIdType>>(iter: T) -> Self {
3322 let items: AHashSet<_> = iter.into_iter().collect();
3323 Self::from(items)
3325 }
3326}
3327
3328#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Validate, Hash)]
3330pub struct Nested {
3331 pub key: PayloadKeyType,
3332 #[validate(nested)]
3333 pub filter: Filter,
3334}
3335
3336#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Validate, Hash)]
3337pub struct NestedCondition {
3338 #[validate(nested)]
3339 pub nested: Nested,
3340}
3341
3342impl NestedCondition {
3344 pub fn new(nested: Nested) -> Self {
3345 Self { nested }
3346 }
3347
3348 pub fn raw_key(&self) -> &PayloadKeyType {
3350 &self.nested.key
3351 }
3352
3353 pub fn array_key(&self) -> PayloadKeyType {
3355 self.raw_key().array_key()
3356 }
3357
3358 pub fn filter(&self) -> &Filter {
3359 &self.nested.filter
3360 }
3361}
3362
3363#[derive(Clone, Debug, Serialize, JsonSchema, PartialEq, Eq, Hash)]
3364#[serde(untagged)]
3365#[serde(
3366 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"
3367)]
3368#[allow(clippy::large_enum_variant)]
3369pub enum Condition {
3370 Field(FieldCondition),
3372 IsEmpty(IsEmptyCondition),
3374 IsNull(IsNullCondition),
3376 HasId(HasIdCondition),
3378 HasVector(HasVectorCondition),
3380 Nested(NestedCondition),
3382 Filter(Filter),
3384
3385 #[serde(skip)]
3386 CustomIdChecker(CustomIdChecker),
3387}
3388
3389#[derive(Deserialize)]
3390#[serde(untagged)]
3391#[serde(
3392 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"
3393)]
3394#[allow(clippy::large_enum_variant, dead_code)]
3395enum ConditionUntagged {
3396 Field(FieldCondition),
3397 IsEmpty(IsEmptyCondition),
3398 IsNull(IsNullCondition),
3399 HasId(HasIdCondition),
3400 HasVector(HasVectorCondition),
3401 Nested(NestedCondition),
3402 Filter(Filter),
3403
3404 #[serde(skip)]
3405 CustomIdChecker(CustomIdChecker),
3406}
3407
3408impl From<ConditionUntagged> for Condition {
3409 fn from(condition: ConditionUntagged) -> Self {
3410 match condition {
3411 ConditionUntagged::Field(condition) => Condition::Field(condition),
3412 ConditionUntagged::IsEmpty(condition) => Condition::IsEmpty(condition),
3413 ConditionUntagged::IsNull(condition) => Condition::IsNull(condition),
3414 ConditionUntagged::HasId(condition) => Condition::HasId(condition),
3415 ConditionUntagged::HasVector(condition) => Condition::HasVector(condition),
3416 ConditionUntagged::Nested(condition) => Condition::Nested(condition),
3417 ConditionUntagged::Filter(condition) => Condition::Filter(condition),
3418 ConditionUntagged::CustomIdChecker(condition) => Condition::CustomIdChecker(condition),
3419 }
3420 }
3421}
3422
3423impl<'de> serde::Deserialize<'de> for Condition {
3424 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3429 where
3430 D: serde::Deserializer<'de>,
3431 {
3432 let value = serde_value::Value::deserialize(deserializer)?;
3438
3439 if let serde_value::Value::Map(obj) = &value
3442 && obj.contains_key(&serde_value::Value::String("key".into()))
3443 {
3444 return value
3445 .deserialize_into()
3446 .map(Condition::Field)
3447 .map_err(serde::de::Error::custom);
3448 }
3449
3450 value
3452 .deserialize_into::<ConditionUntagged>()
3453 .map(Condition::from)
3454 .map_err(serde::de::Error::custom)
3455 }
3456}
3457
3458impl Condition {
3459 pub fn new_custom(checker: Arc<dyn CustomIdCheckerCondition + Send + Sync + 'static>) -> Self {
3460 Condition::CustomIdChecker(CustomIdChecker(checker))
3461 }
3462}
3463
3464#[derive(Debug, Clone)]
3465pub struct CustomIdChecker(pub Arc<dyn CustomIdCheckerCondition + Send + Sync + 'static>);
3466
3467impl Hash for CustomIdChecker {
3468 fn hash<H: hash::Hasher>(&self, state: &mut H) {
3469 std::ptr::hash(Arc::as_ptr(&self.0), state);
3474 }
3475}
3476
3477impl PartialEq for CustomIdChecker {
3478 fn eq(&self, other: &Self) -> bool {
3479 Arc::ptr_eq(&self.0, &other.0)
3484 }
3485}
3486
3487impl Eq for CustomIdChecker {}
3488
3489impl Condition {
3490 pub fn new_nested(key: JsonPath, filter: Filter) -> Self {
3491 Self::Nested(NestedCondition {
3492 nested: Nested { key, filter },
3493 })
3494 }
3495
3496 pub fn size_estimation(&self) -> usize {
3497 match self {
3498 Condition::Field(field_condition) => field_condition.input_size(),
3499 Condition::HasId(has_id_condition) => has_id_condition.has_id.len(),
3500 Condition::Filter(filter) => filter.max_condition_input_size(),
3501 Condition::Nested(nested) => nested.filter().max_condition_input_size(),
3502 Condition::IsEmpty(_)
3503 | Condition::IsNull(_)
3504 | Condition::HasVector(_)
3505 | Condition::CustomIdChecker(_) => 0,
3506 }
3507 }
3508
3509 pub fn sub_conditions_count(&self) -> usize {
3510 match self {
3511 Condition::Nested(nested_condition) => {
3512 nested_condition.filter().total_conditions_count()
3513 }
3514 Condition::Filter(filter) => filter.total_conditions_count(),
3515 Condition::Field(_)
3516 | Condition::IsEmpty(_)
3517 | Condition::IsNull(_)
3518 | Condition::CustomIdChecker(_)
3519 | Condition::HasId(_)
3520 | Condition::HasVector(_) => 1,
3521 }
3522 }
3523
3524 pub fn targeted_key(&self) -> Option<PayloadKeyType> {
3525 match self {
3526 Condition::Field(field_condition) => Some(field_condition.key.clone()),
3527 Condition::IsEmpty(is_empty_condition) => Some(is_empty_condition.is_empty.key.clone()),
3528 Condition::IsNull(is_null_condition) => Some(is_null_condition.is_null.key.clone()),
3529 Condition::Nested(nested_condition) => Some(nested_condition.array_key()),
3530 Condition::Filter(filter) => filter.iter_conditions().find_map(|c| c.targeted_key()),
3531 Condition::HasId(_) | Condition::HasVector(_) | Condition::CustomIdChecker(_) => None,
3532 }
3533 }
3534}
3535
3536impl Validate for Condition {
3538 fn validate(&self) -> Result<(), ValidationErrors> {
3539 match self {
3540 Condition::HasId(_)
3541 | Condition::IsEmpty(_)
3542 | Condition::IsNull(_)
3543 | Condition::HasVector(_) => Ok(()),
3544 Condition::Field(field_condition) => field_condition.validate(),
3545 Condition::Nested(nested_condition) => nested_condition.validate(),
3546 Condition::Filter(filter) => filter.validate(),
3547 Condition::CustomIdChecker(_) => Ok(()),
3548 }
3549 }
3550}
3551
3552pub trait CustomIdCheckerCondition: fmt::Debug {
3553 fn estimate_cardinality(&self, points: usize) -> CardinalityEstimation;
3554 fn check(&self, point_id: ExtendedPointId) -> bool;
3555}
3556
3557#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Hash)]
3559#[serde(untagged, rename_all = "snake_case")]
3560#[serde(
3561 expecting = "Expected a boolean, an array of strings, or an object with an include/exclude field"
3562)]
3563pub enum WithPayloadInterface {
3564 Bool(bool),
3567 Fields(Vec<JsonPath>),
3569 Selector(PayloadSelector),
3571}
3572
3573impl From<bool> for WithPayloadInterface {
3574 fn from(b: bool) -> Self {
3575 WithPayloadInterface::Bool(b)
3576 }
3577}
3578
3579impl Default for WithPayloadInterface {
3580 fn default() -> Self {
3581 WithPayloadInterface::Bool(false)
3582 }
3583}
3584
3585#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3587#[serde(untagged, rename_all = "snake_case")]
3588#[serde(expecting = "Expected a boolean, or an array of strings")]
3589pub enum WithVector {
3590 Bool(bool),
3593 Selector(Vec<VectorNameBuf>),
3595}
3596
3597impl WithVector {
3598 pub fn is_enabled(&self) -> bool {
3599 match self {
3600 WithVector::Bool(b) => *b,
3601 WithVector::Selector(_) => true,
3602 }
3603 }
3604
3605 pub fn merge(&self, other: &WithVector) -> WithVector {
3607 match (self, other) {
3608 (WithVector::Bool(true), _) => WithVector::Bool(true),
3610 (_, WithVector::Bool(true)) => WithVector::Bool(true),
3611
3612 (WithVector::Bool(false), WithVector::Bool(false)) => WithVector::Bool(false),
3614
3615 (WithVector::Selector(s1), WithVector::Selector(s2)) => {
3617 WithVector::Selector(s1.iter().chain(s2).unique().cloned().collect())
3618 }
3619
3620 (WithVector::Bool(false), WithVector::Selector(s)) => WithVector::Selector(s.clone()),
3622 (WithVector::Selector(s), WithVector::Bool(false)) => WithVector::Selector(s.clone()),
3623 }
3624 }
3625}
3626
3627impl From<bool> for WithVector {
3628 fn from(b: bool) -> Self {
3629 WithVector::Bool(b)
3630 }
3631}
3632
3633impl From<VectorNameBuf> for WithVector {
3634 fn from(name: VectorNameBuf) -> Self {
3635 WithVector::Selector(vec![name])
3636 }
3637}
3638
3639impl Default for WithVector {
3640 fn default() -> Self {
3641 WithVector::Bool(false)
3642 }
3643}
3644
3645impl WithPayloadInterface {
3646 pub fn is_required(&self) -> bool {
3647 match self {
3648 WithPayloadInterface::Bool(b) => *b,
3649 WithPayloadInterface::Fields(_) | WithPayloadInterface::Selector(_) => true,
3650 }
3651 }
3652}
3653
3654impl From<bool> for WithPayload {
3655 fn from(x: bool) -> Self {
3656 WithPayload {
3657 enable: x,
3658 payload_selector: None,
3659 }
3660 }
3661}
3662
3663impl From<WithPayloadInterface> for WithPayload {
3664 fn from(interface: WithPayloadInterface) -> Self {
3665 match interface {
3666 WithPayloadInterface::Bool(enable) => WithPayload {
3667 enable,
3668 payload_selector: None,
3669 },
3670 WithPayloadInterface::Fields(fields) => WithPayload {
3671 enable: true,
3672 payload_selector: Some(PayloadSelector::new_include(fields)),
3673 },
3674 WithPayloadInterface::Selector(selector) => WithPayload {
3675 enable: true,
3676 payload_selector: Some(selector),
3677 },
3678 }
3679 }
3680}
3681
3682impl From<&WithPayloadInterface> for WithPayload {
3683 fn from(interface: &WithPayloadInterface) -> Self {
3684 WithPayload::from(interface.clone())
3685 }
3686}
3687
3688#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3689#[serde(deny_unknown_fields, rename_all = "snake_case")]
3690pub struct PayloadSelectorInclude {
3691 pub include: Vec<PayloadKeyType>,
3693}
3694
3695impl PayloadSelectorInclude {
3696 pub fn new(include: Vec<PayloadKeyType>) -> Self {
3697 Self { include }
3698 }
3699}
3700
3701#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3702#[serde(deny_unknown_fields, rename_all = "snake_case")]
3703pub struct PayloadSelectorExclude {
3704 pub exclude: Vec<PayloadKeyType>,
3706}
3707
3708impl PayloadSelectorExclude {
3709 pub fn new(exclude: Vec<PayloadKeyType>) -> Self {
3710 Self { exclude }
3711 }
3712}
3713
3714#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq, Hash)]
3716#[serde(untagged, rename_all = "snake_case")]
3717pub enum PayloadSelector {
3718 Include(PayloadSelectorInclude),
3720 Exclude(PayloadSelectorExclude),
3722}
3723
3724impl From<PayloadSelectorExclude> for WithPayloadInterface {
3725 fn from(selector: PayloadSelectorExclude) -> Self {
3726 WithPayloadInterface::Selector(PayloadSelector::Exclude(selector))
3727 }
3728}
3729
3730impl From<PayloadSelectorInclude> for WithPayloadInterface {
3731 fn from(selector: PayloadSelectorInclude) -> Self {
3732 WithPayloadInterface::Selector(PayloadSelector::Include(selector))
3733 }
3734}
3735
3736impl PayloadSelector {
3737 pub fn new_include(vecs_payload_key_type: Vec<PayloadKeyType>) -> Self {
3738 PayloadSelector::Include(PayloadSelectorInclude {
3739 include: vecs_payload_key_type,
3740 })
3741 }
3742
3743 pub fn new_exclude(vecs_payload_key_type: Vec<PayloadKeyType>) -> Self {
3744 PayloadSelector::Exclude(PayloadSelectorExclude {
3745 exclude: vecs_payload_key_type,
3746 })
3747 }
3748
3749 pub fn process(&self, x: Payload) -> Payload {
3751 match self {
3752 PayloadSelector::Include(selector) => JsonPath::value_filter(&x.0, |key, _| {
3753 selector
3754 .include
3755 .iter()
3756 .any(|pattern| pattern.check_include_pattern(key))
3757 })
3758 .into(),
3759 PayloadSelector::Exclude(selector) => JsonPath::value_filter(&x.0, |key, _| {
3760 selector
3761 .exclude
3762 .iter()
3763 .all(|pattern| !pattern.check_exclude_pattern(key))
3764 })
3765 .into(),
3766 }
3767 }
3768}
3769
3770#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Default, PartialEq, Eq)]
3771#[serde(deny_unknown_fields, rename_all = "snake_case")]
3772pub struct WithPayload {
3773 pub enable: bool,
3775 pub payload_selector: Option<PayloadSelector>,
3777}
3778
3779#[derive(
3780 Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Default, Hash,
3781)]
3782#[serde(rename_all = "snake_case")]
3783pub struct MinShould {
3784 #[validate(nested)]
3785 pub conditions: Vec<Condition>,
3786 pub min_count: usize,
3787}
3788
3789#[derive(
3790 Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq, Eq, Default, Hash,
3791)]
3792#[serde(deny_unknown_fields, rename_all = "snake_case")]
3793pub struct Filter {
3794 #[validate(nested)]
3796 #[serde(
3797 default,
3798 with = "MaybeOneOrMany",
3799 skip_serializing_if = "Option::is_none"
3800 )]
3801 #[schemars(with = "MaybeOneOrMany<Condition>")]
3802 pub should: Option<Vec<Condition>>,
3803 #[validate(nested)]
3805 #[serde(skip_serializing_if = "Option::is_none")]
3806 pub min_should: Option<MinShould>,
3807 #[validate(nested)]
3809 #[serde(
3810 default,
3811 with = "MaybeOneOrMany",
3812 skip_serializing_if = "Option::is_none"
3813 )]
3814 #[schemars(with = "MaybeOneOrMany<Condition>")]
3815 pub must: Option<Vec<Condition>>,
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 must_not: Option<Vec<Condition>>,
3825}
3826
3827impl Filter {
3828 pub fn new() -> Self {
3829 Filter {
3830 should: None,
3831 min_should: None,
3832 must: None,
3833 must_not: None,
3834 }
3835 }
3836
3837 pub fn new_should(condition: Condition) -> Self {
3838 Filter {
3839 should: Some(vec![condition]),
3840 min_should: None,
3841 must: None,
3842 must_not: None,
3843 }
3844 }
3845
3846 pub fn new_min_should(min_should: MinShould) -> Self {
3847 Filter {
3848 should: None,
3849 min_should: Some(min_should),
3850 must: None,
3851 must_not: None,
3852 }
3853 }
3854
3855 pub fn new_must(condition: Condition) -> Self {
3856 Filter {
3857 should: None,
3858 min_should: None,
3859 must: Some(vec![condition]),
3860 must_not: None,
3861 }
3862 }
3863
3864 pub fn new_must_not(condition: Condition) -> Self {
3865 Filter {
3866 should: None,
3867 min_should: None,
3868 must: None,
3869 must_not: Some(vec![condition]),
3870 }
3871 }
3872
3873 pub fn with_point_ids(self, ids: impl IntoIterator<Item = PointIdType>) -> Filter {
3875 let has_id_condition: HasIdCondition = ids.into_iter().collect();
3876
3877 let Filter {
3878 should,
3879 min_should,
3880 must,
3881 must_not,
3882 } = self;
3883
3884 let new_must = match must {
3885 Some(mut must) => {
3886 must.push(Condition::HasId(has_id_condition));
3887 Some(must)
3888 }
3889 None => Some(vec![Condition::HasId(has_id_condition)]),
3890 };
3891
3892 Filter {
3893 should,
3894 min_should,
3895 must: new_must,
3896 must_not,
3897 }
3898 }
3899
3900 pub fn merge(&self, other: &Filter) -> Filter {
3901 self.clone().merge_owned(other.clone())
3902 }
3903
3904 pub fn merge_owned(self, other: Filter) -> Filter {
3905 let merge_component = |this, other| -> Option<Vec<Condition>> {
3906 match (this, other) {
3907 (None, None) => None,
3908 (Some(this), None) => Some(this),
3909 (None, Some(other)) => Some(other),
3910 (Some(mut this), Some(mut other)) => {
3911 this.append(&mut other);
3912 Some(this)
3913 }
3914 }
3915 };
3916 Filter {
3917 should: merge_component(self.should, other.should),
3918 min_should: {
3919 match (self.min_should, other.min_should) {
3920 (None, None) => None,
3921 (Some(this), None) => Some(this),
3922 (None, Some(other)) => Some(other),
3923 (Some(mut this), Some(mut other)) => {
3924 this.conditions.append(&mut other.conditions);
3925
3926 this.min_count = this.min_count.max(other.min_count);
3928
3929 Some(this)
3930 }
3931 }
3932 },
3933 must: merge_component(self.must, other.must),
3934 must_not: merge_component(self.must_not, other.must_not),
3935 }
3936 }
3937
3938 pub fn merge_opts(this: Option<Self>, other: Option<Self>) -> Option<Self> {
3939 match (this, other) {
3940 (None, None) => None,
3941 (Some(this), None) => Some(this),
3942 (None, Some(other)) => Some(other),
3943 (Some(this), Some(other)) => Some(this.merge_owned(other)),
3944 }
3945 }
3946
3947 pub fn iter_conditions(&self) -> impl Iterator<Item = &Condition> {
3948 self.must
3949 .iter()
3950 .flatten()
3951 .chain(self.must_not.iter().flatten())
3952 .chain(self.should.iter().flatten())
3953 .chain(self.min_should.iter().flat_map(|i| &i.conditions))
3954 }
3955
3956 pub fn total_conditions_count(&self) -> usize {
3958 fn count_all_conditions(field: Option<&Vec<Condition>>) -> usize {
3959 field
3960 .map(|i| i.iter().map(|j| j.sub_conditions_count()).sum::<usize>())
3961 .unwrap_or(0)
3962 }
3963
3964 count_all_conditions(self.should.as_ref())
3965 + count_all_conditions(self.min_should.as_ref().map(|i| &i.conditions))
3966 + count_all_conditions(self.must.as_ref())
3967 + count_all_conditions(self.must_not.as_ref())
3968 }
3969
3970 pub fn max_condition_input_size(&self) -> usize {
3972 self.iter_conditions()
3973 .map(|i| i.size_estimation())
3974 .max()
3975 .unwrap_or(0)
3976 }
3977}
3978
3979#[derive(Debug, Clone, Copy, Eq, PartialEq)]
3980pub enum SnapshotFormat {
3981 Ancient,
3998 Regular,
4024 Streamable,
4044}
4045
4046#[cfg(test)]
4047pub(crate) mod test_utils {
4048 use super::{GeoLineString, GeoPoint, GeoPolygon};
4049
4050 pub fn build_polygon(exterior_points: Vec<(f64, f64)>) -> GeoPolygon {
4051 let exterior_line = GeoLineString {
4052 points: exterior_points
4053 .into_iter()
4054 .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4055 .collect(),
4056 };
4057
4058 GeoPolygon {
4059 exterior: exterior_line,
4060 interiors: None,
4061 }
4062 }
4063
4064 pub fn build_polygon_with_interiors(
4065 exterior_points: Vec<(f64, f64)>,
4066 interiors_points: Vec<Vec<(f64, f64)>>,
4067 ) -> GeoPolygon {
4068 let exterior_line = GeoLineString {
4069 points: exterior_points
4070 .into_iter()
4071 .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4072 .collect(),
4073 };
4074
4075 let interior_lines = Some(
4076 interiors_points
4077 .into_iter()
4078 .map(|points| GeoLineString {
4079 points: points
4080 .into_iter()
4081 .map(|(lon, lat)| GeoPoint::new_unchecked(lon, lat))
4082 .collect(),
4083 })
4084 .collect(),
4085 );
4086
4087 GeoPolygon {
4088 exterior: exterior_line,
4089 interiors: interior_lines,
4090 }
4091 }
4092}
4093
4094#[cfg(test)]
4095mod tests {
4096 #![expect(clippy::wildcard_enum_match_arm, reason = "test code")]
4097
4098 use itertools::Itertools;
4099 use rstest::rstest;
4100 use serde_json;
4101
4102 use super::test_utils::build_polygon_with_interiors;
4103 use super::*;
4104
4105 #[test]
4106 #[ignore]
4107 fn test_rmp_vs_cbor_deserialize() {
4108 let payload = payload_json! {"payload_key": "payload_value"};
4109 let raw = rmp_serde::to_vec(&payload).unwrap();
4110 let de_record: Payload = serde_cbor::from_slice(&raw).unwrap();
4111 eprintln!("payload = {payload:#?}");
4112 eprintln!("de_record = {de_record:#?}");
4113 }
4114
4115 #[rstest]
4116 #[case::rfc_3339("2020-03-01T00:00:00Z")]
4117 #[case::rfc_3339_custom_tz("2020-03-01T00:00:00-09:00")]
4118 #[case::rfc_3339_custom_tz_no_colon("2020-03-01 00:00:00-0900")]
4119 #[case::rfc_3339_custom_tz_no_colon_and_t("2020-03-01T00:00:00-0900")]
4120 #[case::rfc_3339_custom_tz_no_minutes("2020-03-01 00:00:00-09")]
4121 #[case::rfc_3339_and_decimals("2020-03-01T00:00:00.123456Z")]
4122 #[case::without_z("2020-03-01T00:00:00")]
4123 #[case::without_z_and_decimals("2020-03-01T00:00:00.12")]
4124 #[case::space_sep_without_z("2020-03-01 00:00:00")]
4125 #[case::space_sep_without_z_and_decimals("2020-03-01 00:00:00.123456")]
4126 #[case::t_sep_without_seconds("2020-03-01T00:00")]
4127 #[case::space_sep_without_seconds("2020-03-01 00:00")]
4128 #[case::date_only("2020-03-01")]
4129 fn test_datetime_deserialization(#[case] datetime: &str) {
4130 let datetime = DateTimePayloadType::from_str(datetime).unwrap();
4131 let serialized = serde_json::to_string(&datetime).unwrap();
4132 let deserialized: DateTimePayloadType = serde_json::from_str(&serialized).unwrap();
4133 assert_eq!(datetime, deserialized);
4134 }
4135
4136 #[test]
4137 fn test_datetime_deserialization_equivalency() {
4138 let datetime_str = "2020-03-01T01:02:03.123456Z";
4139 let datetime_str_no_z = "2020-03-01T01:02:03.123456";
4140 let datetime = DateTimePayloadType::from_str(datetime_str).unwrap();
4141 let datetime_no_z = DateTimePayloadType::from_str(datetime_str_no_z).unwrap();
4142
4143 assert_eq!(datetime.timestamp(), datetime_no_z.timestamp());
4145 }
4146
4147 #[test]
4148 fn test_invalid_datetime_range_returns_clear_rfc3339_error() {
4149 let json = r#"{
4150 "key": "created_at",
4151 "range": {
4152 "gte": "2014-01-01T00:00:00BAD"
4153 }
4154 }"#;
4155
4156 let err = serde_json::from_str::<Condition>(json)
4157 .unwrap_err()
4158 .to_string();
4159
4160 assert!(err.contains("RFC3339"), "err was: {err}");
4161 assert!(err.contains("2014-01-01T00:00:00BAD"), "err was: {err}");
4162 assert!(err.contains("Example"), "err was: {err}");
4163 }
4164
4165 #[test]
4168 fn test_datetime_payload_type_binary_roundtrip() {
4169 let original = DateTimePayloadType::from_str("2024-06-15T12:30:45Z").unwrap();
4170
4171 let binary = rmp_serde::to_vec(&original).expect("serialize");
4173 let restored: DateTimePayloadType = rmp_serde::from_slice(&binary).expect("deserialize");
4174
4175 assert_eq!(original, restored);
4176 }
4177
4178 #[test]
4181 fn test_range_interface_datetime_binary_roundtrip() {
4182 let dt_gte = DateTimePayloadType::from_str("2024-01-01T00:00:00Z").unwrap();
4183 let dt_lte = DateTimePayloadType::from_str("2024-12-31T23:59:59Z").unwrap();
4184
4185 let range = RangeInterface::DateTime(Range {
4186 lt: None,
4187 gt: None,
4188 gte: Some(dt_gte),
4189 lte: Some(dt_lte),
4190 });
4191
4192 let binary = rmp_serde::to_vec(&range).expect("serialize");
4194 let restored: RangeInterface = rmp_serde::from_slice(&binary).expect("deserialize");
4195
4196 assert_eq!(range, restored);
4197 }
4198
4199 #[test]
4202 fn test_condition_json_fallback_to_untagged() {
4203 let is_empty_json = r#"{"is_empty": {"key": "optional_field"}}"#;
4205 let condition: Condition = serde_json::from_str(is_empty_json).unwrap();
4206 assert!(matches!(condition, Condition::IsEmpty(_)));
4207
4208 let has_id_json = r#"{"has_id": [1, 2, 3]}"#;
4210 let condition: Condition = serde_json::from_str(has_id_json).unwrap();
4211 assert!(matches!(condition, Condition::HasId(_)));
4212
4213 let nested_json = r#"{"nested": {"key": "items", "filter": {"must": []}}}"#;
4215 let condition: Condition = serde_json::from_str(nested_json).unwrap();
4216 assert!(matches!(condition, Condition::Nested(_)));
4217 }
4218
4219 #[test]
4220 fn test_datetime_wrapper_transcoding() {
4221 let expected = DateTimeWrapper(chrono::Utc::now());
4222 let transcoded = DateTimeWrapper::from_str(&expected.to_string()).unwrap();
4223 assert_eq!(expected, transcoded);
4224 }
4225
4226 #[test]
4227 fn test_timezone_ordering() {
4228 let datetimes = [
4229 "2000-06-08 00:18:53+0900",
4230 "2000-06-07 07:25:34-1100",
4231 "2000-07-10T00:18:53+0100",
4232 "2000-07-11 00:25:34-01:00",
4233 "2000-07-11 00:25:35-01",
4234 ];
4235
4236 let sorted_datetimes: Vec<_> = datetimes
4237 .iter()
4238 .enumerate()
4239 .map(|(i, s)| (i, DateTimePayloadType::from_str(s).unwrap()))
4240 .sorted_by_key(|(_, dt)| dt.timestamp())
4241 .collect();
4242
4243 sorted_datetimes
4244 .array_windows()
4245 .for_each(|[(i1, dt1), (i2, dt2)]| {
4246 assert!(
4247 i1 < i2,
4248 "i1: {}, dt1: {}, ts1: {}\ni2: {}, dt2: {}, ts2: {}",
4249 i1,
4250 dt1.0,
4251 dt1.timestamp(),
4252 i2,
4253 dt2.0,
4254 dt2.timestamp()
4255 );
4256 });
4257 }
4258
4259 #[test]
4260 fn test_geo_radius_check_point() {
4261 let radius = GeoRadius {
4262 center: GeoPoint::new_unchecked(0.0, 0.0),
4263 radius: OrderedFloat(80000.0),
4264 };
4265
4266 let inside_result = radius.check_point(&GeoPoint::new_unchecked(0.5, 0.5));
4267 assert!(inside_result);
4268
4269 let outside_result = radius.check_point(&GeoPoint::new_unchecked(1.5, 1.5));
4270 assert!(!outside_result);
4271 }
4272
4273 #[test]
4274 fn test_geo_boundingbox_check_point() {
4275 let bounding_box = GeoBoundingBox {
4276 top_left: GeoPoint::new_unchecked(-1.0, 1.0),
4277 bottom_right: GeoPoint::new_unchecked(1.0, -1.0),
4278 };
4279
4280 let inside_result = bounding_box.check_point(&GeoPoint::new_unchecked(-0.5, 0.5));
4282 assert!(inside_result);
4283
4284 let outside_result = bounding_box.check_point(&GeoPoint::new_unchecked(1.5, 1.5));
4286 assert!(!outside_result);
4287 }
4288
4289 #[test]
4290 fn test_geo_boundingbox_antimeridian_check_point() {
4291 let bounding_box = GeoBoundingBox {
4293 top_left: GeoPoint::new_unchecked(167.0, 74.071028),
4294 bottom_right: GeoPoint::new_unchecked(-66.885417, 18.7763),
4295 };
4296
4297 let inside_result =
4299 bounding_box.check_point(&GeoPoint::new_unchecked(-73.991516, 40.75798));
4300 assert!(inside_result);
4301
4302 let outside_result = bounding_box.check_point(&GeoPoint::new_unchecked(13.41053, 52.52437));
4304 assert!(!outside_result);
4305 }
4306
4307 #[test]
4308 fn test_geo_polygon_check_point() {
4309 let test_cases = [
4310 (
4312 vec![
4314 (-1.0, -1.0),
4315 (1.0, -1.0),
4316 (1.0, 1.0),
4317 (-1.0, 1.0),
4318 (-1.0, -1.0),
4319 ],
4320 vec![vec![]],
4322 vec![((0.5, 0.5), true), ((1.5, 1.5), false), ((1.0, 0.0), false)],
4324 ),
4325 (
4327 vec![
4329 (-1.0, -1.0),
4330 (1.0, 1.0),
4331 (1.0, -1.0),
4332 (-1.0, 1.0),
4333 (-1.0, -1.0),
4334 ],
4335 vec![vec![]],
4337 vec![((0.5, 0.0), true), ((0.0, 0.5), false), ((0.0, 0.0), false)],
4339 ),
4340 (
4342 vec![
4344 (-1.0, -1.0),
4345 (1.5, -1.0),
4346 (1.5, 1.5),
4347 (-1.0, 1.5),
4348 (-1.0, -1.0),
4349 ],
4350 vec![vec![
4352 (-0.5, -0.5),
4353 (-0.5, 0.5),
4354 (0.5, 0.5),
4355 (0.5, -0.5),
4356 (-0.5, -0.5),
4357 ]],
4358 vec![((0.6, 0.6), true), ((0.0, 0.0), false), ((0.5, 0.5), false)],
4360 ),
4361 ];
4362
4363 for (exterior, interiors, points) in test_cases {
4364 let polygon = build_polygon_with_interiors(exterior, interiors);
4365
4366 for ((lon, lat), expected_result) in points {
4367 let inside_result = polygon
4368 .convert()
4369 .check_point(&GeoPoint::new_unchecked(lon, lat));
4370 assert_eq!(inside_result, expected_result);
4371 }
4372 }
4373 }
4374
4375 #[test]
4376 fn test_serialize_query() {
4377 let filter = Filter {
4378 must: Some(vec![Condition::Field(FieldCondition::new_match(
4379 JsonPath::new("hello"),
4380 "world".to_owned().into(),
4381 ))]),
4382 must_not: None,
4383 should: None,
4384 min_should: None,
4385 };
4386 let json = serde_json::to_string_pretty(&filter).unwrap();
4387 eprintln!("{json}")
4388 }
4389
4390 #[test]
4391 fn test_deny_unknown_fields() {
4392 let query1 = r#"
4393 {
4394 "wrong": "query"
4395 }
4396 "#;
4397 let filter: Result<Filter, _> = serde_json::from_str(query1);
4398
4399 assert!(filter.is_err())
4400 }
4401
4402 #[test]
4403 fn test_parse_match_query() {
4404 let query = r#"
4405 {
4406 "key": "hello",
4407 "match": { "value": 42 }
4408 }
4409 "#;
4410 let condition: FieldCondition = serde_json::from_str(query).unwrap();
4411 assert_eq!(
4412 condition.r#match.unwrap(),
4413 Match::Value(MatchValue {
4414 value: ValueVariants::Integer(42)
4415 })
4416 );
4417
4418 let query = r#"
4419 {
4420 "key": "hello",
4421 "match": { "value": true }
4422 }
4423 "#;
4424 let condition: FieldCondition = serde_json::from_str(query).unwrap();
4425 assert_eq!(
4426 condition.r#match.unwrap(),
4427 Match::Value(MatchValue {
4428 value: ValueVariants::Bool(true)
4429 })
4430 );
4431
4432 let query = r#"
4433 {
4434 "key": "hello",
4435 "match": { "value": "world" }
4436 }
4437 "#;
4438
4439 let condition: FieldCondition = serde_json::from_str(query).unwrap();
4440 assert_eq!(
4441 condition.r#match.unwrap(),
4442 Match::Value(MatchValue {
4443 value: ValueVariants::String("world".to_owned())
4444 })
4445 );
4446 }
4447
4448 #[test]
4449 fn test_parse_match_any() {
4450 let query = r#"
4451 {
4452 "should": [
4453 {
4454 "key": "Jason",
4455 "match": {
4456 "any": [
4457 "Bourne",
4458 "Momoa",
4459 "Statham"
4460 ]
4461 }
4462 }
4463 ]
4464 }
4465 "#;
4466
4467 let filter: Filter = serde_json::from_str(query).unwrap();
4468 let should = filter.should.unwrap();
4469
4470 assert_eq!(should.len(), 1);
4471 let Some(Condition::Field(c)) = should.first() else {
4472 panic!("Condition::Field expected")
4473 };
4474
4475 assert_eq!(c.key.to_string(), "Jason");
4476
4477 let Match::Any(m) = c.r#match.as_ref().unwrap() else {
4478 panic!("Match::Any expected")
4479 };
4480 if let AnyVariants::Strings(kws) = &m.any {
4481 assert_eq!(kws.len(), 3);
4482 let expect: IndexSet<_, FnvBuildHasher> = ["Bourne", "Momoa", "Statham"]
4483 .into_iter()
4484 .map(|i| i.to_string())
4485 .collect();
4486 assert_eq!(kws, &expect);
4487 } else {
4488 panic!("AnyVariants::Keywords expected");
4489 }
4490 }
4491
4492 #[test]
4493 fn test_parse_match_any_mixed_types() {
4494 let query = r#"
4495 {
4496 "should": [
4497 {
4498 "key": "Jason",
4499 "match": {
4500 "any": [
4501 "Bourne",
4502 42
4503 ]
4504 }
4505 }
4506 ]
4507 }
4508 "#;
4509
4510 let result: Result<Filter, _> = serde_json::from_str(query);
4511 assert!(result.is_err());
4512 }
4513
4514 #[test]
4515 fn test_parse_nested_match_query() {
4516 let query = r#"
4517 {
4518 "key": "hello.nested",
4519 "match": { "value": 42 }
4520 }
4521 "#;
4522 let condition: FieldCondition = serde_json::from_str(query).unwrap();
4523 assert_eq!(
4524 condition.r#match.unwrap(),
4525 Match::Value(MatchValue {
4526 value: ValueVariants::Integer(42)
4527 })
4528 );
4529
4530 let query = r#"
4531 {
4532 "key": "hello.nested",
4533 "match": { "value": true }
4534 }
4535 "#;
4536 let condition: FieldCondition = serde_json::from_str(query).unwrap();
4537 assert_eq!(
4538 condition.r#match.unwrap(),
4539 Match::Value(MatchValue {
4540 value: ValueVariants::Bool(true)
4541 })
4542 );
4543
4544 let query = r#"
4545 {
4546 "key": "hello.nested",
4547 "match": { "value": "world" }
4548 }
4549 "#;
4550
4551 let condition: FieldCondition = serde_json::from_str(query).unwrap();
4552 assert_eq!(
4553 condition.r#match.unwrap(),
4554 Match::Value(MatchValue {
4555 value: ValueVariants::String("world".to_owned())
4556 })
4557 );
4558 }
4559
4560 #[test]
4561 fn test_parse_empty_query() {
4562 let query = r#"
4563 {
4564 "should": [
4565 {
4566 "is_empty" : {
4567 "key" : "Jason"
4568 }
4569 }
4570 ]
4571 }
4572 "#;
4573
4574 let filter: Filter = serde_json::from_str(query).unwrap();
4575 let should = filter.should.unwrap();
4576
4577 assert_eq!(should.len(), 1);
4578 let Some(Condition::IsEmpty(c)) = should.first() else {
4579 panic!("Condition::IsEmpty expected")
4580 };
4581
4582 assert_eq!(c.is_empty.key.to_string(), "Jason");
4583 }
4584
4585 #[test]
4586 fn test_parse_null_query() {
4587 let query = r#"
4588 {
4589 "should": [
4590 {
4591 "is_null" : {
4592 "key" : "Jason"
4593 }
4594 }
4595 ]
4596 }
4597 "#;
4598
4599 let filter: Filter = serde_json::from_str(query).unwrap();
4600 let should = filter.should.unwrap();
4601
4602 assert_eq!(should.len(), 1);
4603 let Some(Condition::IsNull(c)) = should.first() else {
4604 panic!("Condition::IsNull expected")
4605 };
4606
4607 assert_eq!(c.is_null.key.to_string(), "Jason");
4608 }
4609
4610 #[test]
4611 fn test_parse_nested_filter_query() {
4612 let query = r#"
4613 {
4614 "must": [
4615 {
4616 "nested": {
4617 "key": "country.cities",
4618 "filter": {
4619 "must": [
4620 {
4621 "key": "population",
4622 "range": {
4623 "gte": 8
4624 }
4625 },
4626 {
4627 "key": "sightseeing",
4628 "values_count": {
4629 "lt": 3
4630 }
4631 }
4632 ]
4633 }
4634 }
4635 }
4636 ]
4637 }
4638 "#;
4639 let filter: Filter = serde_json::from_str(query).unwrap();
4640 let musts = filter.must.unwrap();
4641 assert_eq!(musts.len(), 1);
4642 match musts.first() {
4643 Some(Condition::Nested(nested_condition)) => {
4644 assert_eq!(nested_condition.raw_key().to_string(), "country.cities");
4645 assert_eq!(nested_condition.array_key().to_string(), "country.cities[]");
4646 let nested_musts = nested_condition.filter().must.as_ref().unwrap();
4647 assert_eq!(nested_musts.len(), 2);
4648 let first_must = nested_musts.first().unwrap();
4649 match first_must {
4650 Condition::Field(c) => {
4651 assert_eq!(c.key.to_string(), "population");
4652 assert!(c.range.is_some());
4653 }
4654 _ => panic!("Condition::Field expected"),
4655 }
4656
4657 let second_must = nested_musts.get(1).unwrap();
4658 match second_must {
4659 Condition::Field(c) => {
4660 assert_eq!(c.key.to_string(), "sightseeing");
4661 assert!(c.values_count.is_some());
4662 }
4663 _ => panic!("Condition::Field expected"),
4664 }
4665 }
4666 o => panic!("Condition::Nested expected but got {o:?}"),
4667 };
4668 }
4669
4670 #[test]
4671 fn test_parse_single_nested_filter_query() {
4672 let query = r#"
4673 {
4674 "must": {
4675 "nested": {
4676 "key": "country.cities",
4677 "filter": {
4678 "must": {
4679 "key": "population",
4680 "range": {
4681 "gte": 8
4682 }
4683 }
4684 }
4685 }
4686 }
4687 }
4688 "#;
4689 let filter: Filter = serde_json::from_str(query).unwrap();
4690 let musts = filter.must.unwrap();
4691 assert_eq!(musts.len(), 1);
4692
4693 let first_must = musts.first().unwrap();
4694 let Condition::Nested(nested_condition) = first_must else {
4695 panic!("Condition::Nested expected but got {first_must:?}")
4696 };
4697
4698 assert_eq!(nested_condition.raw_key().to_string(), "country.cities");
4699 assert_eq!(nested_condition.array_key().to_string(), "country.cities[]");
4700
4701 let nested_must = nested_condition.filter().must.as_ref().unwrap();
4702 assert_eq!(nested_must.len(), 1);
4703
4704 let must = nested_must.first().unwrap();
4705 let Condition::Field(c) = must else {
4706 panic!("Condition::Field expected, got {must:?}")
4707 };
4708
4709 assert_eq!(c.key.to_string(), "population");
4710 assert!(c.range.is_some());
4711 }
4712
4713 #[test]
4714 fn test_payload_query_parse() {
4715 let query1 = r#"
4716 {
4717 "must": [
4718 {
4719 "key": "hello",
4720 "match": {
4721 "value": 42
4722 }
4723 },
4724 {
4725 "must_not": [
4726 {
4727 "has_id": [1, 2, 3, 4]
4728 },
4729 {
4730 "key": "geo_field",
4731 "geo_bounding_box": {
4732 "top_left": {
4733 "lon": 13.410146,
4734 "lat": 52.519289
4735 },
4736 "bottom_right": {
4737 "lon": 13.432683,
4738 "lat": 52.505582
4739 }
4740 }
4741 }
4742 ]
4743 }
4744 ]
4745 }
4746 "#;
4747
4748 let filter: Filter = serde_json::from_str(query1).unwrap();
4749 eprintln!("{filter:?}");
4750 let must = filter.must.unwrap();
4751 let _must_not = filter.must_not;
4752 assert_eq!(must.len(), 2);
4753 match must.get(1) {
4754 Some(Condition::Filter(f)) => {
4755 let must_not = &f.must_not;
4756 match must_not {
4757 Some(v) => assert_eq!(v.len(), 2),
4758 None => panic!("Filter expected"),
4759 }
4760 }
4761 _ => panic!("Condition expected"),
4762 }
4763 }
4764
4765 #[test]
4766 fn test_nested_payload_query_parse() {
4767 let query1 = r#"
4768 {
4769 "must": [
4770 {
4771 "key": "hello.nested.world",
4772 "match": {
4773 "value": 42
4774 }
4775 },
4776 {
4777 "key": "foo.nested.bar",
4778 "match": {
4779 "value": 1
4780 }
4781 }
4782 ]
4783 }
4784 "#;
4785
4786 let filter: Filter = serde_json::from_str(query1).unwrap();
4787 let must = filter.must.unwrap();
4788 assert_eq!(must.len(), 2);
4789 }
4790
4791 #[test]
4792 fn test_min_should_query_parse() {
4793 let query1 = r#"
4794 {
4795 "min_should": {
4796 "conditions": [
4797 {
4798 "key": "hello.nested.world",
4799 "match": {
4800 "value": 42
4801 }
4802 },
4803 {
4804 "key": "foo.nested.bar",
4805 "match": {
4806 "value": 1
4807 }
4808 }
4809 ],
4810 "min_count": 2
4811 }
4812 }
4813 "#;
4814
4815 let filter: Filter = serde_json::from_str(query1).unwrap();
4816 let min_should = filter.min_should.unwrap();
4817 assert_eq!(min_should.conditions.len(), 2);
4818 }
4819
4820 #[test]
4821 fn test_min_should_nested_parse() {
4822 let query1 = r#"
4823 {
4824 "must": [
4825 {
4826 "min_should": {
4827 "conditions": [
4828 {
4829 "key": "hello.nested.world",
4830 "match": {
4831 "value": 42
4832 }
4833 },
4834 {
4835 "key": "foo.nested.bar",
4836 "match": {
4837 "value": 1
4838 }
4839 }
4840 ],
4841 "min_count": 2
4842 }
4843 }
4844 ]
4845 }
4846 "#;
4847
4848 let filter: Filter = serde_json::from_str(query1).unwrap();
4849 let must = filter.must.unwrap();
4850 assert_eq!(must.len(), 1);
4851
4852 match must.first() {
4853 Some(Condition::Filter(f)) => {
4854 let min_should = &f.min_should;
4855 match min_should {
4856 Some(v) => assert_eq!(v.conditions.len(), 2),
4857 None => panic!("Filter expected"),
4858 }
4859 }
4860 _ => panic!("Condition expected"),
4861 }
4862 }
4863
4864 #[test]
4865 fn test_geo_validation() {
4866 let query1 = r#"
4867 {
4868 "must": [
4869 {
4870 "key": "geo_field",
4871 "geo_bounding_box": {
4872 "top_left": {
4873 "lon": 1113.410146,
4874 "lat": 52.519289
4875 },
4876 "bottom_right": {
4877 "lon": 13.432683,
4878 "lat": 52.505582
4879 }
4880 }
4881 }
4882 ]
4883 }
4884 "#;
4885 let filter: Result<Filter, _> = serde_json::from_str(query1);
4886 assert!(filter.is_err());
4887
4888 let query2 = r#"
4889 {
4890 "must": [
4891 {
4892 "key": "geo_field",
4893 "geo_polygon": {
4894 "exterior": {},
4895 "interiors": []
4896 }
4897 }
4898 ]
4899 }
4900 "#;
4901 let filter: Result<Filter, _> = serde_json::from_str(query2);
4902 assert!(filter.is_err());
4903
4904 let query3 = r#"
4905 {
4906 "must": [
4907 {
4908 "key": "geo_field",
4909 "geo_polygon": {
4910 "exterior":{
4911 "points": [
4912 {"lon": -12.0, "lat": -34.0},
4913 {"lon": 11.0, "lat": -22.0},
4914 {"lon": -32.0, "lat": -14.0}
4915 ]
4916 },
4917 "interiors": []
4918 }
4919 }
4920 ]
4921 }
4922 "#;
4923 let filter: Result<Filter, _> = serde_json::from_str(query3);
4924 assert!(filter.is_err());
4925
4926 let query4 = 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 {"lon": -12.0, "lat": -34.0}
4938 ]
4939 },
4940 "interiors": []
4941 }
4942 }
4943 ]
4944 }
4945 "#;
4946 let filter: Result<Filter, _> = serde_json::from_str(query4);
4947 assert!(filter.is_ok());
4948
4949 let query5 = r#"
4950 {
4951 "must": [
4952 {
4953 "key": "geo_field",
4954 "geo_polygon": {
4955 "exterior": {
4956 "points": [
4957 {"lon": -12.0, "lat": -34.0},
4958 {"lon": 11.0, "lat": -22.0},
4959 {"lon": -32.0, "lat": -14.0},
4960 {"lon": -12.0, "lat": -34.0}
4961 ]
4962 },
4963 "interiors": [
4964 {
4965 "points": [
4966 {"lon": -12.0, "lat": -34.0},
4967 {"lon": 11.0, "lat": -22.0},
4968 {"lon": -32.0, "lat": -14.0}
4969 ]
4970 }
4971 ]
4972 }
4973 }
4974 ]
4975 }
4976 "#;
4977 let filter: Result<Filter, _> = serde_json::from_str(query5);
4978 assert!(filter.is_err());
4979
4980 let query6 = r#"
4981 {
4982 "must": [
4983 {
4984 "key": "geo_field",
4985 "geo_polygon": {
4986 "exterior": {
4987 "points": [
4988 {"lon": -12.0, "lat": -34.0},
4989 {"lon": 11.0, "lat": -22.0},
4990 {"lon": -32.0, "lat": -14.0},
4991 {"lon": -12.0, "lat": -34.0}
4992 ]
4993 },
4994 "interiors": [
4995 {
4996 "points": [
4997 {"lon": -12.0, "lat": -34.0},
4998 {"lon": 11.0, "lat": -22.0},
4999 {"lon": -32.0, "lat": -14.0},
5000 {"lon": -12.0, "lat": -34.0}
5001 ]
5002 }
5003 ]
5004 }
5005 }
5006 ]
5007 }
5008 "#;
5009 let filter: Result<Filter, _> = serde_json::from_str(query6);
5010 assert!(filter.is_ok());
5011 }
5012
5013 #[test]
5014 fn test_payload_parsing() {
5015 let ft = PayloadFieldSchema::FieldType(PayloadSchemaType::Keyword);
5016 let ft_json = serde_json::to_string(&ft).unwrap();
5017 eprintln!("ft_json = {ft_json:?}");
5018
5019 let ft = PayloadFieldSchema::FieldParams(PayloadSchemaParams::Text(Default::default()));
5020 let ft_json = serde_json::to_string(&ft).unwrap();
5021 eprintln!("ft_json = {ft_json:?}");
5022
5023 let query = r#""keyword""#;
5024 let field_type: PayloadSchemaType = serde_json::from_str(query).unwrap();
5025 eprintln!("field_type = {field_type:?}");
5026 }
5027
5028 #[test]
5029 fn merge_filters() {
5030 let condition1 = Condition::Field(FieldCondition::new_match(
5031 JsonPath::new("summary"),
5032 Match::new_text("Berlin"),
5033 ));
5034 let mut this = Filter::new_must(condition1.clone());
5035 this.should = Some(vec![condition1.clone()]);
5036
5037 let condition2 = Condition::Field(FieldCondition::new_match(
5038 JsonPath::new("city"),
5039 Match::new_value(ValueVariants::String("Osaka".into())),
5040 ));
5041 let other = Filter::new_must(condition2.clone());
5042
5043 let merged = this.merge(&other);
5044
5045 assert!(merged.must.is_some());
5046 assert_eq!(merged.must.as_ref().unwrap().len(), 2);
5047 assert!(merged.must_not.is_none());
5048 assert!(merged.should.is_some());
5049 assert_eq!(merged.should.as_ref().unwrap().len(), 1);
5050
5051 assert!(merged.must.as_ref().unwrap().contains(&condition1));
5052 assert!(merged.must.as_ref().unwrap().contains(&condition2));
5053 assert!(merged.should.as_ref().unwrap().contains(&condition1));
5054 }
5055
5056 #[test]
5057 fn test_payload_selector_include() {
5058 let payload = payload_json! {
5059 "a": 1,
5060 "b": {
5061 "c": 123,
5062 "e": {
5063 "f": [1,2,3],
5064 "g": 7,
5065 "h": "text",
5066 "i": [
5067 {
5068 "j": 1,
5069 "k": 2
5070
5071 },
5072 {
5073 "j": 3,
5074 "k": 4
5075 }
5076 ]
5077 }
5078 }
5079 };
5080
5081 let selector =
5083 PayloadSelector::new_include(vec![JsonPath::new("a"), JsonPath::new("b.e.f")]);
5084 let payload = selector.process(payload);
5085
5086 let expected = payload_json! {
5087 "a": 1,
5088 "b": {
5089 "e": {
5090 "f": [1,2,3],
5091 }
5092 }
5093 };
5094 assert_eq!(payload, expected);
5095 }
5096
5097 #[test]
5098 fn test_payload_selector_array_include() {
5099 let payload = payload_json! {
5100 "a": 1,
5101 "b": {
5102 "c": 123,
5103 "f": [1,2,3,4,5],
5104 }
5105 };
5106
5107 let selector = PayloadSelector::new_include(vec![JsonPath::new("a"), JsonPath::new("a")]);
5109 let payload = selector.process(payload);
5110
5111 let expected = payload_json! {
5112 "a": 1
5113 };
5114 assert_eq!(payload, expected);
5115
5116 let selector = PayloadSelector::new_include(vec![JsonPath::new("b.f[0]")]);
5118 let payload = selector.process(payload);
5119
5120 let expected = payload_json! {};
5122 assert_eq!(payload, expected);
5123 }
5124
5125 #[test]
5126 fn test_payload_selector_no_implicit_array_include() {
5127 let payload = payload_json! {
5128 "a": 1,
5129 "b": {
5130 "c": [
5131 {
5132 "d": 1,
5133 "e": 2
5134 },
5135 {
5136 "d": 3,
5137 "e": 4
5138 }
5139 ],
5140 }
5141 };
5142
5143 let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c")]);
5144 let selected_payload = selector.process(payload.clone());
5145
5146 let expected = payload_json! {
5147 "b": {
5148 "c": [
5149 {
5150 "d": 1,
5151 "e": 2
5152 },
5153 {
5154 "d": 3,
5155 "e": 4
5156 }
5157 ]
5158 }
5159 };
5160 assert_eq!(selected_payload, expected);
5161
5162 let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c[].d")]);
5164 let selected_payload = selector.process(payload.clone());
5165
5166 let expected = payload_json! {
5167 "b": {
5168 "c": [
5169 {"d": 1},
5170 {"d": 3}
5171 ]
5172 }
5173 };
5174 assert_eq!(selected_payload, expected);
5175
5176 let selector = PayloadSelector::new_include(vec![JsonPath::new("b.c.d")]);
5178 let selected_payload = selector.process(payload);
5179
5180 let expected = payload_json! {
5181 "b": {
5182 "c": []
5183 }
5184 };
5185 assert_eq!(selected_payload, expected);
5186 }
5187
5188 #[test]
5189 fn test_payload_selector_exclude() {
5190 let payload = payload_json! {
5191 "a": 1,
5192 "b": {
5193 "c": 123,
5194 "e": {
5195 "f": [1,2,3],
5196 "g": 7,
5197 "h": "text",
5198 "i": [
5199 {
5200 "j": 1,
5201 "k": 2
5202
5203 },
5204 {
5205 "j": 3,
5206 "k": 4
5207 }
5208 ]
5209 }
5210 }
5211 };
5212
5213 let selector =
5215 PayloadSelector::new_exclude(vec![JsonPath::new("a"), JsonPath::new("b.e.f")]);
5216 let payload = selector.process(payload);
5217
5218 let expected = payload_json! {
5220 "b": {
5221 "c": 123,
5222 "e": {
5223 "g": 7,
5224 "h": "text",
5225 "i": [
5226 {
5227 "j": 1,
5228 "k": 2
5229
5230 },
5231 {
5232 "j": 3,
5233 "k": 4
5234 }
5235 ]
5236 }
5237 }
5238 };
5239 assert_eq!(payload, expected);
5240 }
5241
5242 #[test]
5243 fn test_payload_selector_array_exclude() {
5244 let payload = payload_json! {
5245 "a": 1,
5246 "b": {
5247 "c": 123,
5248 "f": [1,2,3,4,5],
5249 }
5250 };
5251
5252 let selector = PayloadSelector::new_exclude(vec![JsonPath::new("a"), JsonPath::new("a")]);
5254 let payload = selector.process(payload);
5255
5256 let expected = payload_json! {
5258 "b": {
5259 "c": 123,
5260 "f": [1,2,3,4,5],
5261 }
5262 };
5263 assert_eq!(payload, expected);
5264
5265 let selector = PayloadSelector::new_exclude(vec![JsonPath::new("b.f[0]")]);
5267
5268 let payload = selector.process(payload);
5269
5270 let expected = payload_json! {
5272 "b": {
5273 "c": 123,
5274 "f": [1,2,3,4,5],
5275 }
5276 };
5277 assert_eq!(payload, expected);
5278 }
5279
5280 #[test]
5281 fn test_extended_point_id_cbor_roundtrip() {
5282 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
5283
5284 for point_id in [ExtendedPointId::Uuid(uuid), ExtendedPointId::NumId(42)] {
5285 let cbor_bytes = serde_cbor::to_vec(&point_id).unwrap();
5286 let deserialized: ExtendedPointId = serde_cbor::from_slice(&cbor_bytes).unwrap();
5287 assert_eq!(point_id, deserialized);
5288 }
5289 }
5290
5291 #[test]
5292 fn test_filter_with_match_and_has_id_uuid_cbor_roundtrip() {
5293 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
5294 let filter = Filter {
5295 should: None,
5296 min_should: None,
5297 must: Some(vec![Condition::Field(FieldCondition::new_match(
5298 crate::segment::json_path::JsonPath::new("org_id"),
5299 Match::new_value(ValueVariants::String("test_org".to_string())),
5300 ))]),
5301 must_not: Some(vec![Condition::HasId(HasIdCondition {
5302 has_id: [ExtendedPointId::Uuid(uuid)].into_iter().collect(),
5303 })]),
5304 };
5305
5306 let cbor_bytes = serde_cbor::to_vec(&filter).unwrap();
5307 let deserialized: Filter = serde_cbor::from_slice(&cbor_bytes).unwrap();
5308 assert_eq!(filter, deserialized);
5309 }
5310}
5311
5312fn shard_key_string_example() -> String {
5313 "region_1".to_string()
5314}
5315
5316fn shard_key_number_example() -> u64 {
5317 12
5318}
5319
5320#[derive(Deserialize, Serialize, JsonSchema, Debug, Clone, PartialEq, Eq, Hash)]
5321#[serde(untagged)]
5322pub enum ShardKey {
5323 #[schemars(
5324 schema_with = "String::json_schema",
5325 example = "shard_key_string_example"
5326 )]
5327 Keyword(EcoString),
5328 #[schemars(example = "shard_key_number_example")]
5329
5330 Number(u64),
5331}
5332
5333impl From<String> for ShardKey {
5334 fn from(s: String) -> Self {
5335 ShardKey::Keyword(EcoString::from(s))
5336 }
5337}
5338
5339impl From<EcoString> for ShardKey {
5340 fn from(s: EcoString) -> Self {
5341 ShardKey::Keyword(s)
5342 }
5343}
5344
5345impl From<&str> for ShardKey {
5346 fn from(s: &str) -> Self {
5347 ShardKey::Keyword(EcoString::from(s))
5348 }
5349}
5350
5351impl From<u64> for ShardKey {
5352 fn from(n: u64) -> Self {
5353 ShardKey::Number(n)
5354 }
5355}
5356
5357impl Display for ShardKey {
5358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5359 match self {
5360 ShardKey::Keyword(keyword) => write!(f, "\"{keyword}\""),
5361 ShardKey::Number(number) => write!(f, "{number}"),
5362 }
5363 }
5364}