oku_fs/database/posts/
operations.rsuse super::super::core::*;
use super::core::OkuPost;
use super::core::POST_INDEX;
use super::core::POST_INDEX_READER;
use super::core::POST_INDEX_WRITER;
use super::core::POST_SCHEMA;
use crate::fs::util::path_to_entry_key;
use iroh_docs::AuthorId;
use miette::IntoDiagnostic;
use native_db::*;
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::{collections::HashSet, path::PathBuf};
use tantivy::{collector::TopDocs, query::QueryParser, TantivyDocument};
impl OkuDatabase {
pub fn search_posts(
query_string: &str,
result_limit: &Option<usize>,
) -> miette::Result<Vec<OkuPost>> {
let searcher = POST_INDEX_READER.searcher();
let query_parser = QueryParser::for_index(
&POST_INDEX,
vec![
POST_SCHEMA.1["author_id"],
POST_SCHEMA.1["path"],
POST_SCHEMA.1["title"],
POST_SCHEMA.1["body"],
POST_SCHEMA.1["tag"],
],
);
let query = query_parser.parse_query(query_string).into_diagnostic()?;
let limit = result_limit.unwrap_or(10);
let top_docs = searcher
.search(&query, &TopDocs::with_limit(limit))
.into_diagnostic()?;
Ok(top_docs
.par_iter()
.filter_map(|x| searcher.doc(x.1).ok())
.collect::<Vec<TantivyDocument>>()
.into_par_iter()
.filter_map(|x| TryInto::try_into(x).ok())
.collect())
}
pub fn upsert_post(&self, post: &OkuPost) -> miette::Result<Option<OkuPost>> {
let rw: transaction::RwTransaction<'_> =
self.database.rw_transaction().into_diagnostic()?;
let old_value: Option<OkuPost> = rw.upsert(post.clone()).into_diagnostic()?;
rw.commit().into_diagnostic()?;
let mut index_writer = POST_INDEX_WRITER
.clone()
.try_lock_owned()
.into_diagnostic()?;
if let Some(old_post) = old_value.clone() {
index_writer.delete_term(old_post.index_term());
}
index_writer
.add_document(post.to_owned().into())
.into_diagnostic()?;
index_writer.commit().into_diagnostic()?;
Ok(old_value)
}
pub fn upsert_posts(&self, posts: &Vec<OkuPost>) -> miette::Result<Vec<Option<OkuPost>>> {
let rw = self.database.rw_transaction().into_diagnostic()?;
let old_posts: Vec<_> = posts
.clone()
.into_iter()
.filter_map(|post| rw.upsert(post).ok())
.collect();
rw.commit().into_diagnostic()?;
let mut index_writer = POST_INDEX_WRITER
.clone()
.try_lock_owned()
.into_diagnostic()?;
old_posts.par_iter().for_each(|old_post| {
if let Some(old_post) = old_post {
index_writer.delete_term(old_post.index_term());
}
});
posts.par_iter().for_each(|post| {
let _ = index_writer.add_document(post.clone().into());
});
index_writer.commit().into_diagnostic()?;
Ok(old_posts)
}
pub fn delete_post(&self, post: &OkuPost) -> miette::Result<OkuPost> {
let rw = self.database.rw_transaction().into_diagnostic()?;
let removed_post = rw.remove(post.to_owned()).into_diagnostic()?;
rw.commit().into_diagnostic()?;
let mut index_writer = POST_INDEX_WRITER
.clone()
.try_lock_owned()
.into_diagnostic()?;
index_writer.delete_term(removed_post.index_term());
index_writer.commit().into_diagnostic()?;
Ok(removed_post)
}
pub fn delete_posts(&self, posts: &[OkuPost]) -> miette::Result<Vec<OkuPost>> {
let rw = self.database.rw_transaction().into_diagnostic()?;
let removed_posts: Vec<_> = posts
.iter()
.filter_map(|post| rw.remove(post.to_owned()).ok())
.collect();
rw.commit().into_diagnostic()?;
let mut index_writer = POST_INDEX_WRITER
.clone()
.try_lock_owned()
.into_diagnostic()?;
removed_posts.par_iter().for_each(|removed_post| {
index_writer.delete_term(removed_post.index_term());
});
index_writer.commit().into_diagnostic()?;
Ok(removed_posts)
}
pub fn get_posts(&self) -> miette::Result<Vec<OkuPost>> {
let r = self.database.r_transaction().into_diagnostic()?;
r.scan()
.primary()
.into_diagnostic()?
.all()
.into_diagnostic()?
.collect::<Result<Vec<_>, _>>()
.into_diagnostic()
}
pub fn get_posts_by_author(&self, author_id: &AuthorId) -> miette::Result<Vec<OkuPost>> {
Ok(self
.get_posts()?
.into_par_iter()
.filter(|x| x.entry.author() == *author_id)
.collect())
}
pub fn get_posts_by_tag(&self, tag: &String) -> miette::Result<Vec<OkuPost>> {
Ok(self
.get_posts()?
.into_par_iter()
.filter(|x| x.note.tags.contains(tag))
.collect())
}
pub fn get_tags(&self) -> miette::Result<HashSet<String>> {
Ok(self
.get_posts()?
.into_iter()
.flat_map(|x| x.note.tags)
.collect())
}
pub fn get_post(
&self,
author_id: &AuthorId,
path: &PathBuf,
) -> miette::Result<Option<OkuPost>> {
let r = self.database.r_transaction().into_diagnostic()?;
let entry_key = (
author_id.as_bytes().to_vec(),
path_to_entry_key(path).to_vec(),
);
r.get().primary(entry_key).into_diagnostic()
}
}