MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / execute_bulk_delete

Method execute_bulk_delete

nodedb/src/data/executor/handlers/bulk_dml.rs:326–482  ·  view source on GitHub ↗

Bulk delete: scan documents matching filters, delete all matches. Cascades to inverted index, secondary indexes, and graph edges. When `returning` is `None`, returns affected row count as JSON payload: `{"affected": N}`. When `returning` is `Some(spec)`, returns a `RowsPayload` with the pre-deletion documents.

(
        &mut self,
        task: &ExecutionTask,
        tid: u64,
        collection: &str,
        filter_bytes: &[u8],
        returning: Option<&ReturningSpec>,
        ollp_predicted_surrogates

Source from the content-addressed store, hash-verified

324 /// When `returning` is `None`, returns affected row count as JSON payload: `{"affected": N}`.
325 /// When `returning` is `Some(spec)`, returns a `RowsPayload` with the pre-deletion documents.
326 pub(in crate::data::executor) fn execute_bulk_delete(
327 &mut self,
328 task: &ExecutionTask,
329 tid: u64,
330 collection: &str,
331 filter_bytes: &[u8],
332 returning: Option<&ReturningSpec>,
333 ollp_predicted_surrogates: Option<&[u32]>,
334 ) -> Response {
335 debug!(core = self.core_id, %collection, has_returning = returning.is_some(), "bulk delete");
336
337 // Empty `filter_bytes` means "no WHERE clause" — match every row.
338 let filters: Vec<ScanFilter> = if filter_bytes.is_empty() {
339 Vec::new()
340 } else {
341 match zerompk::from_msgpack(filter_bytes) {
342 Ok(f) => f,
343 Err(e) => {
344 return self.response_error(
345 task,
346 ErrorCode::Internal {
347 detail: format!("deserialize filters: {e}"),
348 },
349 );
350 }
351 }
352 };
353
354 let matching_ids = match self.scan_matching_documents(tid, collection, &filters) {
355 Ok(ids) => ids,
356 Err(e) => {
357 return self.response_error(
358 task,
359 ErrorCode::Internal {
360 detail: e.to_string(),
361 },
362 );
363 }
364 };
365
366 // OLLP verification: when predicted surrogates are provided, compare
367 // against the actual matching set. On mismatch return OllpRetryRequired
368 // WITHOUT writing. The set comparison is deterministic: both sides are
369 // sorted before comparison.
370 if let Some(predicted) = ollp_predicted_surrogates {
371 let actual = ollp_actual_surrogates(&matching_ids);
372 let mut predicted_sorted: Vec<u32> = predicted.to_vec();
373 predicted_sorted.sort_unstable();
374 if actual != predicted_sorted {
375 return self.response_error(task, ErrorCode::OllpRetryRequired);
376 }
377 }
378
379 // Delete each matching document with full cascade.
380 let mut affected = 0u64;
381 let mut returned_docs: Vec<serde_json::Value> = if returning.is_some() {
382 Vec::with_capacity(matching_ids.len())
383 } else {

Callers 1

dispatch_documentMethod · 0.80

Calls 15

ollp_actual_surrogatesFunction · 0.85
inject_str_fieldFunction · 0.85
decode_documentFunction · 0.85
doc_id_to_surrogateFunction · 0.85
build_rows_payloadFunction · 0.85
encode_jsonFunction · 0.85
response_errorMethod · 0.80
to_stringMethod · 0.80
remove_node_edgesMethod · 0.80
csr_partition_mutMethod · 0.80

Tested by

no test coverage detected