qdrant_edge/edge/update_only/
preview.rs1use crate::common::types::PointOffsetType;
11use crate::common::universal_io::UniversalRead;
12use rayon::ThreadPool;
13use crate::segment::common::operation_error::OperationResult;
14use crate::segment::data_types::fully_qualified_point::FullyQualifiedPoint;
15use crate::segment::types::{PointIdType, SeqNumberType};
16use crate::shard::operations::CollectionUpdateOperations;
17use uuid::Uuid;
18
19use crate::edge::update_only::UpdateOnlyEdgeShard;
20use crate::edge::update_only::apply::{locate_points, read_stored_points};
21use crate::edge::update_only::batch::UpdateBatchPlan;
22use crate::edge::update_only::holder::UpdateOnlySegmentHolder;
23
24pub struct UpdateBatchPreview {
26 pub points: Vec<PointPreview>,
27}
28
29pub struct PointPreview {
31 pub id: PointIdType,
32 pub current: Option<PointCopy>,
34 pub slots: Vec<(Uuid, PointOffsetType)>,
37 pub action: PointAction,
38}
39
40pub struct PointCopy {
43 pub segment: Uuid,
44 pub internal_id: PointOffsetType,
45 pub version: SeqNumberType,
46}
47
48pub enum PointAction {
50 Store(Box<FullyQualifiedPoint>),
54 Delete,
56 Skip,
59 Missing,
62}
63
64pub(super) fn resolve_batch<S: UniversalRead + 'static>(
70 segments: &UpdateOnlySegmentHolder<S>,
71 plan: UpdateBatchPlan,
72 pool: &ThreadPool,
73) -> OperationResult<Vec<PointPreview>> {
74 let locations = locate_points(segments, &plan, pool)?;
75 let mut stored = read_stored_points(segments, &plan, &locations, pool)?;
76
77 let mut points = Vec::with_capacity(plan.len());
78 for (id, updates) in plan.into_point_updates() {
79 let location = locations.get(&id);
80 let current = location.map(|location| PointCopy {
81 segment: location.newest.segment,
82 internal_id: location.newest.internal_id,
83 version: location.newest.version,
84 });
85 let slots = location
86 .map(|location| location.slots.clone())
87 .unwrap_or_default();
88
89 let already_applied = current
92 .as_ref()
93 .is_some_and(|current| current.version >= updates.version());
94
95 let action = if already_applied {
96 PointAction::Skip
97 } else {
98 match updates.materialize(id, stored.remove(&id))? {
99 Some(point) => PointAction::Store(Box::new(point)),
100 None if current.is_some() => PointAction::Delete,
101 None => PointAction::Missing,
102 }
103 };
104
105 points.push(PointPreview {
106 id,
107 current,
108 slots,
109 action,
110 });
111 }
112
113 Ok(points)
114}
115
116impl<S: UniversalRead + 'static> UpdateOnlyEdgeShard<S> {
117 pub fn preview_batch(
124 &self,
125 operations: impl IntoIterator<Item = (SeqNumberType, CollectionUpdateOperations)>,
126 ) -> OperationResult<UpdateBatchPreview> {
127 let plan = UpdateBatchPlan::build(operations)?;
128 if plan.is_empty() {
129 return Ok(UpdateBatchPreview { points: Vec::new() });
130 }
131
132 let segments = self.segments.read();
133 let points = resolve_batch(&segments, plan, &self.pool)?;
134 Ok(UpdateBatchPreview { points })
135 }
136}