aboutsummaryrefslogtreecommitdiff
path: root/server/src/routes/ui/search.rs
blob: c1f9865271775ddf1e5fb606d8c1f3710c57dba8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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?<query>&<page>")]
pub async fn r_search<'a>(
    session: Session,
    db: &State<DataAcid>,
    query: Option<&str>,
    page: Option<usize>,
) -> MyResult<DynLayoutPage<'a>> {
    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"))?;
            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
            }
        },
    })
}