use crate::db::{exec_single_row, get_neo4j_graph, RedisOps};
use crate::types::DynError;
use async_trait::async_trait;
use core::fmt;
use neo4rs::Query;
use std::fmt::Debug;
pub trait CollectionId {
fn to_string_id(self) -> String;
}
impl CollectionId for &str {
fn to_string_id(self) -> String {
String::from(self)
}
}
impl CollectionId for &[&str] {
fn to_string_id(self) -> String {
self.join(":")
}
}
#[async_trait]
pub trait Collection<T>
where
Self: RedisOps + Clone + Debug + Default,
T: CollectionId + fmt::Debug + Sync + Send + Copy,
{
async fn get_by_ids(ids: &[T]) -> Result<Vec<Option<Self>>, DynError> {
let key_parts_list: Vec<String> = ids.iter().map(|id| id.to_string_id()).collect();
let keys_refs: Vec<Vec<&str>> = key_parts_list.iter().map(|id| vec![id.as_str()]).collect();
let keys: Vec<&[&str]> = keys_refs.iter().map(|arr| &arr[..]).collect();
let mut collection = Self::get_from_index(keys).await?;
let mut missing_ids: Vec<(usize, T)> = Vec::new();
for (i, details) in collection.iter().enumerate() {
if details.is_none() {
missing_ids.push((i, ids[i]));
}
}
if !missing_ids.is_empty() {
let flat_missing_ids: Vec<T> = missing_ids.iter().map(|&(_, id)| id).collect();
let fetched_details = Self::get_from_graph(&flat_missing_ids).await?;
if !fetched_details.is_empty() {
for (i, (original_index, _)) in missing_ids.iter().enumerate() {
collection[*original_index].clone_from(&fetched_details[i]);
}
Self::put_to_index(&flat_missing_ids, fetched_details).await?;
}
}
Ok(collection)
}
async fn get_from_graph(ids: &[T]) -> Result<Vec<Option<Self>>, DynError> {
let mut result;
{
let graph = get_neo4j_graph()?;
let query = Self::collection_details_graph_query(ids);
let graph = graph.lock().await;
result = graph.execute(query).await?;
}
let mut records = Vec::with_capacity(ids.len());
while let Some(row) = result.next().await? {
let record: Option<Self> = row.get("record").ok();
records.push(record);
}
Ok(records)
}
async fn get_from_index(keys: Vec<&[&str]>) -> Result<Vec<Option<Self>>, DynError> {
Self::try_from_index_multiple_json(&keys).await
}
async fn put_to_index(ids: &[T], records: Vec<Option<Self>>) -> Result<(), DynError> {
let mut found_records = Vec::with_capacity(records.len());
let mut found_record_ids = Vec::with_capacity(records.len());
for (detail, id) in records.iter().zip(ids.iter()) {
if let Some(value) = detail {
found_records.push(Some(value.clone()));
found_record_ids.push(*id);
}
}
let key_parts_list: Vec<String> = found_record_ids
.iter()
.map(|id| id.to_string_id())
.collect();
let keys_refs: Vec<Vec<&str>> = key_parts_list.iter().map(|id| vec![id.as_str()]).collect();
let keys: Vec<&[&str]> = keys_refs.iter().map(|arr| &arr[..]).collect();
Self::put_multiple_json_indexes(&keys, found_records).await?;
Self::extend_on_index_miss(&records).await?;
Ok(())
}
async fn put_to_graph(&self) -> Result<(), DynError> {
exec_single_row(self.put_graph_query()?).await
}
async fn reindex(collection_ids: &[T]) -> Result<(), DynError> {
match Self::get_from_graph(collection_ids).await {
Ok(collection_details_list) => {
if !collection_details_list.is_empty() {
Self::put_to_index(collection_ids, collection_details_list).await?;
}
}
Err(e) => tracing::error!("Error: Could not find any element of the collection: {}", e),
}
Ok(())
}
fn collection_details_graph_query(id_list: &[T]) -> Query;
fn put_graph_query(&self) -> Result<Query, DynError>;
async fn extend_on_index_miss(elements: &[std::option::Option<Self>]) -> Result<(), DynError>;
}