mod backend;
mod document;
mod error;
mod memory;
#[cfg(feature = "meilisearch")]
mod meilisearch;
mod query;
mod token;
pub use backend::{Backend, IndexDoc};
pub use document::Document;
pub use error::{Result, SearchError};
pub use memory::MemoryBackend;
#[cfg(feature = "meilisearch")]
pub use meilisearch::MeilisearchBackend;
pub use query::{Filter, Hit, Query, DEFAULT_LIMIT};
pub trait Searchable {
fn index_name() -> &'static str
where
Self: Sized;
fn search_id(&self) -> String;
fn to_document(&self) -> Document;
}
pub struct SearchEngine {
backend: Box<dyn Backend>,
}
impl SearchEngine {
pub fn new(backend: impl Backend + 'static) -> Self {
Self {
backend: Box::new(backend),
}
}
pub async fn index<T: Searchable>(&self, item: &T) -> Result<()> {
self.backend
.upsert(
T::index_name(),
vec![IndexDoc {
id: item.search_id(),
document: item.to_document(),
}],
)
.await
}
pub async fn index_many<T: Searchable>(&self, items: &[T]) -> Result<()> {
if items.is_empty() {
return Ok(());
}
let docs = items
.iter()
.map(|i| IndexDoc {
id: i.search_id(),
document: i.to_document(),
})
.collect();
self.backend.upsert(T::index_name(), docs).await
}
pub async fn remove<T: Searchable>(&self, item: &T) -> Result<()> {
self.remove_id::<T>(&item.search_id()).await
}
pub async fn remove_id<T: Searchable>(&self, id: &str) -> Result<()> {
self.backend
.delete(T::index_name(), &[id.to_string()])
.await
}
pub async fn reindex<T: Searchable>(&self, items: &[T]) -> Result<usize> {
self.backend.clear(T::index_name()).await?;
self.index_many(items).await?;
Ok(items.len())
}
pub async fn search<T: Searchable>(&self, query: impl Into<Query>) -> Result<Vec<Hit>> {
self.backend.search(T::index_name(), &query.into()).await
}
pub async fn search_ids<T: Searchable>(&self, query: impl Into<Query>) -> Result<Vec<String>> {
Ok(self
.search::<T>(query)
.await?
.into_iter()
.map(|h| h.id)
.collect())
}
pub async fn count<T: Searchable>(&self) -> Result<usize> {
self.backend.count(T::index_name()).await
}
}