use std::collections::BTreeSet;
use crate::data::executor::core_loop::CoreLoop;
use crate::engine::document::store::{IndexPath, extract_index_values};
use crate::engine::sparse::btree_versioned::{VersionedIndexEntry, VersionedPut};
pub(in crate::data::executor) struct BitemporalUpdateReindex<'a> {
pub database_id: u64,
pub tid: u64,
pub collection: &'a str,
pub doc_id: &'a str,
pub sys_from_ms: i64,
pub valid_from_ms: i64,
pub valid_until_ms: i64,
pub new_body: &'a [u8],
pub index_paths: &'a [IndexPath],
pub old_doc: Option<&'a serde_json::Value>,
pub new_doc: &'a serde_json::Value,
pub wal_lsn: Option<crate::types::Lsn>,
}
pub(in crate::data::executor) struct NonbitemporalUpdateReindex<'a> {
pub database_id: u64,
pub tid: u64,
pub collection: &'a str,
pub doc_id: &'a str,
pub new_body: &'a [u8],
pub index_paths: &'a [IndexPath],
pub old_doc: &'a serde_json::Value,
pub new_doc: &'a serde_json::Value,
pub wal_lsn: Option<crate::types::Lsn>,
}
impl CoreLoop {
fn indexed_values_for_path(doc: &serde_json::Value, path: &IndexPath) -> BTreeSet<String> {
if let Some(ref pred) = path.predicate
&& !pred.evaluate_json(doc)
{
return BTreeSet::new();
}
extract_index_values(doc, &path.path, path.is_array)
.into_iter()
.map(|v| {
if path.case_insensitive {
v.to_lowercase()
} else {
v
}
})
.collect()
}
pub(in crate::data::executor) fn bitemporal_update_reindex(
&mut self,
p: BitemporalUpdateReindex<'_>,
) -> crate::Result<()> {
let txn = self.sparse.begin_write()?;
self.sparse.versioned_put_in_txn(
&txn,
VersionedPut {
database_id: p.database_id,
tenant: p.tid,
coll: p.collection,
doc_id: p.doc_id,
sys_from_ms: p.sys_from_ms,
valid_from_ms: p.valid_from_ms,
valid_until_ms: p.valid_until_ms,
body: p.new_body,
},
)?;
let mut touched_values: Vec<(String, String)> = Vec::new();
for path in p.index_paths {
let new_values = Self::indexed_values_for_path(p.new_doc, path);
let old_values = p
.old_doc
.map(|d| Self::indexed_values_for_path(d, path))
.unwrap_or_default();
for value in old_values.difference(&new_values) {
self.sparse.versioned_index_tombstone_in_txn(
&txn,
VersionedIndexEntry {
database_id: p.database_id,
tenant: p.tid,
coll: p.collection,
field: &path.path,
value,
doc_id: p.doc_id,
sys_from_ms: p.sys_from_ms,
},
)?;
}
for value in &new_values {
self.sparse.versioned_index_put_in_txn(
&txn,
VersionedIndexEntry {
database_id: p.database_id,
tenant: p.tid,
coll: p.collection,
field: &path.path,
value,
doc_id: p.doc_id,
sys_from_ms: p.sys_from_ms,
},
)?;
}
for value in old_values.union(&new_values) {
touched_values.push((path.path.clone(), value.clone()));
}
}
txn.commit().map_err(|e| crate::Error::Storage {
engine: "sparse".into(),
detail: format!("bitemporal update reindex commit: {e}"),
})?;
if let Some(lsn) = p.wal_lsn {
self.note_index_write_values(
nodedb_types::DatabaseId::new(p.database_id),
crate::types::TenantId::new(p.tid),
p.collection,
&touched_values,
lsn,
);
}
Ok(())
}
pub(in crate::data::executor) fn nonbitemporal_update_reindex(
&mut self,
p: NonbitemporalUpdateReindex<'_>,
) -> crate::Result<()> {
let txn = self.sparse.begin_write()?;
self.sparse.put_in_txn(
&txn,
p.database_id,
p.tid,
p.collection,
p.doc_id,
p.new_body,
)?;
let (added, removed) = if !p.index_paths.is_empty() {
self.apply_secondary_indexes_in_txn(
&txn,
crate::data::executor::core_loop::maintenance::SecondaryIndexInputs {
database_id: p.database_id,
tid: p.tid,
collection: p.collection,
old_doc: Some(p.old_doc),
new_doc: p.new_doc,
doc_id: p.doc_id,
index_paths: p.index_paths,
},
)
} else {
(Vec::new(), Vec::new())
};
txn.commit().map_err(|e| crate::Error::Storage {
engine: "sparse".into(),
detail: format!("nonbitemporal update reindex commit: {e}"),
})?;
if let Some(lsn) = p.wal_lsn {
let mut tuples = added;
tuples.extend(removed);
self.note_index_write_values(
nodedb_types::DatabaseId::new(p.database_id),
crate::types::TenantId::new(p.tid),
p.collection,
&tuples,
lsn,
);
}
Ok(())
}
}