diff options
Diffstat (limited to 'server/src')
-rw-r--r-- | server/src/routes/ui/error.rs | 5 | ||||
-rw-r--r-- | server/src/routes/ui/layout.rs | 2 | ||||
-rw-r--r-- | server/src/routes/ui/search.rs | 100 |
3 files changed, 61 insertions, 46 deletions
diff --git a/server/src/routes/ui/error.rs b/server/src/routes/ui/error.rs index 41a2de9..02011fc 100644 --- a/server/src/routes/ui/error.rs +++ b/server/src/routes/ui/error.rs @@ -139,3 +139,8 @@ impl From<jellybase::database::TransactionError> for MyError { MyError(anyhow::anyhow!("database oopsie during transaction: {err}")) } } +impl From<jellybase::database::tantivy::TantivyError> for MyError { + fn from(err: jellybase::database::tantivy::TantivyError) -> Self { + MyError(anyhow::anyhow!("database during search: {err}")) + } +} diff --git a/server/src/routes/ui/layout.rs b/server/src/routes/ui/layout.rs index 07fc70c..1696ac4 100644 --- a/server/src/routes/ui/layout.rs +++ b/server/src/routes/ui/layout.rs @@ -46,7 +46,7 @@ markup::define! { @if let Some(_) = session { a.library[href=uri!(r_library_node("library"))] { "My Library" } " " a.library[href=uri!(r_all_items())] { "All Items" } " " - a.library[href=uri!(r_search(None::<&'static str>))] { "Search" } " " + a.library[href=uri!(r_search(None::<&'static str>, None::<usize>))] { "Search" } " " } div.account { @if let Some(session) = session { diff --git a/server/src/routes/ui/search.rs b/server/src/routes/ui/search.rs index cafa755..f87a13b 100644 --- a/server/src/routes/ui/search.rs +++ b/server/src/routes/ui/search.rs @@ -4,60 +4,68 @@ use super::{ layout::{DynLayoutPage, LayoutPage}, node::NodeCard, }; -use edit_distance::edit_distance; -use jellybase::database::{ - tantivy::query::QueryParser, DataAcid, ReadableTable, T_NODE, T_USER_NODE, +use anyhow::{anyhow, Context}; +use jellybase::{ + database::{ + tantivy::{ + collector::{Count, TopDocs}, + query::QueryParser, + schema::Value, + TantivyDocument, + }, + DataAcid, TableExt, T_NODE, T_USER_NODE, + }, + permission::NodePermissionExt, }; use rocket::{get, State}; +use std::time::Instant; -#[get("/search?<query>")] +#[get("/search?<query>&<page>")] pub async fn r_search<'a>( session: Session, db: &State<DataAcid>, query: Option<&str>, + page: Option<usize>, ) -> MyResult<DynLayoutPage<'a>> { - // let results = if let Some(query) = query { - // let mut items = { - // let txn = db.begin_read()?; - // let nodes = txn.open_table(T_NODE)?; - // let node_users = txn.open_table(T_USER_NODE)?; - // let i = nodes - // .iter()? - // .map(|a| { - // let (x, y) = a.unwrap(); - // let (x, y) = (x.value().to_owned(), y.value().0); - // let z = node_users - // .get(&(session.user.name.as_str(), x.as_str())) - // .unwrap() - // .map(|z| z.value().0) - // .unwrap_or_default(); - // let y = y.public; - // (x, y, z) - // }) - // .collect::<Vec<_>>(); - // drop(nodes); - // i - // }; - // let query = query.to_lowercase(); - // items.sort_by_cached_key(|(_, n, _)| { - // n.title - // .as_ref() - // .map(|x| x.to_lowercase()) - // .unwrap_or_default() - // .split(" ") - // .map(|tok| edit_distance(query.as_str(), tok)) - // .min() - // .unwrap_or(usize::MAX) - // }); - // Some(items.into_iter().take(64).collect::<Vec<_>>()) - // } else { - // None - // }; + let timing = Instant::now(); + let results = if let Some(query) = query { + let query = QueryParser::for_index( + &db.node_index.index, + vec![db.node_index.title, db.node_index.description], + ) + .parse_query(query) + .context("parsing query")?; + + let searcher = db.node_index.reader.searcher(); + let sres = searcher.search( + &query, + &TopDocs::with_limit(32).and_offset(page.unwrap_or_default() * 32), + )?; + let scount = searcher.search(&query, &Count)?; + + let mut results = Vec::new(); + for (_, daddr) in sres { + let doc: TantivyDocument = searcher.doc(daddr)?; + let id = doc.get_first(db.node_index.id).unwrap().as_str().unwrap(); + + let node = T_NODE + .get(&db, id)? + .only_if_permitted(&session.user.permissions) + .ok_or(anyhow!("node does not exist"))? + .public; + let udata = T_USER_NODE + .get(&db, &(session.user.name.as_str(), id))? + .unwrap_or_default(); - let query = QueryParser::for_index(index, vec![]); + results.push((id.to_owned(), node, udata)); + } + Some((scount, results)) + } else { + None + }; + let search_dur = timing.elapsed(); - let searcher = db.node_index.reader.searcher(); - searcher.Ok(LayoutPage { + Ok(LayoutPage { title: "Search".to_string(), class: Some("search"), content: markup::new! { @@ -66,11 +74,13 @@ pub async fn r_search<'a>( input[type="text", name="query", placeholder="Search Term"]; input[type="submit", value="Search"]; } - @if let Some(results) = &results { + @if let Some((count, results)) = &results { h2 { "Results" } + p.stats { @format!("Found {count} nodes in {search_dur:?}.") } ul.children {@for (id, node, udata) in results.iter() { li { @NodeCard { id, node, udata } } }} + // TODO pagination } }, }) |