use crate::address::Address;
use crate::data_store::Datastore;
use crate::guardian::error::{GuardianError, Result};
use crate::log::identity::Identity;
use crate::p2p::EventBus;
use crate::p2p::network::client::IrohClient;
use crate::stores::base_store::BaseStore;
use crate::stores::document_store::index::DocumentIndex;
use crate::stores::operation::{self, Operation};
use crate::traits::{
CreateDocumentDBOptions, DocumentStoreGetOptions, NewStoreOptions, Store, TracerWrapper,
};
use serde_json::{Map, Value};
use std::sync::Arc;
use tracing::{Span, instrument, warn};
pub mod index;
pub type Document = Value;
pub struct GuardianDBDocumentStore {
base_store: Arc<BaseStore>,
doc_opts: CreateDocumentDBOptions,
doc_index: Arc<DocumentIndex>,
cached_address: Arc<dyn Address + Send + Sync>,
span: Span,
}
#[async_trait::async_trait]
impl Store for GuardianDBDocumentStore {
type Error = GuardianError;
#[allow(deprecated)]
fn events(&self) -> &dyn crate::events::EmitterInterface {
self.base_store.events()
}
async fn close(&self) -> std::result::Result<(), Self::Error> {
self.base_store.close().await
}
fn address(&self) -> &dyn Address {
self.cached_address.as_ref()
}
fn index(&self) -> Box<dyn crate::traits::StoreIndex<Error = GuardianError> + Send + Sync> {
Box::new((*self.doc_index).clone())
}
fn store_type(&self) -> &str {
"document"
}
fn replication_status(&self) -> crate::stores::replicator::replication_info::ReplicationInfo {
self.base_store.replication_status()
}
fn cache(&self) -> Arc<dyn Datastore> {
self.base_store.cache()
}
async fn drop(&self) -> std::result::Result<(), Self::Error> {
Ok(())
}
async fn load(&self, amount: usize) -> std::result::Result<(), Self::Error> {
self.base_store.load(Some(amount as isize)).await
}
async fn sync(
&self,
heads: Vec<crate::log::entry::Entry>,
) -> std::result::Result<(), Self::Error> {
self.base_store.sync(heads).await
}
async fn load_more_from(&self, _amount: u64, entries: Vec<crate::log::entry::Entry>) {
let _ = self.base_store.load_more_from(entries);
}
async fn load_from_snapshot(&self) -> std::result::Result<(), Self::Error> {
Ok(())
}
fn op_log(&self) -> Arc<parking_lot::RwLock<crate::log::Log>> {
self.base_store.op_log()
}
fn client(&self) -> Arc<IrohClient> {
self.base_store.client()
}
fn db_name(&self) -> &str {
self.base_store.db_name()
}
fn identity(&self) -> &Identity {
self.base_store.identity()
}
fn access_controller(&self) -> &dyn crate::access_control::traits::AccessController {
self.base_store.access_controller()
}
async fn add_operation(
&self,
op: Operation,
on_progress_callback: Option<tokio::sync::mpsc::Sender<crate::log::entry::Entry>>,
) -> std::result::Result<crate::log::entry::Entry, Self::Error> {
self.base_store
.add_operation(op, on_progress_callback)
.await
}
fn span(&self) -> Arc<tracing::Span> {
Arc::new(self.span.clone())
}
fn tracer(&self) -> Arc<TracerWrapper> {
self.base_store.tracer()
}
fn event_bus(&self) -> Arc<EventBus> {
Arc::new(EventBus::new())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl GuardianDBDocumentStore {
pub fn basestore(&self) -> &BaseStore {
&self.base_store
}
pub fn span(&self) -> &Span {
&self.span
}
#[instrument(level = "debug", skip(client, identity, options))]
pub async fn new(
client: Arc<IrohClient>,
identity: Arc<Identity>,
addr: Arc<dyn Address>,
mut options: NewStoreOptions,
) -> Result<Self> {
if options.store_specific_opts.is_none() {
let default_opts = default_store_opts_for_map("_id");
options.store_specific_opts = Some(Box::new(default_opts));
}
let specific_opts_box = options.store_specific_opts.take().ok_or_else(|| {
GuardianError::InvalidArgument("StoreSpecificOpts is required".to_string())
})?;
let doc_opts_box = specific_opts_box
.downcast::<CreateDocumentDBOptions>()
.map_err(|_| {
GuardianError::InvalidArgument(
"Tipo inválido fornecido para opts.StoreSpecificOpts".to_string(),
)
})?;
let doc_opts = Arc::new(*doc_opts_box);
let doc_index = Arc::new(DocumentIndex::new(doc_opts.clone()));
let doc_index_for_base = doc_index.clone();
options.index = Some(Box::new(move |_data: &[u8]| {
Box::new((*doc_index_for_base).clone())
}));
let base_store = BaseStore::new(client, identity, addr, Some(options))
.await
.map_err(|e| {
GuardianError::Store(format!(
"Não foi possível inicializar a document store: {}",
e
))
})?;
let cached_address = base_store.address();
let span = tracing::info_span!("document_store", address = %cached_address.to_string());
let store = GuardianDBDocumentStore {
base_store,
doc_opts: (*doc_opts).clone(),
doc_index,
cached_address,
span,
};
Ok(store)
}
#[instrument(level = "debug", skip(self, opts))]
pub async fn get(
&self,
key: &str,
opts: Option<DocumentStoreGetOptions>,
) -> Result<Vec<Document>> {
let _entered = self.span.enter();
let opts = opts.unwrap_or_default();
let has_multiple_terms = key.contains(' ');
let mut key_for_search = key.to_string();
if has_multiple_terms {
key_for_search = key_for_search.replace('.', " ");
}
if opts.case_insensitive {
key_for_search = key_for_search.to_lowercase();
}
let doc_index = &self.doc_index;
let mut documents: Vec<Document> = Vec::new();
for index_key in doc_index.keys() {
let mut index_key_for_search = index_key.clone();
if opts.case_insensitive {
index_key_for_search = index_key_for_search.to_lowercase();
if has_multiple_terms {
index_key_for_search = index_key_for_search.replace('.', " ");
}
}
let matches = if opts.partial_matches {
index_key_for_search.contains(&key_for_search)
} else {
index_key_for_search == key_for_search
};
if !matches {
continue;
}
if let Some(value_bytes) = doc_index.get_bytes(&index_key) {
let doc: Document = serde_json::from_slice(&value_bytes).map_err(|e| {
GuardianError::Serialization(format!(
"Impossível desserializar o valor para a chave {}: {}",
index_key, e
))
})?;
documents.push(doc);
} else {
eprintln!(
"Aviso: chave '{}' encontrada no conjunto de chaves do índice, mas sem valor correspondente.",
index_key
);
}
}
Ok(documents)
}
#[instrument(level = "debug", skip(self, document))]
pub async fn put(&mut self, document: Document) -> Result<Operation> {
let _entered = self.span.enter();
let key = (self.doc_opts.key_extractor)(&document)?;
let data = (self.doc_opts.marshal)(&document)?;
let op = Operation::new(Some(key.clone()), "PUT".to_string(), Some(data.clone()));
let entry = self.base_store.add_operation(op, None).await?;
let parsed_op = operation::parse_operation(entry)?;
if let Err(e) = self.base_store.update_index() {
warn!(
"Warning: Failed to update index after PUT operation: {:?}",
e
);
}
if let Err(e) = self.doc_index.put(key, data) {
warn!("Warning: Failed to update local document index: {:?}", e);
}
Ok(parsed_op)
}
#[instrument(level = "debug", skip(self))]
pub async fn delete(&mut self, document_id: &str) -> Result<Operation> {
let _entered = self.span.enter();
let doc_index = &self.doc_index;
if doc_index.get_bytes(document_id).is_none() {
return Err(GuardianError::NotFound(format!(
"Nenhuma entrada com a chave '{}' na base de dados",
document_id
)));
}
let op = Operation::new(Some(document_id.to_string()), "DEL".to_string(), None);
let entry = self.base_store.add_operation(op, None).await?;
let parsed_op = operation::parse_operation(entry)?;
if let Err(e) = self.base_store.update_index() {
warn!(
"Warning: Failed to update index after DELETE operation: {:?}",
e
);
}
if let Err(e) = self.doc_index.remove(document_id) {
warn!(
"Warning: Failed to remove from local document index: {:?}",
e
);
}
Ok(parsed_op)
}
#[instrument(level = "debug", skip(self, documents))]
pub async fn put_batch(&mut self, documents: Vec<Document>) -> Result<Vec<Operation>> {
if documents.is_empty() {
return Err(GuardianError::InvalidArgument(
"Nada para adicionar à store".to_string(),
));
}
let mut operations = Vec::new();
for doc in documents {
let op = self.put(doc).await?;
operations.push(op);
}
Ok(operations)
}
#[instrument(level = "debug", skip(self, documents))]
pub async fn put_all(&mut self, documents: Vec<Document>) -> Result<Operation> {
if documents.is_empty() {
return Err(GuardianError::InvalidArgument(
"Nada para adicionar à store".to_string(),
));
}
let mut to_add: Vec<(String, Vec<u8>)> = Vec::new();
for doc in documents {
let key = (self.doc_opts.key_extractor)(&doc).map_err(|_| {
GuardianError::InvalidArgument(
"Um dos documentos fornecidos não possui chave de índice".to_string(),
)
})?;
let data = (self.doc_opts.marshal)(&doc).map_err(|_| {
GuardianError::Serialization(
"Não foi possível serializar um dos documentos fornecidos".to_string(),
)
})?;
to_add.push((key, data));
}
let op = Operation::new_with_documents(None, "PUTALL".to_string(), to_add.clone());
let entry = self.base_store.add_operation(op, None).await?;
let parsed_op = operation::parse_operation(entry)?;
if let Err(e) = self.base_store.update_index() {
warn!(
"Warning: Failed to update index after PUTALL operation: {:?}",
e
);
}
for (key, data) in to_add {
if let Err(e) = self.doc_index.put(key, data) {
warn!("Warning: Failed to update local document index: {:?}", e);
}
}
Ok(parsed_op)
}
#[instrument(level = "debug", skip(self, filter))]
pub fn query<F>(&self, mut filter: F) -> Result<Vec<Document>>
where
F: FnMut(&Document) -> Result<bool>,
{
let doc_index = &self.doc_index;
let mut results: Vec<Document> = Vec::new();
for index_key in doc_index.keys() {
if let Some(doc_bytes) = doc_index.get_bytes(&index_key) {
let doc: Document = serde_json::from_slice(&doc_bytes).map_err(|e| {
GuardianError::Serialization(format!(
"Não foi possível desserializar o documento: {}",
e
))
})?;
if filter(&doc)? {
results.push(doc);
}
}
}
Ok(results)
}
pub fn store_type(&self) -> &'static str {
"document"
}
}
pub fn map_key_extractor(key_field: String) -> impl Fn(&Document) -> Result<String> {
move |doc: &Document| {
let obj = doc.as_object().ok_or_else(|| {
GuardianError::InvalidArgument(
"A entrada precisa ser um objeto JSON (map[string]interface{{}})".to_string(),
)
})?;
let value = obj.get(&key_field).ok_or_else(|| {
GuardianError::NotFound(format!(
"Faltando valor para o campo `{}` na entrada",
key_field
))
})?;
let key = value.as_str().ok_or_else(|| {
GuardianError::InvalidArgument(format!(
"O valor para o campo `{}` não é uma string",
key_field
))
})?;
if key.is_empty() {
return Err(GuardianError::InvalidArgument(format!(
"O campo `{}` não pode ser uma string vazia",
key_field
)));
}
Ok(key.to_string())
}
}
pub fn default_store_opts_for_map(key_field: &str) -> CreateDocumentDBOptions {
CreateDocumentDBOptions {
marshal: Arc::new(|doc: &Document| serde_json::to_vec(doc).map_err(GuardianError::from)),
unmarshal: Arc::new(|bytes: &[u8]| {
serde_json::from_slice(bytes).map_err(GuardianError::from)
}),
key_extractor: Arc::new(map_key_extractor(key_field.to_string())),
item_factory: Arc::new(|| Value::Object(Map::new())),
}
}