1use std::collections::{HashMap, HashSet};
2use std::fmt::{Debug, Formatter};
3use std::hash::{Hash, Hasher};
4use std::mem;
5
6#[cfg(feature = "api")]
7use api::grpc::RawPayload;
8use crate::common::validation::validate_multi_vector;
9use itertools::Itertools as _;
10use ordered_float::OrderedFloat;
11use schemars::JsonSchema;
12use crate::segment::common::operation_error::OperationError;
13use crate::segment::common::utils::unordered_hash_unique;
14use crate::segment::data_types::named_vectors::NamedVectors;
15use crate::segment::data_types::segment_record::{SegmentRecord, SegmentRecordRaw};
16use crate::segment::data_types::vectors::{
17 BatchVectorStructInternal, DEFAULT_VECTOR_NAME, DenseVector, MultiDenseVector,
18 MultiDenseVectorInternal, VectorInternal, VectorRef, VectorStructInternal,
19};
20use crate::segment::types::{Filter, Payload, PointIdType, VectorNameBuf};
21use serde::{Deserialize, Serialize};
22use smallvec::SmallVec;
23use crate::sparse::common::types::{DimId, DimWeight};
24use strum::{EnumDiscriminants, EnumIter};
25use validator::{Validate, ValidationErrors};
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize, Hash)]
33#[serde(rename_all = "snake_case")]
34pub enum UpdateMode {
35 #[default]
37 Upsert,
38 InsertOnly,
40 UpdateOnly,
42}
43
44#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Validate, Hash)]
45#[serde(rename_all = "snake_case")]
46pub struct PointIdsList {
47 pub points: Vec<PointIdType>,
48 #[cfg(feature = "api")]
49 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub shard_key: Option<api::rest::ShardKeySelector>,
51}
52
53impl From<Vec<PointIdType>> for PointIdsList {
54 fn from(points: Vec<PointIdType>) -> Self {
55 Self {
56 points,
57 #[cfg(feature = "api")]
58 shard_key: None,
59 }
60 }
61}
62
63#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, EnumDiscriminants, Hash)]
109#[strum_discriminants(derive(EnumIter))]
110#[serde(rename_all = "snake_case")]
111pub enum PointOperations {
112 UpsertPoints(PointInsertOperationsInternal),
114 UpsertPointsConditional(ConditionalInsertOperationInternal),
116 DeletePoints { ids: Vec<PointIdType> },
118 DeletePointsByFilter(Filter),
120 SyncPoints(PointSyncOperation),
122 UpsertPointsRaw(Vec<PointStructRawPersisted>),
124 SyncPointsRaw(PointSyncRawOperation),
126}
127
128impl PointOperations {
129 pub fn point_ids(&self) -> Option<Vec<PointIdType>> {
130 match self {
131 Self::UpsertPoints(op) => Some(op.point_ids()),
132 Self::UpsertPointsConditional(op) => Some(op.points_op.point_ids()),
133 Self::DeletePoints { ids } => Some(ids.clone()),
134 Self::DeletePointsByFilter(_) => None,
135 Self::SyncPoints(op) => Some(op.points.iter().map(|point| point.id).collect()),
136 Self::UpsertPointsRaw(points) => Some(points.iter().map(|point| point.id).collect()),
137 Self::SyncPointsRaw(op) => Some(op.points.iter().map(|point| point.id).collect()),
138 }
139 }
140
141 pub fn retain_point_ids<F>(&mut self, filter: F)
142 where
143 F: Fn(&PointIdType) -> bool,
144 {
145 match self {
146 Self::UpsertPoints(op) => op.retain_point_ids(filter),
147 Self::UpsertPointsConditional(op) => {
148 op.points_op.retain_point_ids(filter);
149 }
150 Self::DeletePoints { ids } => ids.retain(filter),
151 Self::DeletePointsByFilter(_) => (),
152 Self::SyncPoints(op) => op.points.retain(|point| filter(&point.id)),
153 Self::UpsertPointsRaw(points) => points.retain(|point| filter(&point.id)),
154 Self::SyncPointsRaw(op) => op.points.retain(|point| filter(&point.id)),
155 }
156 }
157
158 pub fn retain_vector_names(&mut self, valid: &HashSet<VectorNameBuf>) {
161 match self {
162 Self::UpsertPoints(op) => op.retain_vector_names(valid),
163 Self::UpsertPointsConditional(op) => op.points_op.retain_vector_names(valid),
164 Self::SyncPoints(op) => {
165 for point in &mut op.points {
166 point.vector.retain_vector_names(valid);
167 }
168 }
169 Self::UpsertPointsRaw(points) => {
170 for point in points {
171 point.vectors.retain(|(name, _)| valid.contains(name));
172 }
173 }
174 Self::SyncPointsRaw(op) => {
175 for point in &mut op.points {
176 point.vectors.retain(|(name, _)| valid.contains(name));
177 }
178 }
179 Self::DeletePoints { .. } | Self::DeletePointsByFilter(_) => (),
180 }
181 }
182}
183
184#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, EnumDiscriminants, Hash)]
185#[strum_discriminants(derive(EnumIter))]
186#[serde(rename_all = "snake_case")]
187pub enum PointInsertOperationsInternal {
188 #[serde(rename = "batch")]
190 PointsBatch(BatchPersisted),
191 #[serde(rename = "points")]
193 PointsList(Vec<PointStructPersisted>),
194}
195
196impl PointInsertOperationsInternal {
197 pub fn point_ids(&self) -> Vec<PointIdType> {
198 match self {
199 Self::PointsBatch(batch) => batch.ids.clone(),
200 Self::PointsList(points) => points.iter().map(|point| point.id).collect(),
201 }
202 }
203
204 pub fn into_point_vec(self) -> Vec<PointStructPersisted> {
205 match self {
206 PointInsertOperationsInternal::PointsBatch(batch) => {
207 let batch_vectors = BatchVectorStructInternal::from(batch.vectors);
208 let all_vectors = batch_vectors.into_all_vectors(batch.ids.len());
209 let vectors_iter = batch.ids.into_iter().zip(all_vectors);
210 match batch.payloads {
211 None => vectors_iter
212 .map(|(id, vectors)| PointStructPersisted {
213 id,
214 vector: VectorStructInternal::from(vectors).into(),
215 payload: None,
216 })
217 .collect(),
218 Some(payloads) => vectors_iter
219 .zip(payloads)
220 .map(|((id, vectors), payload)| PointStructPersisted {
221 id,
222 vector: VectorStructInternal::from(vectors).into(),
223 payload,
224 })
225 .collect(),
226 }
227 }
228 PointInsertOperationsInternal::PointsList(points) => points,
229 }
230 }
231
232 pub fn retain_vector_names(&mut self, valid: &HashSet<VectorNameBuf>) {
235 match self {
236 Self::PointsBatch(batch) => batch.vectors.retain_vector_names(valid),
237 Self::PointsList(points) => {
238 for point in points {
239 point.vector.retain_vector_names(valid);
240 }
241 }
242 }
243 }
244
245 pub fn retain_point_ids<F>(&mut self, filter: F)
246 where
247 F: Fn(&PointIdType) -> bool,
248 {
249 match self {
250 Self::PointsBatch(batch) => {
251 let mut retain_indices = HashSet::new();
252
253 retain_with_index(&mut batch.ids, |index, id| {
254 if filter(id) {
255 retain_indices.insert(index);
256 true
257 } else {
258 false
259 }
260 });
261
262 match &mut batch.vectors {
263 BatchVectorStructPersisted::Single(vectors) => {
264 retain_with_index(vectors, |index, _| retain_indices.contains(&index));
265 }
266
267 BatchVectorStructPersisted::MultiDense(vectors) => {
268 retain_with_index(vectors, |index, _| retain_indices.contains(&index));
269 }
270
271 BatchVectorStructPersisted::Named(vectors) => {
272 for vectors in vectors.values_mut() {
273 retain_with_index(vectors, |index, _| retain_indices.contains(&index));
274 }
275 }
276 }
277
278 if let Some(payload) = &mut batch.payloads {
279 retain_with_index(payload, |index, _| retain_indices.contains(&index));
280 }
281 }
282
283 Self::PointsList(points) => points.retain(|point| filter(&point.id)),
284 }
285 }
286}
287
288impl From<BatchPersisted> for PointInsertOperationsInternal {
289 fn from(batch: BatchPersisted) -> Self {
290 PointInsertOperationsInternal::PointsBatch(batch)
291 }
292}
293
294impl From<Vec<PointStructPersisted>> for PointInsertOperationsInternal {
295 fn from(points: Vec<PointStructPersisted>) -> Self {
296 PointInsertOperationsInternal::PointsList(points)
297 }
298}
299
300#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Hash)]
301pub struct ConditionalInsertOperationInternal {
302 pub points_op: PointInsertOperationsInternal,
303 pub condition: Filter,
305 #[serde(default, skip_serializing_if = "Option::is_none")]
307 pub update_mode: Option<UpdateMode>,
308}
309
310#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Hash)]
311pub struct PointSyncOperation {
312 pub from_id: Option<PointIdType>,
314 pub to_id: Option<PointIdType>,
316 pub points: Vec<PointStructPersisted>,
317}
318
319#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Hash)]
320pub struct PointSyncRawOperation {
321 pub from_id: Option<PointIdType>,
323 pub to_id: Option<PointIdType>,
325 pub points: Vec<PointStructRawPersisted>,
326}
327
328pub type RawVectorsPersisted = SmallVec<[(VectorNameBuf, Vec<u8>); 1]>;
329
330#[derive(Clone, PartialEq, Deserialize, Serialize, Hash)]
332#[serde(rename_all = "snake_case")]
333pub struct PointStructRawPersisted {
334 pub id: PointIdType,
336 #[serde(with = "raw_vectors_serde")]
338 pub vectors: RawVectorsPersisted,
339 pub payload: Option<Payload>,
341}
342
343mod raw_vectors_serde {
350 use std::fmt;
351
352 use crate::segment::types::VectorNameBuf;
353 use serde::de::{self, Deserializer, SeqAccess, Visitor};
354 use serde::ser::{SerializeSeq, Serializer};
355
356 use super::RawVectorsPersisted;
357
358 const MAX_RAW_VECTOR_PREALLOC: usize = 128 * 1024 * 1024;
366
367 struct BytesRef<'a>(&'a [u8]);
369
370 impl serde::Serialize for BytesRef<'_> {
371 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
372 serializer.serialize_bytes(self.0)
373 }
374 }
375
376 pub fn serialize<S: Serializer>(
377 vectors: &[(VectorNameBuf, Vec<u8>)],
378 serializer: S,
379 ) -> Result<S::Ok, S::Error> {
380 let mut seq = serializer.serialize_seq(Some(vectors.len()))?;
381 for (name, bytes) in vectors {
382 seq.serialize_element(&(name, BytesRef(bytes)))?;
383 }
384 seq.end()
385 }
386
387 struct ByteVec(Vec<u8>);
389
390 impl<'de> serde::Deserialize<'de> for ByteVec {
391 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
392 struct ByteVecVisitor;
393
394 impl<'de> Visitor<'de> for ByteVecVisitor {
395 type Value = Vec<u8>;
396
397 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
398 formatter.write_str("a byte string")
399 }
400
401 fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
402 Ok(value.to_vec())
403 }
404
405 fn visit_byte_buf<E: de::Error>(self, value: Vec<u8>) -> Result<Self::Value, E> {
406 Ok(value)
407 }
408
409 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
412 let capacity = seq.size_hint().unwrap_or(0).min(MAX_RAW_VECTOR_PREALLOC);
413 let mut bytes = Vec::with_capacity(capacity);
414 while let Some(byte) = seq.next_element()? {
415 bytes.push(byte);
416 }
417 Ok(bytes)
418 }
419 }
420
421 deserializer
422 .deserialize_byte_buf(ByteVecVisitor)
423 .map(ByteVec)
424 }
425 }
426
427 pub fn deserialize<'de, D: Deserializer<'de>>(
428 deserializer: D,
429 ) -> Result<RawVectorsPersisted, D::Error> {
430 let raw: Vec<(VectorNameBuf, ByteVec)> = serde::Deserialize::deserialize(deserializer)?;
431 Ok(raw
432 .into_iter()
433 .map(|(name, bytes)| (name, bytes.0))
434 .collect())
435 }
436}
437
438impl From<SegmentRecordRaw> for PointStructRawPersisted {
439 fn from(record: SegmentRecordRaw) -> Self {
440 let SegmentRecordRaw {
441 id,
442 vectors,
443 payload,
444 } = record;
445
446 Self {
447 id,
448 vectors: vectors.unwrap_or_default(),
449 payload,
450 }
451 }
452}
453
454impl PointStructRawPersisted {
455 pub fn is_equal_to(&self, segment_record: &SegmentRecordRaw) -> bool {
456 let SegmentRecordRaw {
457 id,
458 vectors,
459 payload,
460 } = segment_record;
461
462 if &self.id != id {
463 return false;
464 }
465
466 let segment_vectors = vectors.as_deref().unwrap_or(&[]);
467 if self.vectors.len() != segment_vectors.len() {
468 return false;
469 }
470 for (name, bytes) in segment_vectors {
471 let own_bytes = self
472 .vectors
473 .iter()
474 .find(|(own_name, _)| own_name == name)
475 .map(|(_, bytes)| bytes);
476 if own_bytes != Some(bytes) {
477 return false;
478 }
479 }
480
481 let self_payload = self.payload.as_ref().filter(|p| !p.is_empty());
483 let segment_payload = payload.as_ref().filter(|p| !p.is_empty());
484 self_payload == segment_payload
485 }
486}
487
488#[cfg(feature = "api")]
489impl From<PointStructRawPersisted> for api::grpc::qdrant::PointStructRaw {
490 fn from(value: PointStructRawPersisted) -> Self {
491 let PointStructRawPersisted {
492 id,
493 vectors,
494 payload,
495 } = value;
496
497 Self {
498 id: Some(id.into()),
499 vectors: vectors.into_iter().collect(),
500 payload: payload
501 .map(api::conversions::json::payload_to_proto)
502 .unwrap_or_default(),
503 raw_payload: None,
504 }
505 }
506}
507
508#[cfg(feature = "api")]
509impl TryFrom<api::grpc::qdrant::PointStructRaw> for PointStructRawPersisted {
510 type Error = tonic::Status;
511
512 fn try_from(value: api::grpc::qdrant::PointStructRaw) -> Result<Self, Self::Error> {
513 let api::grpc::qdrant::PointStructRaw {
514 id,
515 vectors,
516 payload,
517 raw_payload,
518 } = value;
519
520 let id = id
521 .ok_or_else(|| tonic::Status::invalid_argument("Empty id is not allowed"))?
522 .try_into()?;
523
524 let payload = match raw_payload {
527 Some(raw_payload) => decode_payload(raw_payload)?,
528 None => api::conversions::json::proto_to_payloads(payload)?,
529 };
530 let payload = (!payload.is_empty()).then_some(payload);
531
532 Ok(Self {
533 id,
534 vectors: vectors.into_iter().collect(),
535 payload,
536 })
537 }
538}
539
540#[cfg(feature = "api")]
542fn decode_payload(raw_payload: RawPayload) -> Result<Payload, tonic::Status> {
543 match raw_payload.encoding() {
544 api::grpc::RawPayloadEncoding::JsonBytes => {
545 serde_json::from_slice(&raw_payload.payload_bytes).map_err(|err| {
546 tonic::Status::invalid_argument(format!("Malformed raw payload blob: {err}"))
547 })
548 }
549 }
550}
551
552impl Debug for PointStructRawPersisted {
553 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
554 let vectors = self
555 .vectors
556 .iter()
557 .map(|(name, bytes)| format!("{name}: {} bytes", bytes.len()))
558 .join(", ");
559 write!(
560 f,
561 "PointStructRawPersisted {{ id: {}, vectors: [{vectors}], payload: {:?} }}",
562 self.id, self.payload,
563 )
564 }
565}
566
567#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Hash)]
568#[serde(rename_all = "snake_case")]
569pub struct BatchPersisted {
570 pub ids: Vec<PointIdType>,
571 pub vectors: BatchVectorStructPersisted,
572 pub payloads: Option<Vec<Option<Payload>>>,
573}
574
575#[cfg(feature = "api")]
576impl TryFrom<BatchPersisted> for Vec<api::grpc::qdrant::PointStruct> {
577 type Error = tonic::Status;
578
579 fn try_from(batch: BatchPersisted) -> Result<Self, Self::Error> {
580 let BatchPersisted {
581 ids,
582 vectors,
583 payloads,
584 } = batch;
585 let mut points = Vec::with_capacity(ids.len());
586 let batch_vectors = BatchVectorStructInternal::from(vectors);
587 let all_vectors = batch_vectors.into_all_vectors(ids.len());
588 for (i, p_id) in ids.into_iter().enumerate() {
589 let id = Some(p_id.into());
590 let vector = all_vectors.get(i).cloned();
591 let payload = payloads.as_ref().and_then(|payloads| {
592 payloads.get(i).map(|payload| match payload {
593 None => HashMap::new(),
594 Some(payload) => api::conversions::json::payload_to_proto(payload.clone()),
595 })
596 });
597 let vectors: Option<VectorStructInternal> = vector.map(NamedVectors::into);
598
599 let point = api::grpc::qdrant::PointStruct {
600 id,
601 vectors: vectors.map(api::grpc::qdrant::Vectors::from),
602 payload: payload.unwrap_or_default(),
603 };
604 points.push(point);
605 }
606
607 Ok(points)
608 }
609}
610
611#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
612#[serde(untagged, rename_all = "snake_case")]
613pub enum BatchVectorStructPersisted {
614 Single(Vec<DenseVector>),
615 MultiDense(Vec<MultiDenseVector>),
616 Named(HashMap<VectorNameBuf, Vec<VectorPersisted>>),
617}
618
619impl BatchVectorStructPersisted {
620 pub fn retain_vector_names(&mut self, valid: &HashSet<VectorNameBuf>) {
623 if let BatchVectorStructPersisted::Named(named) = self {
624 named.retain(|name, _| valid.contains(name));
625 }
626 }
627}
628
629impl Hash for BatchVectorStructPersisted {
630 fn hash<H: Hasher>(&self, state: &mut H) {
631 mem::discriminant(self).hash(state);
632 match self {
633 BatchVectorStructPersisted::Single(dense) => {
634 for vector in dense {
635 for v in vector {
636 OrderedFloat(*v).hash(state);
637 }
638 }
639 }
640 BatchVectorStructPersisted::MultiDense(multidense) => {
641 for vector in multidense {
642 for v in vector {
643 for element in v {
644 OrderedFloat(*element).hash(state);
645 }
646 }
647 }
648 }
649 BatchVectorStructPersisted::Named(named) => unordered_hash_unique(state, named.iter()),
650 }
651 }
652}
653
654impl From<BatchVectorStructPersisted> for BatchVectorStructInternal {
655 fn from(value: BatchVectorStructPersisted) -> Self {
656 match value {
657 BatchVectorStructPersisted::Single(vector) => BatchVectorStructInternal::Single(vector),
658 BatchVectorStructPersisted::MultiDense(vectors) => {
659 BatchVectorStructInternal::MultiDense(
660 vectors
661 .into_iter()
662 .map(MultiDenseVectorInternal::new_unchecked)
663 .collect(),
664 )
665 }
666 BatchVectorStructPersisted::Named(vectors) => BatchVectorStructInternal::Named(
667 vectors
668 .into_iter()
669 .map(|(k, v)| (k, v.into_iter().map(VectorInternal::from).collect()))
670 .collect(),
671 ),
672 }
673 }
674}
675
676#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Validate, Hash)]
677#[serde(rename_all = "snake_case")]
678pub struct PointStructPersisted {
679 pub id: PointIdType,
681 pub vector: VectorStructPersisted,
683 pub payload: Option<Payload>,
685}
686
687impl PointStructPersisted {
688 pub fn get_vectors(&self) -> NamedVectors<'_> {
689 let mut named_vectors = NamedVectors::default();
690 match &self.vector {
691 VectorStructPersisted::Single(vector) => named_vectors.insert(
692 DEFAULT_VECTOR_NAME.to_owned(),
693 VectorInternal::from(vector.clone()),
694 ),
695 VectorStructPersisted::MultiDense(vector) => named_vectors.insert(
696 DEFAULT_VECTOR_NAME.to_owned(),
697 VectorInternal::from(MultiDenseVectorInternal::new_unchecked(vector.clone())),
698 ),
699 VectorStructPersisted::Named(vectors) => {
700 for (name, vector) in vectors {
701 named_vectors.insert(name.clone(), VectorInternal::from(vector.clone()));
702 }
703 }
704 }
705 named_vectors
706 }
707
708 pub fn is_equal_to(&self, segment_record: &SegmentRecord) -> bool {
709 let SegmentRecord {
710 id,
711 vectors,
712 payload,
713 } = segment_record;
714
715 if &self.id != id {
716 return false;
717 }
718
719 let self_vectors = self.get_vectors();
720
721 if let Some(segment_vectors) = vectors {
722 if self_vectors.len() != segment_vectors.len() {
723 return false;
724 }
725 for (name, vec) in segment_vectors {
726 if self_vectors.get(name) != Some(VectorRef::from(vec)) {
727 return false;
728 }
729 }
730 } else if !self_vectors.is_empty() {
731 return false;
732 }
733
734 let self_payload = self.payload.as_ref().filter(|p| !p.is_empty());
736 let segment_payload = payload.as_ref().filter(|p| !p.is_empty());
737 self_payload == segment_payload
738 }
739}
740
741#[cfg(feature = "api")]
742impl TryFrom<api::rest::schema::Record> for PointStructPersisted {
743 type Error = String;
744
745 fn try_from(record: api::rest::schema::Record) -> Result<Self, Self::Error> {
746 let api::rest::schema::Record {
747 id,
748 payload,
749 vector,
750 shard_key: _,
751 order_value: _,
752 } = record;
753
754 if vector.is_none() {
755 return Err("Vector is empty".to_string());
756 }
757
758 Ok(Self {
759 id,
760 payload,
761 vector: VectorStructPersisted::from(vector.unwrap()),
762 })
763 }
764}
765
766#[cfg(feature = "api")]
767impl TryFrom<PointStructPersisted> for api::grpc::qdrant::PointStruct {
768 type Error = tonic::Status;
769
770 fn try_from(value: PointStructPersisted) -> Result<Self, Self::Error> {
771 let PointStructPersisted {
772 id,
773 vector,
774 payload,
775 } = value;
776
777 let vectors_internal = VectorStructInternal::try_from(vector).map_err(|e| {
778 tonic::Status::invalid_argument(format!("Failed to convert vectors: {e}"))
779 })?;
780
781 let vectors = api::grpc::qdrant::Vectors::from(vectors_internal);
782 let converted_payload = match payload {
783 None => HashMap::new(),
784 Some(payload) => api::conversions::json::payload_to_proto(payload),
785 };
786
787 Ok(Self {
788 id: Some(id.into()),
789 vectors: Some(vectors),
790 payload: converted_payload,
791 })
792 }
793}
794
795#[derive(Clone, PartialEq, Deserialize, Serialize)]
797#[serde(untagged, rename_all = "snake_case")]
798pub enum VectorStructPersisted {
799 Single(DenseVector),
800 MultiDense(MultiDenseVector),
801 Named(HashMap<VectorNameBuf, VectorPersisted>),
802}
803
804impl VectorStructPersisted {
805 pub fn retain_vector_names(&mut self, valid: &HashSet<VectorNameBuf>) {
808 if let VectorStructPersisted::Named(named) = self {
809 named.retain(|name, _| valid.contains(name));
810 }
811 }
812}
813
814impl std::hash::Hash for VectorStructPersisted {
815 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
816 mem::discriminant(self).hash(state);
817 match self {
818 VectorStructPersisted::Single(vec) => {
819 for v in vec {
820 OrderedFloat(*v).hash(state);
821 }
822 }
823 VectorStructPersisted::MultiDense(multi_vec) => {
824 for vec in multi_vec {
825 for v in vec {
826 OrderedFloat(*v).hash(state);
827 }
828 }
829 }
830 VectorStructPersisted::Named(map) => {
831 unordered_hash_unique(state, map.iter());
832 }
833 }
834 }
835}
836
837impl Debug for VectorStructPersisted {
838 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
839 match self {
840 VectorStructPersisted::Single(vector) => {
841 let first_elements = vector.iter().take(4).join(", ");
842 write!(f, "Single([{}, ... x {}])", first_elements, vector.len())
843 }
844 VectorStructPersisted::MultiDense(vector) => {
845 let first_vectors = vector
846 .iter()
847 .take(4)
848 .map(|v| {
849 let first_elements = v.iter().take(4).join(", ");
850 format!("[{}, ... x {}]", first_elements, v.len())
851 })
852 .join(", ");
853 write!(f, "MultiDense([{}, ... x {})", first_vectors, vector.len())
854 }
855 VectorStructPersisted::Named(vectors) => write!(f, "Named(( ")
856 .and_then(|_| {
857 for (name, vector) in vectors {
858 write!(f, "{name}: {vector:?}, ")?;
859 }
860 Ok(())
861 })
862 .and_then(|_| write!(f, "))")),
863 }
864 }
865}
866
867impl VectorStructPersisted {
868 pub fn is_empty(&self) -> bool {
870 match self {
871 VectorStructPersisted::Single(vector) => vector.is_empty(),
872 VectorStructPersisted::MultiDense(vector) => vector.is_empty(),
873 VectorStructPersisted::Named(vectors) => vectors.values().all(|v| match v {
874 VectorPersisted::Dense(vector) => vector.is_empty(),
875 VectorPersisted::Sparse(vector) => vector.indices.is_empty(),
876 VectorPersisted::MultiDense(vector) => vector.is_empty(),
877 }),
878 }
879 }
880}
881
882impl Validate for VectorStructPersisted {
883 fn validate(&self) -> Result<(), ValidationErrors> {
884 match self {
885 VectorStructPersisted::Single(_) => Ok(()),
886 VectorStructPersisted::MultiDense(v) => validate_multi_vector(v),
887 VectorStructPersisted::Named(v) => crate::common::validation::validate_iter(v.values()),
888 }
889 }
890}
891
892impl From<DenseVector> for VectorStructPersisted {
893 fn from(value: DenseVector) -> Self {
894 VectorStructPersisted::Single(value)
895 }
896}
897
898impl From<VectorStructInternal> for VectorStructPersisted {
899 fn from(value: VectorStructInternal) -> Self {
900 match value {
901 VectorStructInternal::Single(vector) => VectorStructPersisted::Single(vector),
902 VectorStructInternal::MultiDense(vector) => {
903 VectorStructPersisted::MultiDense(vector.into_multi_vectors())
904 }
905 VectorStructInternal::Named(vectors) => VectorStructPersisted::Named(
906 vectors
907 .into_iter()
908 .map(|(k, v)| (k, VectorPersisted::from(v)))
909 .collect(),
910 ),
911 }
912 }
913}
914
915#[cfg(feature = "api")]
916impl From<api::rest::VectorStructOutput> for VectorStructPersisted {
917 fn from(value: api::rest::VectorStructOutput) -> Self {
918 match value {
919 api::rest::VectorStructOutput::Single(vector) => VectorStructPersisted::Single(vector),
920 api::rest::VectorStructOutput::MultiDense(vector) => {
921 VectorStructPersisted::MultiDense(vector)
922 }
923 api::rest::VectorStructOutput::Named(vectors) => VectorStructPersisted::Named(
924 vectors
925 .into_iter()
926 .map(|(k, v)| (k, VectorPersisted::from(v)))
927 .collect(),
928 ),
929 }
930 }
931}
932
933impl TryFrom<VectorStructPersisted> for VectorStructInternal {
934 type Error = OperationError;
935 fn try_from(value: VectorStructPersisted) -> Result<Self, Self::Error> {
936 let vector_struct = match value {
937 VectorStructPersisted::Single(vector) => VectorStructInternal::Single(vector),
938 VectorStructPersisted::MultiDense(vector) => {
939 VectorStructInternal::MultiDense(MultiDenseVectorInternal::try_from(vector)?)
940 }
941 VectorStructPersisted::Named(vectors) => VectorStructInternal::Named(
942 vectors
943 .into_iter()
944 .map(|(k, v)| (k, VectorInternal::from(v)))
945 .collect(),
946 ),
947 };
948 Ok(vector_struct)
949 }
950}
951
952impl From<VectorStructPersisted> for NamedVectors<'_> {
953 fn from(value: VectorStructPersisted) -> Self {
954 match value {
955 VectorStructPersisted::Single(vector) => {
956 NamedVectors::from_pairs([(DEFAULT_VECTOR_NAME.to_owned(), vector)])
957 }
958 VectorStructPersisted::MultiDense(vector) => {
959 let mut named_vector = NamedVectors::default();
960 let multivec = MultiDenseVectorInternal::new_unchecked(vector);
961
962 named_vector.insert(
963 DEFAULT_VECTOR_NAME.to_owned(),
964 crate::segment::data_types::vectors::VectorInternal::from(multivec),
965 );
966 named_vector
967 }
968 VectorStructPersisted::Named(vectors) => {
969 let mut named_vector = NamedVectors::default();
970 for (name, vector) in vectors {
971 named_vector.insert(
972 name,
973 crate::segment::data_types::vectors::VectorInternal::from(vector),
974 );
975 }
976 named_vector
977 }
978 }
979 }
980}
981
982#[derive(Clone, PartialEq, Deserialize, Serialize)]
986#[serde(untagged, rename_all = "snake_case")]
987pub enum VectorPersisted {
988 Dense(DenseVector),
989 Sparse(crate::sparse::common::sparse_vector::SparseVector),
990 MultiDense(MultiDenseVector),
991}
992
993impl Hash for VectorPersisted {
994 fn hash<H: Hasher>(&self, state: &mut H) {
995 mem::discriminant(self).hash(state);
996 match self {
997 VectorPersisted::Dense(vec) => {
998 for v in vec {
999 OrderedFloat(*v).hash(state);
1000 }
1001 }
1002 VectorPersisted::Sparse(sparse) => {
1003 sparse.hash(state);
1004 }
1005 VectorPersisted::MultiDense(multi_vec) => {
1006 for vec in multi_vec {
1007 for v in vec {
1008 OrderedFloat(*v).hash(state);
1009 }
1010 }
1011 }
1012 }
1013 }
1014}
1015
1016impl VectorPersisted {
1017 pub fn new_sparse(indices: Vec<DimId>, values: Vec<DimWeight>) -> Self {
1018 Self::Sparse(crate::sparse::common::sparse_vector::SparseVector { indices, values })
1019 }
1020
1021 pub fn empty_sparse() -> Self {
1022 Self::new_sparse(vec![], vec![])
1023 }
1024}
1025
1026impl Debug for VectorPersisted {
1027 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1028 match self {
1029 VectorPersisted::Dense(vector) => {
1030 let first_elements = vector.iter().take(4).join(", ");
1031 write!(f, "Dense([{}, ... x {}])", first_elements, vector.len())
1032 }
1033 VectorPersisted::Sparse(vector) => {
1034 let first_elements = vector
1035 .indices
1036 .iter()
1037 .zip(vector.values.iter())
1038 .take(4)
1039 .map(|(k, v)| format!("{k}->{v}"))
1040 .join(", ");
1041 write!(
1042 f,
1043 "Sparse([{}, ... x {})",
1044 first_elements,
1045 vector.indices.len()
1046 )
1047 }
1048 VectorPersisted::MultiDense(vector) => {
1049 let first_vectors = vector
1050 .iter()
1051 .take(4)
1052 .map(|v| {
1053 let first_elements = v.iter().take(4).join(", ");
1054 format!("[{}, ... x {}]", first_elements, v.len())
1055 })
1056 .join(", ");
1057 write!(f, "MultiDense([{}, ... x {})", first_vectors, vector.len())
1058 }
1059 }
1060 }
1061}
1062
1063impl Validate for VectorPersisted {
1064 fn validate(&self) -> Result<(), ValidationErrors> {
1065 match self {
1066 VectorPersisted::Dense(_) => Ok(()),
1067 VectorPersisted::Sparse(v) => v.validate(),
1068 VectorPersisted::MultiDense(m) => validate_multi_vector(m),
1069 }
1070 }
1071}
1072
1073impl From<VectorInternal> for VectorPersisted {
1074 fn from(value: VectorInternal) -> Self {
1075 match value {
1076 VectorInternal::Dense(vector) => VectorPersisted::Dense(vector),
1077 VectorInternal::Sparse(vector) => VectorPersisted::Sparse(vector),
1078 VectorInternal::MultiDense(vector) => {
1079 VectorPersisted::MultiDense(vector.into_multi_vectors())
1080 }
1081 }
1082 }
1083}
1084
1085#[cfg(feature = "api")]
1086impl From<api::rest::VectorOutput> for VectorPersisted {
1087 fn from(value: api::rest::VectorOutput) -> Self {
1088 match value {
1089 api::rest::VectorOutput::Dense(vector) => VectorPersisted::Dense(vector),
1090 api::rest::VectorOutput::Sparse(vector) => VectorPersisted::Sparse(vector),
1091 api::rest::VectorOutput::MultiDense(vector) => VectorPersisted::MultiDense(vector),
1092 }
1093 }
1094}
1095
1096impl From<VectorPersisted> for VectorInternal {
1097 fn from(value: VectorPersisted) -> Self {
1098 match value {
1099 VectorPersisted::Dense(vector) => VectorInternal::Dense(vector),
1100 VectorPersisted::Sparse(vector) => VectorInternal::Sparse(vector),
1101 VectorPersisted::MultiDense(vector) => {
1102 VectorInternal::MultiDense(MultiDenseVectorInternal::new_unchecked(vector))
1105 }
1106 }
1107 }
1108}
1109
1110fn retain_with_index<T, F>(vec: &mut Vec<T>, mut filter: F)
1111where
1112 F: FnMut(usize, &T) -> bool,
1113{
1114 let mut index = 0;
1115
1116 vec.retain(|item| {
1117 let retain = filter(index, item);
1118 index += 1;
1119 retain
1120 });
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125 use super::*;
1126
1127 #[test]
1128 fn raw_persisted_vectors_use_compact_byte_string() {
1129 let blob: Vec<u8> = (0..4096u32).map(|i| i as u8).collect();
1131 let point = PointStructRawPersisted {
1132 id: 1.into(),
1133 vectors: vec![("dense".to_string(), blob.clone())].into(),
1134 payload: None,
1135 };
1136
1137 let encoded = serde_cbor::to_vec(&point).unwrap();
1138 assert!(
1141 encoded.len() < blob.len() + 128,
1142 "expected compact byte-string encoding, got {} bytes for a {}-byte blob",
1143 encoded.len(),
1144 blob.len(),
1145 );
1146
1147 let decoded: PointStructRawPersisted = serde_cbor::from_slice(&encoded).unwrap();
1149 assert!(decoded == point, "round-trip mismatch");
1150 }
1151
1152 fn dense(v: f32) -> VectorPersisted {
1153 VectorPersisted::Dense(vec![v])
1154 }
1155
1156 #[test]
1157 fn retain_vector_names_strips_list_batch_and_delete() {
1158 let valid: HashSet<VectorNameBuf> = ["a".to_string()].into_iter().collect();
1159
1160 let mut list =
1162 PointOperations::UpsertPoints(PointInsertOperationsInternal::PointsList(vec![
1163 PointStructPersisted {
1164 id: 1.into(),
1165 vector: VectorStructPersisted::Named(
1166 [("a".to_string(), dense(0.1)), ("b".to_string(), dense(0.2))]
1167 .into_iter()
1168 .collect(),
1169 ),
1170 payload: None,
1171 },
1172 ]));
1173 list.retain_vector_names(&valid);
1174 let PointOperations::UpsertPoints(PointInsertOperationsInternal::PointsList(points)) =
1175 &list
1176 else {
1177 unreachable!()
1178 };
1179 let VectorStructPersisted::Named(named) = &points[0].vector else {
1180 unreachable!()
1181 };
1182 assert_eq!(named.keys().cloned().collect::<Vec<_>>(), vec!["a"]);
1183
1184 let mut batch = PointOperations::UpsertPoints(PointInsertOperationsInternal::PointsBatch(
1186 BatchPersisted {
1187 ids: vec![1.into(), 2.into()],
1188 vectors: BatchVectorStructPersisted::Named(
1189 [
1190 ("a".to_string(), vec![dense(0.1), dense(0.2)]),
1191 ("b".to_string(), vec![dense(0.3), dense(0.4)]),
1192 ]
1193 .into_iter()
1194 .collect(),
1195 ),
1196 payloads: None,
1197 },
1198 ));
1199 batch.retain_vector_names(&valid);
1200 let PointOperations::UpsertPoints(PointInsertOperationsInternal::PointsBatch(batch)) =
1201 &batch
1202 else {
1203 unreachable!()
1204 };
1205 let BatchVectorStructPersisted::Named(named) = &batch.vectors else {
1206 unreachable!()
1207 };
1208 assert_eq!(named.keys().cloned().collect::<Vec<_>>(), vec!["a"]);
1209 assert_eq!(batch.ids.len(), 2);
1210 }
1211}