use std::collections::HashMap;
use std::sync::Arc;
use nodedb_types::Namespace;
use nodedb_types::value::Value;
use crate::error::LiteError;
use crate::index::IndexCatalog;
use crate::index::IndexEngine;
use crate::index::durable::RowImage;
use crate::index::key::{doc_id_of_key, key_of_doc_id};
use crate::storage::engine::{StorageEngine, WriteOp};
use super::crud::decode_tuple;
use super::engine::{CollectionState, StrictEngine};
pub(crate) fn row_object(state: &CollectionState, values: &[Value]) -> Value {
Value::Object(
state
.schema
.columns
.iter()
.zip(values)
.map(|(col, v)| (col.name.clone(), v.clone()))
.collect::<HashMap<_, _>>(),
)
}
impl<S: StorageEngine> StrictEngine<S> {
pub fn set_index_catalog(&self, catalog: Arc<IndexCatalog>) {
let _ = self.indexes.set(catalog);
}
fn doc_id_of(collection: &str, key: &[u8]) -> Option<String> {
key.strip_prefix(collection.as_bytes())
.and_then(|rest| rest.strip_prefix(b":"))
.map(doc_id_of_key)
}
pub(crate) async fn row_by_doc_id(
&self,
collection: &str,
doc_id: &str,
) -> Result<Option<Vec<Value>>, LiteError> {
let state = self.get_state(collection)?;
let Some(suffix) = key_of_doc_id(doc_id) else {
return Ok(None);
};
let mut key = collection.as_bytes().to_vec();
key.push(b':');
key.extend_from_slice(&suffix);
match self.storage.get(Namespace::Strict, &key).await? {
Some(bytes) => decode_tuple(&state, &bytes).map(Some),
None => Ok(None),
}
}
pub(crate) async fn rows_with_ids(
&self,
collection: &str,
) -> Result<Vec<(String, Value)>, LiteError> {
let state = self.get_state(collection)?;
let prefix = format!("{collection}:");
let mut rows = Vec::new();
for (key, bytes) in self
.storage
.scan_prefix(Namespace::Strict, prefix.as_bytes())
.await?
{
if let Some(id) = Self::doc_id_of(collection, &key) {
let values = decode_tuple(&state, &bytes)?;
rows.push((id, row_object(&state, &values)));
}
}
Ok(rows)
}
pub(crate) async fn commit(
&self,
collection: &str,
ops: Vec<WriteOp>,
) -> Result<(), LiteError> {
let Some(catalog) = self.indexes.get() else {
return self.storage.batch_write(&ops).await;
};
let state = self.get_state(collection)?;
let mut images = Vec::new();
for op in &ops {
let (key, row) = match op {
WriteOp::Put { ns, key, value } if *ns == Namespace::Strict => {
let values = decode_tuple(&state, value)?;
(key, Some(row_object(&state, &values)))
}
WriteOp::Delete { ns, key } if *ns == Namespace::Strict => (key, None),
_ => continue,
};
if let Some(doc_id) = Self::doc_id_of(collection, key) {
images.push(RowImage {
collection: collection.to_string(),
doc_id,
row,
});
}
}
catalog
.commit_rows(
&*self.storage,
IndexEngine::Strict,
ops,
images,
|coll, id| async move {
let state = self.get_state(&coll)?;
Ok(self
.row_by_doc_id(&coll, &id)
.await?
.map(|values| row_object(&state, &values)))
},
)
.await
}
}