1use serde::ser::{Serialize, SerializeStruct, Serializer};
2
3use crate::util::*;
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
40pub struct Analyze {
41 text: StringOrVecString,
42
43 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip", flatten)]
44 analysis: Option<Analysis>,
45
46 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
47 attributes: Vec<String>,
48
49 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
50 explain: Option<bool>,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
55pub struct CustomAnalyzer {
56 tokenizer: String,
57
58 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
59 char_filter: Vec<StringOrObject>,
60
61 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
62 filter: Vec<StringOrObject>,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
67pub struct CustomNormalizer {
68 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
69 char_filter: Vec<StringOrObject>,
70
71 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
72 filter: Vec<StringOrObject>,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
77pub enum Analysis {
78 BuiltInAnalyzer(String),
88
89 CustomAnalyzer(CustomAnalyzer),
91
92 BuiltInNormalizer(String),
97
98 CustomNormalizer(CustomNormalizer),
100
101 Field(String),
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
111#[serde(untagged)]
112pub enum StringOrObject {
113 String(String),
115
116 Object(serde_json::Value),
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
122#[serde(untagged)]
123pub enum StringOrVecString {
124 String(String),
126
127 VecString(Vec<String>),
129}
130
131impl Analyze {
132 pub fn new<S>(text: S) -> Self
137 where
138 S: Into<StringOrVecString>, {
139 Self {
140 text: text.into(),
141 analysis: None,
142 attributes: vec![],
143 explain: None,
144 }
145 }
146
147 pub fn analyzer<S>(mut self, analyzer: S) -> Self
150 where
151 S: Into<Analysis>, {
152 self.analysis = Some(analyzer.into());
153 self
154 }
155
156 pub fn attributes<I>(mut self, attributes: I) -> Self
159 where
160 I: IntoIterator,
161 I::Item: ToString, {
162 self.attributes.extend(attributes.into_iter().map(|x| x.to_string()));
163 self
164 }
165
166 pub fn explain(mut self, explain: bool) -> Self {
169 self.explain = Some(explain);
170 self
171 }
172}
173
174impl CustomNormalizer {
175 pub fn new() -> Self {
177 Default::default()
178 }
179
180 pub fn char_filter<I>(mut self, char_filter: I) -> Self
186 where
187 I: IntoIterator,
188 I::Item: Into<StringOrObject>, {
189 self.char_filter.extend(char_filter.into_iter().map(Into::into));
190 self
191 }
192
193 pub fn filter<I>(mut self, filter: I) -> Self
198 where
199 I: IntoIterator,
200 I::Item: Into<StringOrObject>, {
201 self.filter.extend(filter.into_iter().map(Into::into));
202 self
203 }
204}
205
206impl CustomAnalyzer {
207 pub fn new<S>(tokenizer: S) -> Self
213 where
214 S: ToString, {
215 Self {
216 tokenizer: tokenizer.to_string(),
217 char_filter: vec![],
218 filter: vec![],
219 }
220 }
221
222 pub fn char_filter<I>(mut self, char_filter: I) -> Self
228 where
229 I: IntoIterator,
230 I::Item: Into<StringOrObject>, {
231 self.char_filter.extend(char_filter.into_iter().map(Into::into));
232 self
233 }
234
235 pub fn filter<I>(mut self, filter: I) -> Self
240 where
241 I: IntoIterator,
242 I::Item: Into<StringOrObject>, {
243 self.filter.extend(filter.into_iter().map(Into::into));
244 self
245 }
246}
247
248impl Analysis {
249 pub fn field<S>(value: S) -> Self
251 where
252 S: ToString, {
253 Self::Field(value.to_string())
254 }
255
256 pub fn analyzer<S>(value: S) -> Self
258 where
259 S: ToString, {
260 Self::BuiltInAnalyzer(value.to_string())
261 }
262
263 pub fn normalizer<S>(value: S) -> Self
265 where
266 S: ToString, {
267 Self::BuiltInNormalizer(value.to_string())
268 }
269}
270
271impl<'a> From<&'a str> for StringOrObject {
272 fn from(value: &'a str) -> Self {
273 Self::String(value.to_owned())
274 }
275}
276
277impl From<String> for StringOrObject {
278 fn from(value: String) -> Self {
279 Self::String(value)
280 }
281}
282
283impl From<serde_json::Value> for StringOrObject {
284 fn from(value: serde_json::Value) -> Self {
285 Self::Object(value)
286 }
287}
288
289impl From<CustomAnalyzer> for Analysis {
290 fn from(value: CustomAnalyzer) -> Self {
291 Self::CustomAnalyzer(value)
292 }
293}
294
295impl From<CustomNormalizer> for Analysis {
296 fn from(value: CustomNormalizer) -> Self {
297 Self::CustomNormalizer(value)
298 }
299}
300
301impl From<String> for StringOrVecString {
302 fn from(value: String) -> Self {
303 Self::String(value)
304 }
305}
306
307impl From<&str> for StringOrVecString {
308 fn from(value: &str) -> Self {
309 Self::String(value.into())
310 }
311}
312
313impl From<Vec<&str>> for StringOrVecString {
314 fn from(value: Vec<&str>) -> Self {
315 Self::VecString(value.into_iter().map(Into::into).collect())
316 }
317}
318
319impl<const N: usize> From<[&str; N]> for StringOrVecString {
320 fn from(value: [&str; N]) -> Self {
321 Self::VecString(value.iter().map(ToString::to_string).collect())
322 }
323}
324
325impl<'a> From<&'a [&str]> for StringOrVecString {
326 fn from(value: &'a [&str]) -> Self {
327 Self::VecString(value.iter().map(ToString::to_string).collect())
328 }
329}
330
331impl Serialize for Analysis {
332 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
333 where
334 S: Serializer, {
335 match self {
336 Analysis::BuiltInAnalyzer(name) => {
337 let mut state = serializer.serialize_struct("analysis_analyzer", 1)?;
338 state.serialize_field("analyzer", name)?;
339 state.end()
340 }
341 Analysis::CustomAnalyzer(analyzer) => analyzer.serialize(serializer),
342 Analysis::BuiltInNormalizer(name) => {
343 let mut state = serializer.serialize_struct("analysis_normalizer", 1)?;
344 state.serialize_field("normalizer", name)?;
345 state.end()
346 }
347 Analysis::CustomNormalizer(normalizer) => normalizer.serialize(serializer),
348 Analysis::Field(name) => {
349 let mut state = serializer.serialize_struct("analysis_field", 1)?;
350 state.serialize_field("field", name)?;
351 state.end()
352 }
353 }
354 }
355}
356
357impl Default for StringOrVecString {
358 fn default() -> Self {
359 Self::String(Default::default())
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366
367 #[test]
368 fn serialization() {
369 assert_serialize(
370 Analyze::new("analyze these pants"),
371 json!({
372 "text": "analyze these pants"
373 }),
374 );
375
376 assert_serialize(
377 Analyze::new("analyze these pants").analyzer(Analysis::analyzer("test_default")),
378 json!({
379 "text": "analyze these pants",
380 "analyzer": "test_default"
381 }),
382 );
383
384 assert_serialize(
385 Analyze::new(["here is one to test", "and here is another one"])
386 .analyzer(
387 CustomAnalyzer::new("lowercase")
388 .char_filter(["html_strip", "test_strip"])
389 .filter([json!({"type": "stop", "stopwords": ["a", "is", "this"]})]),
390 )
391 .attributes(["score", "keyword"])
392 .explain(true),
393 json!({
394 "attributes": [
395 "score",
396 "keyword"
397 ],
398 "char_filter": [
399 "html_strip",
400 "test_strip"
401 ],
402 "filter" : [{"type": "stop", "stopwords": ["a", "is", "this"]}],
403 "tokenizer": "lowercase",
404 "explain": true,
405 "text": ["here is one to test", "and here is another one"]
406 }),
407 );
408
409 assert_serialize(
410 Analyze::new("analyze these pants").analyzer(Analysis::normalizer("asciifolding")),
411 json!({
412 "text": "analyze these pants",
413 "normalizer": "asciifolding"
414 }),
415 );
416
417 assert_serialize(
418 Analyze::new(["here is one to test", "and here is another one"])
419 .analyzer(
420 CustomNormalizer::new()
421 .char_filter(["html_strip", "test_strip"])
422 .filter([json!({"type": "stop", "stopwords": ["a", "is", "this"]})]),
423 )
424 .attributes(["score", "keyword"])
425 .explain(true),
426 json!({
427 "attributes": [
428 "score",
429 "keyword"
430 ],
431 "char_filter": [
432 "html_strip",
433 "test_strip"
434 ],
435 "filter" : [{"type": "stop", "stopwords": ["a", "is", "this"]}],
436 "explain": true,
437 "text": ["here is one to test", "and here is another one"]
438 }),
439 );
440
441 assert_serialize(
442 Analyze::new("analyze these pants").analyzer(Analysis::field("title")),
443 json!({
444 "text": "analyze these pants",
445 "field": "title"
446 }),
447 );
448 }
449}