use std::marker::PhantomData;
use std::time::Duration;
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_json::Value as Json;
use ulid::Ulid;
use crate::collection::Collection;
use crate::document::Document;
use crate::error::TalaDbError;
use crate::json::{document_to_json, fields_from_json, filter_from_json, update_from_json};
use crate::vector::VectorMetric;
use crate::watch::WatchHandle;
use crate::{Database, HnswOptions};
impl Database {
pub fn typed<T: Serialize + DeserializeOwned>(
&self,
name: &str,
) -> Result<TypedCollection<T>, TalaDbError> {
Ok(TypedCollection {
inner: self.collection(name)?,
_marker: PhantomData,
})
}
}
pub struct TypedCollection<T> {
inner: Collection,
_marker: PhantomData<fn() -> T>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Scored<T> {
pub document: T,
pub score: f32,
}
impl<T: Serialize + DeserializeOwned> TypedCollection<T> {
pub fn raw(&self) -> &Collection {
&self.inner
}
pub fn insert(&self, document: &T) -> Result<Ulid, TalaDbError> {
self.inner.insert(encode(document)?)
}
pub fn insert_many(&self, documents: &[T]) -> Result<Vec<Ulid>, TalaDbError> {
self.inner
.insert_many(documents.iter().map(encode).collect::<Result<_, _>>()?)
}
pub fn find(&self, filter: Json) -> Result<Vec<T>, TalaDbError> {
self.inner
.find(filter_from_json(&filter)?)?
.iter()
.map(decode)
.collect()
}
pub fn find_one(&self, filter: Json) -> Result<Option<T>, TalaDbError> {
self.inner
.find_one(filter_from_json(&filter)?)?
.as_ref()
.map(decode)
.transpose()
}
pub fn find_by_id(&self, id: Ulid) -> Result<Option<T>, TalaDbError> {
self.inner.find_by_id(id)?.as_ref().map(decode).transpose()
}
pub fn count(&self, filter: Json) -> Result<u64, TalaDbError> {
self.inner.count(filter_from_json(&filter)?)
}
pub fn update_one(&self, filter: Json, update: Json) -> Result<bool, TalaDbError> {
self.inner
.update_one(filter_from_json(&filter)?, update_from_json(&update)?)
}
pub fn update_many(&self, filter: Json, update: Json) -> Result<u64, TalaDbError> {
self.inner
.update_many(filter_from_json(&filter)?, update_from_json(&update)?)
}
pub fn delete_one(&self, filter: Json) -> Result<bool, TalaDbError> {
self.inner.delete_one(filter_from_json(&filter)?)
}
pub fn delete_many(&self, filter: Json) -> Result<u64, TalaDbError> {
self.inner.delete_many(filter_from_json(&filter)?)
}
pub fn create_index(&self, field: &str) -> Result<(), TalaDbError> {
self.inner.create_index(field)
}
pub fn create_compound_index(&self, fields: &[&str]) -> Result<(), TalaDbError> {
self.inner.create_compound_index(fields)
}
pub fn create_fts_index(&self, field: &str) -> Result<(), TalaDbError> {
self.inner.create_fts_index(field)
}
pub fn create_vector_index(
&self,
field: &str,
dimensions: usize,
metric: Option<VectorMetric>,
hnsw: Option<HnswOptions>,
) -> Result<(), TalaDbError> {
self.inner
.create_vector_index(field, dimensions, metric, hnsw)
}
pub fn find_nearest(
&self,
field: &str,
query: &[f32],
top_k: usize,
filter: Option<Json>,
) -> Result<Vec<Scored<T>>, TalaDbError> {
let filter = filter.map(|f| filter_from_json(&f)).transpose()?;
self.inner
.find_nearest(field, query, top_k, filter)?
.iter()
.map(|hit| {
Ok(Scored {
document: decode(&hit.document)?,
score: hit.score,
})
})
.collect()
}
pub fn search_text(
&self,
field: &str,
query: &str,
top_k: usize,
) -> Result<Vec<Scored<T>>, TalaDbError> {
self.inner
.search_text(field, query, top_k)?
.iter()
.map(|hit| {
Ok(Scored {
document: decode(&hit.document)?,
score: hit.score,
})
})
.collect()
}
pub fn watch(&self, filter: Json) -> Result<TypedWatch<T>, TalaDbError> {
Ok(TypedWatch {
inner: self.inner.watch(filter_from_json(&filter)?),
_marker: PhantomData,
})
}
}
pub struct TypedWatch<T> {
inner: WatchHandle,
_marker: PhantomData<fn() -> T>,
}
impl<T: DeserializeOwned> TypedWatch<T> {
pub fn next(&self) -> Result<Vec<T>, TalaDbError> {
self.inner.next()?.iter().map(decode).collect()
}
pub fn next_timeout(&self, timeout: Duration) -> Result<Option<Vec<T>>, TalaDbError> {
self.inner
.next_timeout(timeout)?
.map(|docs| docs.iter().map(decode).collect())
.transpose()
}
pub fn try_next(&self) -> Result<Option<Vec<T>>, TalaDbError> {
self.inner
.try_next()?
.map(|docs| docs.iter().map(decode).collect())
.transpose()
}
}
fn encode<T: Serialize>(document: &T) -> Result<Vec<(String, crate::Value)>, TalaDbError> {
let mut json =
serde_json::to_value(document).map_err(|e| TalaDbError::Serialization(e.to_string()))?;
if let Some(obj) = json.as_object_mut()
&& obj.get("_id").is_some_and(Json::is_null)
{
obj.remove("_id");
}
fields_from_json(&json)
}
fn decode<T: DeserializeOwned>(doc: &Document) -> Result<T, TalaDbError> {
serde_json::from_value(document_to_json(doc))
.map_err(|e| TalaDbError::Serialization(e.to_string()))
}