1use std::marker::PhantomData;
11
12use serde_json::{Map, Value};
13
14use super::{
15 Common, FlussoValue, Sort, SortOrder, TermsQuery, common_opts, exists_q, keyed_value_query,
16 kind, wrap,
17};
18use crate::query::{AsQuery, Query, Root};
19
20fn keyword_term(value: &impl serde::Serialize) -> Value {
26 match serde_json::to_value(value) {
27 Ok(Value::String(string)) => Value::String(string),
28 Ok(other) => Value::String(other.to_string()),
29 Err(_) => Value::String(String::new()),
30 }
31}
32
33#[derive(Debug, Clone)]
36pub struct TermQuery<S = Root> {
37 path: String,
38 value: Value,
39 case_insensitive: Option<bool>,
40 common: Common,
41 _scope: PhantomData<fn() -> S>,
42}
43
44impl<S> TermQuery<S> {
45 fn new(path: &str, value: Value) -> Self {
46 Self {
47 path: path.to_string(),
48 value,
49 case_insensitive: None,
50 common: Common::default(),
51 _scope: PhantomData,
52 }
53 }
54
55 #[must_use]
57 pub fn case_insensitive(mut self) -> Self {
58 self.case_insensitive = Some(true);
59 self
60 }
61
62 common_opts!(common);
63}
64
65impl<S> AsQuery<S> for TermQuery<S> {
66 fn into_query(self) -> Option<Query<S>> {
67 let mut opts = Map::new();
68 if let Some(ci) = self.case_insensitive {
69 opts.insert("case_insensitive".to_string(), Value::Bool(ci));
70 }
71 self.common.write(&mut opts);
72 Some(keyed_value_query(
73 "term", &self.path, "value", self.value, opts,
74 ))
75 }
76}
77
78#[derive(Debug, Clone)]
80pub struct PrefixQuery<S = Root> {
81 path: String,
82 value: String,
83 case_insensitive: Option<bool>,
84 rewrite: Option<String>,
85 common: Common,
86 _scope: PhantomData<fn() -> S>,
87}
88
89impl<S> PrefixQuery<S> {
90 fn new(path: &str, value: String) -> Self {
91 Self {
92 path: path.to_string(),
93 value,
94 case_insensitive: None,
95 rewrite: None,
96 common: Common::default(),
97 _scope: PhantomData,
98 }
99 }
100
101 #[must_use]
103 pub fn case_insensitive(mut self) -> Self {
104 self.case_insensitive = Some(true);
105 self
106 }
107
108 #[must_use]
110 pub fn rewrite(mut self, rewrite: impl Into<String>) -> Self {
111 self.rewrite = Some(rewrite.into());
112 self
113 }
114
115 common_opts!(common);
116}
117
118impl<S> AsQuery<S> for PrefixQuery<S> {
119 fn into_query(self) -> Option<Query<S>> {
120 let mut opts = Map::new();
121 if let Some(ci) = self.case_insensitive {
122 opts.insert("case_insensitive".to_string(), Value::Bool(ci));
123 }
124 if let Some(rewrite) = self.rewrite {
125 opts.insert("rewrite".to_string(), Value::String(rewrite));
126 }
127 self.common.write(&mut opts);
128 Some(keyed_value_query(
129 "prefix",
130 &self.path,
131 "value",
132 Value::String(self.value),
133 opts,
134 ))
135 }
136}
137
138#[derive(Debug, Clone)]
140pub struct WildcardQuery<S = Root> {
141 path: String,
142 value: String,
143 case_insensitive: Option<bool>,
144 rewrite: Option<String>,
145 common: Common,
146 _scope: PhantomData<fn() -> S>,
147}
148
149impl<S> WildcardQuery<S> {
150 fn new(path: &str, value: String) -> Self {
151 Self {
152 path: path.to_string(),
153 value,
154 case_insensitive: None,
155 rewrite: None,
156 common: Common::default(),
157 _scope: PhantomData,
158 }
159 }
160
161 #[must_use]
163 pub fn case_insensitive(mut self) -> Self {
164 self.case_insensitive = Some(true);
165 self
166 }
167
168 #[must_use]
170 pub fn rewrite(mut self, rewrite: impl Into<String>) -> Self {
171 self.rewrite = Some(rewrite.into());
172 self
173 }
174
175 common_opts!(common);
176}
177
178impl<S> AsQuery<S> for WildcardQuery<S> {
179 fn into_query(self) -> Option<Query<S>> {
180 let mut opts = Map::new();
181 if let Some(ci) = self.case_insensitive {
182 opts.insert("case_insensitive".to_string(), Value::Bool(ci));
183 }
184 if let Some(rewrite) = self.rewrite {
185 opts.insert("rewrite".to_string(), Value::String(rewrite));
186 }
187 self.common.write(&mut opts);
188 Some(keyed_value_query(
189 "wildcard",
190 &self.path,
191 "value",
192 Value::String(self.value),
193 opts,
194 ))
195 }
196}
197
198#[derive(Debug, Clone)]
201pub struct RegexpQuery<S = Root> {
202 path: String,
203 value: String,
204 case_insensitive: Option<bool>,
205 flags: Option<String>,
206 max_determinized_states: Option<u32>,
207 common: Common,
208 _scope: PhantomData<fn() -> S>,
209}
210
211impl<S> RegexpQuery<S> {
212 fn new(path: &str, value: String) -> Self {
213 Self {
214 path: path.to_string(),
215 value,
216 case_insensitive: None,
217 flags: None,
218 max_determinized_states: None,
219 common: Common::default(),
220 _scope: PhantomData,
221 }
222 }
223
224 #[must_use]
226 pub fn case_insensitive(mut self) -> Self {
227 self.case_insensitive = Some(true);
228 self
229 }
230
231 #[must_use]
233 pub fn flags(mut self, flags: impl Into<String>) -> Self {
234 self.flags = Some(flags.into());
235 self
236 }
237
238 #[must_use]
240 pub fn max_determinized_states(mut self, max: u32) -> Self {
241 self.max_determinized_states = Some(max);
242 self
243 }
244
245 common_opts!(common);
246}
247
248impl<S> AsQuery<S> for RegexpQuery<S> {
249 fn into_query(self) -> Option<Query<S>> {
250 let mut opts = Map::new();
251 if let Some(ci) = self.case_insensitive {
252 opts.insert("case_insensitive".to_string(), Value::Bool(ci));
253 }
254 if let Some(flags) = self.flags {
255 opts.insert("flags".to_string(), Value::String(flags));
256 }
257 if let Some(max) = self.max_determinized_states {
258 opts.insert("max_determinized_states".to_string(), Value::from(max));
259 }
260 self.common.write(&mut opts);
261 Some(keyed_value_query(
262 "regexp",
263 &self.path,
264 "value",
265 Value::String(self.value),
266 opts,
267 ))
268 }
269}
270
271#[derive(Debug, Clone)]
274pub struct FuzzyQuery<S = Root> {
275 path: String,
276 value: String,
277 fuzziness: Option<String>,
278 prefix_length: Option<u32>,
279 max_expansions: Option<u32>,
280 transpositions: Option<bool>,
281 common: Common,
282 _scope: PhantomData<fn() -> S>,
283}
284
285impl<S> FuzzyQuery<S> {
286 fn new(path: &str, value: String) -> Self {
287 Self {
288 path: path.to_string(),
289 value,
290 fuzziness: None,
291 prefix_length: None,
292 max_expansions: None,
293 transpositions: None,
294 common: Common::default(),
295 _scope: PhantomData,
296 }
297 }
298
299 #[must_use]
301 pub fn fuzziness(mut self, fuzziness: impl Into<String>) -> Self {
302 self.fuzziness = Some(fuzziness.into());
303 self
304 }
305
306 #[must_use]
308 pub fn prefix_length(mut self, prefix_length: u32) -> Self {
309 self.prefix_length = Some(prefix_length);
310 self
311 }
312
313 #[must_use]
315 pub fn max_expansions(mut self, max_expansions: u32) -> Self {
316 self.max_expansions = Some(max_expansions);
317 self
318 }
319
320 #[must_use]
322 pub fn transpositions(mut self, transpositions: bool) -> Self {
323 self.transpositions = Some(transpositions);
324 self
325 }
326
327 common_opts!(common);
328}
329
330impl<S> AsQuery<S> for FuzzyQuery<S> {
331 fn into_query(self) -> Option<Query<S>> {
332 let mut opts = Map::new();
333 if let Some(fuzziness) = self.fuzziness {
334 opts.insert("fuzziness".to_string(), Value::String(fuzziness));
335 }
336 if let Some(prefix_length) = self.prefix_length {
337 opts.insert("prefix_length".to_string(), Value::from(prefix_length));
338 }
339 if let Some(max_expansions) = self.max_expansions {
340 opts.insert("max_expansions".to_string(), Value::from(max_expansions));
341 }
342 if let Some(transpositions) = self.transpositions {
343 opts.insert("transpositions".to_string(), Value::Bool(transpositions));
344 }
345 self.common.write(&mut opts);
346 Some(keyed_value_query(
347 "fuzzy",
348 &self.path,
349 "value",
350 Value::String(self.value),
351 opts,
352 ))
353 }
354}
355
356#[derive(Debug, Clone)]
358pub struct Keyword<S = Root> {
359 path: String,
360 _scope: PhantomData<fn() -> S>,
361}
362
363impl<S> Keyword<S> {
364 pub fn at(path: impl Into<String>) -> Self {
365 Self {
366 path: path.into(),
367 _scope: PhantomData,
368 }
369 }
370
371 pub fn eq(&self, value: impl FlussoValue<kind::Keyword> + serde::Serialize) -> TermQuery<S> {
375 TermQuery::new(&self.path, keyword_term(&value))
376 }
377
378 pub fn in_(
380 &self,
381 values: impl IntoIterator<Item = impl FlussoValue<kind::Keyword> + serde::Serialize>,
382 ) -> TermsQuery<S> {
383 let array = values.into_iter().map(|v| keyword_term(&v)).collect();
384 TermsQuery::new(&self.path, array)
385 }
386
387 pub fn prefix(&self, value: impl Into<String>) -> PrefixQuery<S> {
389 PrefixQuery::new(&self.path, value.into())
390 }
391
392 pub fn wildcard(&self, pattern: impl Into<String>) -> WildcardQuery<S> {
394 WildcardQuery::new(&self.path, pattern.into())
395 }
396
397 pub fn regexp(&self, pattern: impl Into<String>) -> RegexpQuery<S> {
399 RegexpQuery::new(&self.path, pattern.into())
400 }
401
402 pub fn fuzzy(&self, value: impl Into<String>) -> FuzzyQuery<S> {
404 FuzzyQuery::new(&self.path, value.into())
405 }
406
407 pub fn text(&self) -> Text<S> {
412 Text::at(format!("{}.text", self.path))
413 }
414
415 pub fn keyword_lowercase(&self) -> Keyword<S> {
419 Keyword::at(format!("{}.keyword_lowercase", self.path))
420 }
421
422 pub fn exists(&self) -> Query<S> {
424 exists_q(&self.path)
425 }
426
427 pub fn asc(&self) -> Sort {
428 Sort::new(&self.path, SortOrder::Asc)
429 }
430
431 pub fn desc(&self) -> Sort {
432 Sort::new(&self.path, SortOrder::Desc)
433 }
434}
435
436#[derive(Debug, Clone)]
442pub struct MatchQuery<S = Root> {
443 wrapper: &'static str,
444 path: String,
445 value: String,
446 opts: Map<String, Value>,
447 common: Common,
448 _scope: PhantomData<fn() -> S>,
449}
450
451impl<S> MatchQuery<S> {
452 fn new(wrapper: &'static str, path: &str, value: String) -> Self {
453 Self {
454 wrapper,
455 path: path.to_string(),
456 value,
457 opts: Map::new(),
458 common: Common::default(),
459 _scope: PhantomData,
460 }
461 }
462
463 fn set(mut self, key: &str, value: Value) -> Self {
464 self.opts.insert(key.to_string(), value);
465 self
466 }
467
468 #[must_use]
470 pub fn fuzziness(self, fuzziness: impl Into<String>) -> Self {
471 self.set("fuzziness", Value::String(fuzziness.into()))
472 }
473
474 #[must_use]
476 pub fn operator(self, operator: impl Into<String>) -> Self {
477 self.set("operator", Value::String(operator.into()))
478 }
479
480 #[must_use]
482 pub fn minimum_should_match(self, value: impl Into<String>) -> Self {
483 self.set("minimum_should_match", Value::String(value.into()))
484 }
485
486 #[must_use]
488 pub fn prefix_length(self, prefix_length: u32) -> Self {
489 self.set("prefix_length", Value::from(prefix_length))
490 }
491
492 #[must_use]
494 pub fn max_expansions(self, max_expansions: u32) -> Self {
495 self.set("max_expansions", Value::from(max_expansions))
496 }
497
498 #[must_use]
500 pub fn analyzer(self, analyzer: impl Into<String>) -> Self {
501 self.set("analyzer", Value::String(analyzer.into()))
502 }
503
504 #[must_use]
506 pub fn slop(self, slop: u32) -> Self {
507 self.set("slop", Value::from(slop))
508 }
509
510 #[must_use]
512 pub fn zero_terms_query(self, value: impl Into<String>) -> Self {
513 self.set("zero_terms_query", Value::String(value.into()))
514 }
515
516 #[must_use]
518 pub fn lenient(self, lenient: bool) -> Self {
519 self.set("lenient", Value::Bool(lenient))
520 }
521
522 common_opts!(common);
523}
524
525impl<S> AsQuery<S> for MatchQuery<S> {
526 fn into_query(self) -> Option<Query<S>> {
527 let mut opts = self.opts;
528 self.common.write(&mut opts);
529 Some(keyed_value_query(
530 self.wrapper,
531 &self.path,
532 "query",
533 Value::String(self.value),
534 opts,
535 ))
536 }
537}
538
539#[derive(Debug, Clone)]
541pub struct Text<S = Root> {
542 path: String,
543 boost: Option<f32>,
544 _scope: PhantomData<fn() -> S>,
545}
546
547impl<S> Text<S> {
548 pub fn at(path: impl Into<String>) -> Self {
549 Self {
550 path: path.into(),
551 boost: None,
552 _scope: PhantomData,
553 }
554 }
555
556 #[must_use]
560 pub fn boosted(mut self, weight: f32) -> Self {
561 self.boost = Some(weight);
562 self
563 }
564
565 pub(crate) fn field_spec(&self) -> String {
568 match self.boost {
569 Some(weight) => format!("{}^{weight}", self.path),
570 None => self.path.clone(),
571 }
572 }
573
574 pub fn matches(&self, value: impl Into<String>) -> MatchQuery<S> {
576 MatchQuery::new("match", &self.path, value.into())
577 }
578
579 pub fn match_phrase(&self, value: impl Into<String>) -> MatchQuery<S> {
581 MatchQuery::new("match_phrase", &self.path, value.into())
582 }
583
584 pub fn match_phrase_prefix(&self, value: impl Into<String>) -> MatchQuery<S> {
586 MatchQuery::new("match_phrase_prefix", &self.path, value.into())
587 }
588
589 pub fn match_bool_prefix(&self, value: impl Into<String>) -> MatchQuery<S> {
592 MatchQuery::new("match_bool_prefix", &self.path, value.into())
593 }
594
595 pub fn matches_fuzzy(&self, value: impl Into<String>) -> MatchQuery<S> {
597 self.matches(value).fuzziness("AUTO")
598 }
599
600 pub fn keyword(&self) -> Keyword<S> {
606 Keyword::at(format!("{}.keyword", self.path))
607 }
608
609 pub fn keyword_lowercase(&self) -> Keyword<S> {
613 Keyword::at(format!("{}.keyword_lowercase", self.path))
614 }
615
616 pub fn exists(&self) -> Query<S> {
618 exists_q(&self.path)
619 }
620}
621
622pub fn multi_match<S>(
626 query: impl Into<String>,
627 fields: impl IntoIterator<Item = Text<S>>,
628) -> MultiMatchQuery<S> {
629 MultiMatchQuery {
630 query: query.into(),
631 fields: fields.into_iter().map(|f| f.field_spec()).collect(),
632 opts: Map::new(),
633 common: Common::default(),
634 _scope: PhantomData,
635 }
636}
637
638#[derive(Debug, Clone)]
642pub struct MultiMatchQuery<S = Root> {
643 query: String,
644 fields: Vec<String>,
645 opts: Map<String, Value>,
646 common: Common,
647 _scope: PhantomData<fn() -> S>,
648}
649
650impl<S> MultiMatchQuery<S> {
651 fn set(mut self, key: &str, value: Value) -> Self {
652 self.opts.insert(key.to_string(), value);
653 self
654 }
655
656 #[must_use]
659 pub fn match_type(self, match_type: impl Into<String>) -> Self {
660 self.set("type", Value::String(match_type.into()))
661 }
662
663 #[must_use]
665 pub fn operator(self, operator: impl Into<String>) -> Self {
666 self.set("operator", Value::String(operator.into()))
667 }
668
669 #[must_use]
671 pub fn fuzziness(self, fuzziness: impl Into<String>) -> Self {
672 self.set("fuzziness", Value::String(fuzziness.into()))
673 }
674
675 #[must_use]
677 pub fn tie_breaker(self, tie_breaker: f32) -> Self {
678 self.set("tie_breaker", Value::from(tie_breaker))
679 }
680
681 #[must_use]
683 pub fn minimum_should_match(self, value: impl Into<String>) -> Self {
684 self.set("minimum_should_match", Value::String(value.into()))
685 }
686
687 common_opts!(common);
688}
689
690impl<S> AsQuery<S> for MultiMatchQuery<S> {
691 fn into_query(self) -> Option<Query<S>> {
692 let mut body = self.opts;
693 body.insert("query".to_string(), Value::String(self.query));
694 body.insert(
695 "fields".to_string(),
696 Value::Array(self.fields.into_iter().map(Value::String).collect()),
697 );
698 self.common.write(&mut body);
699 Some(wrap("multi_match", body))
700 }
701}