use crate::common::types::PointOffsetType;
use crate::common::universal_io::UniversalRead;
use rayon::ThreadPool;
use crate::segment::common::operation_error::OperationResult;
use crate::segment::data_types::fully_qualified_point::FullyQualifiedPoint;
use crate::segment::types::{PointIdType, SeqNumberType};
use crate::shard::operations::CollectionUpdateOperations;
use uuid::Uuid;
use crate::edge::update_only::UpdateOnlyEdgeShard;
use crate::edge::update_only::apply::{locate_points, read_stored_points};
use crate::edge::update_only::batch::UpdateBatchPlan;
use crate::edge::update_only::holder::UpdateOnlySegmentHolder;
pub struct UpdateBatchPreview {
pub points: Vec<PointPreview>,
}
pub struct PointPreview {
pub id: PointIdType,
pub current: Option<PointCopy>,
pub slots: Vec<(Uuid, PointOffsetType)>,
pub action: PointAction,
}
pub struct PointCopy {
pub segment: Uuid,
pub internal_id: PointOffsetType,
pub version: SeqNumberType,
}
pub enum PointAction {
Store(Box<FullyQualifiedPoint>),
Delete,
Skip,
Missing,
}
pub(super) fn resolve_batch<S: UniversalRead + 'static>(
segments: &UpdateOnlySegmentHolder<S>,
plan: UpdateBatchPlan,
pool: &ThreadPool,
) -> OperationResult<Vec<PointPreview>> {
let locations = locate_points(segments, &plan, pool)?;
let mut stored = read_stored_points(segments, &plan, &locations, pool)?;
let mut points = Vec::with_capacity(plan.len());
for (id, updates) in plan.into_point_updates() {
let location = locations.get(&id);
let current = location.map(|location| PointCopy {
segment: location.newest.segment,
internal_id: location.newest.internal_id,
version: location.newest.version,
});
let slots = location
.map(|location| location.slots.clone())
.unwrap_or_default();
let already_applied = current
.as_ref()
.is_some_and(|current| current.version >= updates.version());
let action = if already_applied {
PointAction::Skip
} else {
match updates.materialize(id, stored.remove(&id))? {
Some(point) => PointAction::Store(Box::new(point)),
None if current.is_some() => PointAction::Delete,
None => PointAction::Missing,
}
};
points.push(PointPreview {
id,
current,
slots,
action,
});
}
Ok(points)
}
impl<S: UniversalRead + 'static> UpdateOnlyEdgeShard<S> {
pub fn preview_batch(
&self,
operations: impl IntoIterator<Item = (SeqNumberType, CollectionUpdateOperations)>,
) -> OperationResult<UpdateBatchPreview> {
let plan = UpdateBatchPlan::build(operations)?;
if plan.is_empty() {
return Ok(UpdateBatchPreview { points: Vec::new() });
}
let segments = self.segments.read();
let points = resolve_batch(&segments, plan, &self.pool)?;
Ok(UpdateBatchPreview { points })
}
}