1#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
2use flams_backend_types::search::FragmentQueryFilter;
3use flams_backend_types::search::{QueryFilter, SearchResult, SearchResultKind};
4#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
5use ftml_uris::{DocumentElementUri, DocumentUri};
6
7#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
8#[must_use]
9pub fn build_query(
10 query: &str,
11 index: &tantivy::Index,
12 filter: FragmentQueryFilter,
13) -> Option<Box<dyn tantivy::query::Query>> {
14 use std::fmt::Write;
15 let mut s = String::new();
16 if !filter.flags.allow_documents()
17 || !filter.flags.allow_paragraphs()
18 || !filter.flags.allow_definitions()
19 || !filter.flags.allow_examples()
20 || !filter.flags.allow_assertions()
21 || !filter.flags.allow_problems()
22 {
23 let mut had_first = false;
25 if filter.flags.allow_documents() {
26 had_first = true;
27 s.push_str("(kind:0");
28 }
29 if filter.flags.allow_paragraphs() {
30 s.push_str(if had_first { " OR kind:1" } else { "(kind:1" });
31 had_first = true;
32 }
33 if filter.flags.allow_definitions() {
34 s.push_str(if had_first { " OR kind:2" } else { "(kind:2" });
35 had_first = true;
36 }
37 if filter.flags.allow_examples() {
38 s.push_str(if had_first { " OR kind:3" } else { "(kind:3" });
39 had_first = true;
40 }
41 if filter.flags.allow_assertions() {
42 s.push_str(if had_first { " OR kind:4" } else { "(kind:4" });
43 had_first = true;
44 }
45 if filter.flags.allow_problems() {
46 s.push_str(if had_first { " OR kind:5" } else { "(kind:5" });
47 }
48 if had_first {
49 s.push_str(") AND ");
50 }
51 }
52 if filter.flags.is_definition_like() {
53 s.push_str("deflike:true AND ");
54 }
55 write!(s, "({query})").ok()?;
56 let schema = crate::schema::SearchSchema::get();
57 let mut parser = tantivy::query::QueryParser::for_index(
58 index,
59 vec![schema.fors, schema.uri, schema.title, schema.body],
60 );
61 parser.set_conjunction_by_default();
63 parser.parse_query(&s).ok()
64}
65
66#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
67#[derive(Debug)]
68pub(crate) struct Wrapper<T>(pub T);
69
70#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
71impl tantivy::schema::document::ValueDeserialize for Wrapper<bool> {
72 fn deserialize<'de, D>(
73 deserializer: D,
74 ) -> Result<Self, tantivy::schema::document::DeserializeError>
75 where
76 D: tantivy::schema::document::ValueDeserializer<'de>,
77 {
78 Ok(Self(deserializer.deserialize_bool()?))
79 }
80}
81
82#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
83impl tantivy::schema::document::ValueDeserialize for Wrapper<SearchResultKind> {
84 fn deserialize<'de, D>(
85 deserializer: D,
86 ) -> Result<Self, tantivy::schema::document::DeserializeError>
87 where
88 D: tantivy::schema::document::ValueDeserializer<'de>,
89 {
90 deserializer
91 .deserialize_u64()?
92 .try_into()
93 .map(Wrapper)
94 .map_err(|()| {
95 tantivy::schema::document::DeserializeError::custom(format_args!("weird"))
96 })
97 }
98}
99
100#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
101impl tantivy::schema::document::DocumentDeserialize for Wrapper<SearchResult> {
102 fn deserialize<'de, D>(
103 mut deserializer: D,
104 ) -> Result<Self, tantivy::schema::document::DeserializeError>
105 where
106 D: tantivy::schema::document::DocumentDeserializer<'de>,
107 {
108 macro_rules! next {
109 ($name:literal) => {{
110 let Some((_, r)) = deserializer.next_field()?.map_err(|e| {
111 tantivy::schema::document::DeserializeError::custom(format_args!(
112 "weird A: {e} (in {})",
113 $name
114 ))
115 }) else {
116 return Err(tantivy::schema::document::DeserializeError::custom(
117 format_args!("Missing value {}", $name),
118 ));
119 };
120 r
121 }};
122 ($name:literal!) => {{
123 let Some((_, Wrapper(r))) = deserializer.next_field().map_err(|e| {
124 tantivy::schema::document::DeserializeError::custom(format_args!(
125 "weird A: {e} (in {})",
126 $name
127 ))
128 })?
129 else {
130 return Err(tantivy::schema::document::DeserializeError::custom(
131 format_args!("Missing value {}", $name),
132 ));
133 };
134 r
135 }};
136 }
137 let kind = next!("kind"!);
138 match kind {
139 SearchResultKind::Document => Ok(Self(SearchResult::Document(next!("uri"!)))),
140 kind => {
141 let uri = next!("uri"!);
142 let def_like = next!("deflike"!);
143 let mut fors = Vec::new();
144 while let Ok(Some((_, s))) = deserializer.next_field() {
145 fors.push(s);
146 }
147 Ok(Self(SearchResult::Paragraph {
148 uri,
149 def_like,
150 kind,
151 fors,
152 }))
153 }
154 }
155 }
156}
157
158#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
159impl tantivy::schema::document::ValueDeserialize for Wrapper<DocumentUri> {
160 fn deserialize<'de, D>(
161 mut deserializer: D,
162 ) -> Result<Self, tantivy::schema::document::DeserializeError>
163 where
164 D: tantivy::schema::document::ValueDeserializer<'de>,
165 {
166 unsafe { String::from_utf8_unchecked(deserializer.deserialize_bytes()?) }
168 .parse()
169 .map_or_else(
170 |_| {
171 Err(tantivy::schema::document::DeserializeError::custom(
172 "Invalid DocumentUri",
173 ))
174 },
175 |u| Ok(Wrapper(u)),
176 )
177 }
178}
179
180#[cfg(all(feature = "tantivy", not(feature = "vectorsearch")))]
181impl tantivy::schema::document::ValueDeserialize for Wrapper<DocumentElementUri> {
182 fn deserialize<'de, D>(
183 mut deserializer: D,
184 ) -> Result<Self, tantivy::schema::document::DeserializeError>
185 where
186 D: tantivy::schema::document::ValueDeserializer<'de>,
187 {
188 unsafe { String::from_utf8_unchecked(deserializer.deserialize_bytes()?) }
190 .parse()
191 .map_or_else(
192 |_| {
193 Err(tantivy::schema::document::DeserializeError::custom(
194 "Invalid DocumentElementUri",
195 ))
196 },
197 |u| Ok(Wrapper(u)),
198 )
199 }
200}