use super::{ account::session::Session, error::MyResult, layout::{DynLayoutPage, LayoutPage}, node::NodeCard, }; 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?&")] pub async fn r_search<'a>( session: Session, db: &State, query: Option<&str>, page: Option, ) -> MyResult> { 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(); results.push((id.to_owned(), node, udata)); } Some((scount, results)) } else { None }; let search_dur = timing.elapsed(); let query = query.unwrap_or_default().to_string(); Ok(LayoutPage { title: "Search".to_string(), class: Some("search"), content: markup::new! { h1 { "Search" } form[action="", method="GET"] { input[type="text", name="query", placeholder="Search Term", value=&query]; input[type="submit", value="Search"]; } @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 } }, }) }