flams_search/
query.rs

1use flams_backend_types::search::{QueryFilter, SearchResult, SearchResultKind};
2
3#[must_use]
4pub fn build_query(
5    query: &str,
6    index: &tantivy::Index,
7    filter: QueryFilter,
8) -> Option<Box<dyn tantivy::query::Query>> {
9    use std::fmt::Write;
10    let QueryFilter {
11        allow_documents,
12        allow_paragraphs,
13        allow_definitions,
14        allow_examples,
15        allow_assertions,
16        allow_problems,
17        definition_like_only,
18    } = filter;
19    let mut s = String::new();
20    if !allow_documents
21        || !allow_paragraphs
22        || !allow_definitions
23        || !allow_examples
24        || !allow_assertions
25        || !allow_problems
26    {
27        //s.push('(');
28        let mut had_first = false;
29        if allow_documents {
30            had_first = true;
31            s.push_str("(kind:0");
32        }
33        if allow_paragraphs {
34            s.push_str(if had_first { " OR kind:1" } else { "(kind:1" });
35            had_first = true;
36        }
37        if allow_definitions {
38            s.push_str(if had_first { " OR kind:2" } else { "(kind:2" });
39            had_first = true;
40        }
41        if allow_examples {
42            s.push_str(if had_first { " OR kind:3" } else { "(kind:3" });
43            had_first = true;
44        }
45        if allow_assertions {
46            s.push_str(if had_first { " OR kind:4" } else { "(kind:4" });
47            had_first = true;
48        }
49        if allow_problems {
50            s.push_str(if had_first { " OR kind:5" } else { "(kind:5" });
51        }
52        if had_first {
53            s.push_str(") AND ");
54        }
55    }
56    if definition_like_only {
57        s.push_str("deflike:true AND ");
58    }
59    write!(s, "({query})").ok()?;
60    let schema = crate::schema::SearchSchema::get();
61    let mut parser = tantivy::query::QueryParser::for_index(index, vec![schema.fors,schema.uri, schema.title, schema.body]);
62    //parser.set_field_fuzzy(SCHEMA.body, false, 1, true);
63    parser.set_conjunction_by_default();
64    parser.parse_query(&s).ok()
65}
66
67#[derive(Debug)]
68pub(crate) struct Wrapper<T>(pub T);
69
70impl tantivy::schema::document::ValueDeserialize for Wrapper<bool> {
71    fn deserialize<'de, D>(
72        deserializer: D,
73    ) -> Result<Self, tantivy::schema::document::DeserializeError>
74    where
75        D: tantivy::schema::document::ValueDeserializer<'de>,
76    {
77        Ok(Self(deserializer.deserialize_bool()?))
78    }
79}
80
81impl tantivy::schema::document::ValueDeserialize for Wrapper<SearchResultKind> {
82    fn deserialize<'de, D>(
83        deserializer: D,
84    ) -> Result<Self, tantivy::schema::document::DeserializeError>
85    where
86        D: tantivy::schema::document::ValueDeserializer<'de>,
87    {
88        deserializer
89            .deserialize_u64()?
90            .try_into()
91            .map(Wrapper)
92            .map_err(|()| tantivy::schema::document::DeserializeError::custom(""))
93    }
94}
95
96impl tantivy::schema::document::DocumentDeserialize for Wrapper<SearchResult> {
97    fn deserialize<'de, D>(
98        mut deserializer: D,
99    ) -> Result<Self, tantivy::schema::document::DeserializeError>
100    where
101        D: tantivy::schema::document::DocumentDeserializer<'de>,
102    {
103        macro_rules! next {
104            () => {{
105                let Some((_, r)) = deserializer.next_field()? else {
106                    return Err(tantivy::schema::document::DeserializeError::custom(
107                        "Missing value",
108                    ));
109                };
110                r
111            }};
112            (!) => {{
113                let Some((_, Wrapper(r))) = deserializer.next_field()? else {
114                    return Err(tantivy::schema::document::DeserializeError::custom(
115                        "Missing value",
116                    ));
117                };
118                r
119            }};
120        }
121        let Wrapper(kind) = next!();
122        match kind {
123            SearchResultKind::Document => Ok(Self(SearchResult::Document(next!()))),
124            kind => {
125                let uri = next!();
126                let def_like = next!(!);
127                let mut fors = Vec::new();
128                while let Some((_, s)) = deserializer.next_field()? {
129                    fors.push(s);
130                }
131                Ok(Self(SearchResult::Paragraph {
132                    uri,
133                    def_like,
134                    kind,
135                    fors,
136                }))
137            }
138        }
139    }
140}