1use std::marker::PhantomData;
11
12use serde_json::{Map, Value};
13
14use super::{
15 Common, FlussoValue, Fuzziness, MinimumShouldMatch, MultiMatchType, Operator, Sort, SortOrder,
16 Sortable, TermsQuery, ZeroTermsQuery, common_opts, exists_q, keyed_value_query, kind, wrap,
17};
18use crate::FlussoDocument;
19use crate::query::{AsQuery, Query, Root};
20
21fn keyword_term(value: &impl serde::Serialize) -> Value {
27 match serde_json::to_value(value) {
28 Ok(Value::String(string)) => Value::String(string),
29 Ok(other) => Value::String(other.to_string()),
30 Err(_) => Value::String(String::new()),
31 }
32}
33
34#[derive(Debug, Clone)]
37pub struct TermQuery<S = Root> {
38 path: String,
39 value: Value,
40 case_insensitive: Option<bool>,
41 common: Common,
42 _scope: PhantomData<fn() -> S>,
43}
44
45impl<S> TermQuery<S> {
46 fn new(path: &str, value: Value) -> Self {
47 Self {
48 path: path.to_string(),
49 value,
50 case_insensitive: None,
51 common: Common::default(),
52 _scope: PhantomData,
53 }
54 }
55
56 #[must_use]
58 pub fn case_insensitive(mut self) -> Self {
59 self.case_insensitive = Some(true);
60 self
61 }
62
63 common_opts!(common);
64}
65
66impl<S> AsQuery<S> for TermQuery<S> {
67 fn into_query(self) -> Option<Query<S>> {
68 let mut opts = Map::new();
69 if let Some(ci) = self.case_insensitive {
70 opts.insert("case_insensitive".to_string(), Value::Bool(ci));
71 }
72 self.common.write(&mut opts);
73 Some(keyed_value_query(
74 "term", &self.path, "value", self.value, opts,
75 ))
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct PrefixQuery<S = Root> {
82 path: String,
83 value: String,
84 case_insensitive: Option<bool>,
85 rewrite: Option<String>,
86 common: Common,
87 _scope: PhantomData<fn() -> S>,
88}
89
90impl<S> PrefixQuery<S> {
91 fn new(path: &str, value: String) -> Self {
92 Self {
93 path: path.to_string(),
94 value,
95 case_insensitive: None,
96 rewrite: None,
97 common: Common::default(),
98 _scope: PhantomData,
99 }
100 }
101
102 #[must_use]
104 pub fn case_insensitive(mut self) -> Self {
105 self.case_insensitive = Some(true);
106 self
107 }
108
109 #[must_use]
111 pub fn rewrite(mut self, rewrite: impl Into<String>) -> Self {
112 self.rewrite = Some(rewrite.into());
113 self
114 }
115
116 common_opts!(common);
117}
118
119impl<S> AsQuery<S> for PrefixQuery<S> {
120 fn into_query(self) -> Option<Query<S>> {
121 let mut opts = Map::new();
122 if let Some(ci) = self.case_insensitive {
123 opts.insert("case_insensitive".to_string(), Value::Bool(ci));
124 }
125 if let Some(rewrite) = self.rewrite {
126 opts.insert("rewrite".to_string(), Value::String(rewrite));
127 }
128 self.common.write(&mut opts);
129 Some(keyed_value_query(
130 "prefix",
131 &self.path,
132 "value",
133 Value::String(self.value),
134 opts,
135 ))
136 }
137}
138
139#[derive(Debug, Clone)]
141pub struct WildcardQuery<S = Root> {
142 path: String,
143 value: String,
144 case_insensitive: Option<bool>,
145 rewrite: Option<String>,
146 common: Common,
147 _scope: PhantomData<fn() -> S>,
148}
149
150impl<S> WildcardQuery<S> {
151 fn new(path: &str, value: String) -> Self {
152 Self {
153 path: path.to_string(),
154 value,
155 case_insensitive: None,
156 rewrite: None,
157 common: Common::default(),
158 _scope: PhantomData,
159 }
160 }
161
162 #[must_use]
164 pub fn case_insensitive(mut self) -> Self {
165 self.case_insensitive = Some(true);
166 self
167 }
168
169 #[must_use]
171 pub fn rewrite(mut self, rewrite: impl Into<String>) -> Self {
172 self.rewrite = Some(rewrite.into());
173 self
174 }
175
176 common_opts!(common);
177}
178
179impl<S> AsQuery<S> for WildcardQuery<S> {
180 fn into_query(self) -> Option<Query<S>> {
181 let mut opts = Map::new();
182 if let Some(ci) = self.case_insensitive {
183 opts.insert("case_insensitive".to_string(), Value::Bool(ci));
184 }
185 if let Some(rewrite) = self.rewrite {
186 opts.insert("rewrite".to_string(), Value::String(rewrite));
187 }
188 self.common.write(&mut opts);
189 Some(keyed_value_query(
190 "wildcard",
191 &self.path,
192 "value",
193 Value::String(self.value),
194 opts,
195 ))
196 }
197}
198
199#[derive(Debug, Clone)]
202pub struct RegexpQuery<S = Root> {
203 path: String,
204 value: String,
205 case_insensitive: Option<bool>,
206 flags: Option<String>,
207 max_determinized_states: Option<u32>,
208 common: Common,
209 _scope: PhantomData<fn() -> S>,
210}
211
212impl<S> RegexpQuery<S> {
213 fn new(path: &str, value: String) -> Self {
214 Self {
215 path: path.to_string(),
216 value,
217 case_insensitive: None,
218 flags: None,
219 max_determinized_states: None,
220 common: Common::default(),
221 _scope: PhantomData,
222 }
223 }
224
225 #[must_use]
227 pub fn case_insensitive(mut self) -> Self {
228 self.case_insensitive = Some(true);
229 self
230 }
231
232 #[must_use]
234 pub fn flags(mut self, flags: impl Into<String>) -> Self {
235 self.flags = Some(flags.into());
236 self
237 }
238
239 #[must_use]
241 pub fn max_determinized_states(mut self, max: u32) -> Self {
242 self.max_determinized_states = Some(max);
243 self
244 }
245
246 common_opts!(common);
247}
248
249impl<S> AsQuery<S> for RegexpQuery<S> {
250 fn into_query(self) -> Option<Query<S>> {
251 let mut opts = Map::new();
252 if let Some(ci) = self.case_insensitive {
253 opts.insert("case_insensitive".to_string(), Value::Bool(ci));
254 }
255 if let Some(flags) = self.flags {
256 opts.insert("flags".to_string(), Value::String(flags));
257 }
258 if let Some(max) = self.max_determinized_states {
259 opts.insert("max_determinized_states".to_string(), Value::from(max));
260 }
261 self.common.write(&mut opts);
262 Some(keyed_value_query(
263 "regexp",
264 &self.path,
265 "value",
266 Value::String(self.value),
267 opts,
268 ))
269 }
270}
271
272#[derive(Debug, Clone)]
275pub struct FuzzyQuery<S = Root> {
276 path: String,
277 value: String,
278 fuzziness: Option<Value>,
279 prefix_length: Option<u32>,
280 max_expansions: Option<u32>,
281 transpositions: Option<bool>,
282 common: Common,
283 _scope: PhantomData<fn() -> S>,
284}
285
286impl<S> FuzzyQuery<S> {
287 fn new(path: &str, value: String) -> Self {
288 Self {
289 path: path.to_string(),
290 value,
291 fuzziness: None,
292 prefix_length: None,
293 max_expansions: None,
294 transpositions: None,
295 common: Common::default(),
296 _scope: PhantomData,
297 }
298 }
299
300 #[must_use]
302 pub fn fuzziness(mut self, fuzziness: Fuzziness) -> Self {
303 self.fuzziness = Some(fuzziness.to_value());
304 self
305 }
306
307 #[must_use]
309 pub fn prefix_length(mut self, prefix_length: u32) -> Self {
310 self.prefix_length = Some(prefix_length);
311 self
312 }
313
314 #[must_use]
316 pub fn max_expansions(mut self, max_expansions: u32) -> Self {
317 self.max_expansions = Some(max_expansions);
318 self
319 }
320
321 #[must_use]
323 pub fn transpositions(mut self, transpositions: bool) -> Self {
324 self.transpositions = Some(transpositions);
325 self
326 }
327
328 common_opts!(common);
329}
330
331impl<S> AsQuery<S> for FuzzyQuery<S> {
332 fn into_query(self) -> Option<Query<S>> {
333 let mut opts = Map::new();
334 if let Some(fuzziness) = self.fuzziness {
335 opts.insert("fuzziness".to_string(), fuzziness);
336 }
337 if let Some(prefix_length) = self.prefix_length {
338 opts.insert("prefix_length".to_string(), Value::from(prefix_length));
339 }
340 if let Some(max_expansions) = self.max_expansions {
341 opts.insert("max_expansions".to_string(), Value::from(max_expansions));
342 }
343 if let Some(transpositions) = self.transpositions {
344 opts.insert("transpositions".to_string(), Value::Bool(transpositions));
345 }
346 self.common.write(&mut opts);
347 Some(keyed_value_query(
348 "fuzzy",
349 &self.path,
350 "value",
351 Value::String(self.value),
352 opts,
353 ))
354 }
355}
356
357#[derive(Debug)]
364pub enum WithSubfields {}
365
366#[derive(Debug)]
371pub enum NoSubfields {}
372
373#[derive(Debug)]
383pub enum MapKey {}
384
385#[derive(Debug, Clone)]
389pub struct Keyword<S = Root, Sub = WithSubfields> {
390 path: String,
391 _marker: PhantomData<fn() -> (S, Sub)>,
392}
393
394impl<S, Sub> Keyword<S, Sub> {
395 fn handle(path: impl Into<String>) -> Self {
396 Self {
397 path: path.into(),
398 _marker: PhantomData,
399 }
400 }
401
402 pub fn eq(&self, value: impl FlussoValue<kind::Keyword>) -> TermQuery<S> {
406 TermQuery::new(&self.path, keyword_term(&value))
407 }
408
409 pub fn any_of(
411 &self,
412 values: impl IntoIterator<Item = impl FlussoValue<kind::Keyword>>,
413 ) -> TermsQuery<S> {
414 let array = values.into_iter().map(|v| keyword_term(&v)).collect();
415 TermsQuery::new(&self.path, array)
416 }
417
418 pub fn prefix(&self, value: impl Into<String>) -> PrefixQuery<S> {
420 PrefixQuery::new(&self.path, value.into())
421 }
422
423 pub fn wildcard(&self, pattern: impl Into<String>) -> WildcardQuery<S> {
425 WildcardQuery::new(&self.path, pattern.into())
426 }
427
428 pub fn regexp(&self, pattern: impl Into<String>) -> RegexpQuery<S> {
430 RegexpQuery::new(&self.path, pattern.into())
431 }
432
433 pub fn fuzzy(&self, value: impl Into<String>) -> FuzzyQuery<S> {
435 FuzzyQuery::new(&self.path, value.into())
436 }
437
438 pub fn exists(&self) -> Query<S> {
440 exists_q(&self.path)
441 }
442}
443
444macro_rules! keyword_sortable {
449 ($sub:ty) => {
450 impl<S: FlussoDocument> Sortable for Keyword<S, $sub> {
451 fn asc(&self) -> Sort {
452 Sort::field::<S>(&self.path, SortOrder::Asc)
453 }
454 fn desc(&self) -> Sort {
455 Sort::field::<S>(&self.path, SortOrder::Desc)
456 }
457 }
458 };
459}
460keyword_sortable!(WithSubfields);
461keyword_sortable!(NoSubfields);
462
463impl<S> Keyword<S, MapKey> {
464 pub(crate) fn map_key(path: impl Into<String>) -> Self {
467 Self::handle(path)
468 }
469}
470
471impl<S> Keyword<S, WithSubfields> {
472 pub fn at(path: impl Into<String>) -> Self {
473 Self::handle(path)
474 }
475
476 pub fn text(&self) -> Text<S, NoSubfields> {
480 Text::leaf(format!("{}.text", self.path))
481 }
482
483 pub fn keyword_lowercase(&self) -> Keyword<S, NoSubfields> {
487 Keyword::leaf(format!("{}.keyword_lowercase", self.path))
488 }
489}
490
491impl<S> Keyword<S, NoSubfields> {
492 pub fn leaf(path: impl Into<String>) -> Self {
495 Self::handle(path)
496 }
497}
498
499#[derive(Debug, Clone)]
505pub struct MatchQuery<S = Root> {
506 wrapper: &'static str,
507 path: String,
508 value: String,
509 opts: Map<String, Value>,
510 common: Common,
511 _scope: PhantomData<fn() -> S>,
512}
513
514impl<S> MatchQuery<S> {
515 fn new(wrapper: &'static str, path: &str, value: String) -> Self {
516 Self {
517 wrapper,
518 path: path.to_string(),
519 value,
520 opts: Map::new(),
521 common: Common::default(),
522 _scope: PhantomData,
523 }
524 }
525
526 fn set(mut self, key: &str, value: Value) -> Self {
527 self.opts.insert(key.to_string(), value);
528 self
529 }
530
531 #[must_use]
533 pub fn fuzziness(self, fuzziness: Fuzziness) -> Self {
534 self.set("fuzziness", fuzziness.to_value())
535 }
536
537 #[must_use]
540 pub fn operator(self, operator: Operator) -> Self {
541 self.set("operator", Value::String(operator.as_str().to_string()))
542 }
543
544 #[must_use]
547 pub fn minimum_should_match(self, value: impl Into<MinimumShouldMatch>) -> Self {
548 self.set("minimum_should_match", value.into().to_value())
549 }
550
551 #[must_use]
553 pub fn prefix_length(self, prefix_length: u32) -> Self {
554 self.set("prefix_length", Value::from(prefix_length))
555 }
556
557 #[must_use]
559 pub fn max_expansions(self, max_expansions: u32) -> Self {
560 self.set("max_expansions", Value::from(max_expansions))
561 }
562
563 #[must_use]
565 pub fn analyzer(self, analyzer: impl Into<String>) -> Self {
566 self.set("analyzer", Value::String(analyzer.into()))
567 }
568
569 #[must_use]
571 pub fn slop(self, slop: u32) -> Self {
572 self.set("slop", Value::from(slop))
573 }
574
575 #[must_use]
578 pub fn zero_terms_query(self, value: ZeroTermsQuery) -> Self {
579 self.set(
580 "zero_terms_query",
581 Value::String(value.as_str().to_string()),
582 )
583 }
584
585 #[must_use]
587 pub fn lenient(self, lenient: bool) -> Self {
588 self.set("lenient", Value::Bool(lenient))
589 }
590
591 common_opts!(common);
592}
593
594impl<S> AsQuery<S> for MatchQuery<S> {
595 fn into_query(self) -> Option<Query<S>> {
596 let mut opts = self.opts;
597 self.common.write(&mut opts);
598 Some(keyed_value_query(
599 self.wrapper,
600 &self.path,
601 "query",
602 Value::String(self.value),
603 opts,
604 ))
605 }
606}
607
608#[derive(Debug, Clone)]
612pub struct Text<S = Root, Sub = WithSubfields> {
613 path: String,
614 boost: Option<f32>,
615 _marker: PhantomData<fn() -> (S, Sub)>,
616}
617
618impl<S, Sub> Text<S, Sub> {
619 fn handle(path: impl Into<String>) -> Self {
620 Self {
621 path: path.into(),
622 boost: None,
623 _marker: PhantomData,
624 }
625 }
626
627 #[must_use]
631 pub fn boosted(mut self, weight: f32) -> Self {
632 self.boost = Some(weight);
633 self
634 }
635
636 pub(crate) fn field_spec(&self) -> String {
639 match self.boost {
640 Some(weight) => format!("{}^{weight}", self.path),
641 None => self.path.clone(),
642 }
643 }
644
645 pub fn matches(&self, value: impl Into<String>) -> MatchQuery<S> {
647 MatchQuery::new("match", &self.path, value.into())
648 }
649
650 pub fn match_phrase(&self, value: impl Into<String>) -> MatchQuery<S> {
652 MatchQuery::new("match_phrase", &self.path, value.into())
653 }
654
655 pub fn match_phrase_prefix(&self, value: impl Into<String>) -> MatchQuery<S> {
657 MatchQuery::new("match_phrase_prefix", &self.path, value.into())
658 }
659
660 pub fn match_bool_prefix(&self, value: impl Into<String>) -> MatchQuery<S> {
663 MatchQuery::new("match_bool_prefix", &self.path, value.into())
664 }
665
666 pub fn matches_fuzzy(&self, value: impl Into<String>) -> MatchQuery<S> {
669 self.matches(value).fuzziness(Fuzziness::Auto)
670 }
671
672 pub fn exists(&self) -> Query<S> {
674 exists_q(&self.path)
675 }
676}
677
678impl<S> Text<S, WithSubfields> {
679 pub fn at(path: impl Into<String>) -> Self {
680 Self::handle(path)
681 }
682
683 pub fn any_of(
688 &self,
689 values: impl IntoIterator<Item = impl FlussoValue<kind::Keyword>>,
690 ) -> TermsQuery<S> {
691 self.keyword().any_of(values)
692 }
693
694 pub fn keyword(&self) -> Keyword<S, NoSubfields> {
699 Keyword::leaf(format!("{}.keyword", self.path))
700 }
701
702 pub fn keyword_lowercase(&self) -> Keyword<S, NoSubfields> {
706 Keyword::leaf(format!("{}.keyword_lowercase", self.path))
707 }
708}
709
710impl<S: FlussoDocument> Sortable for Text<S, WithSubfields> {
714 fn asc(&self) -> Sort {
715 self.keyword_lowercase().asc()
716 }
717 fn desc(&self) -> Sort {
718 self.keyword_lowercase().desc()
719 }
720}
721
722impl<S> Text<S, NoSubfields> {
723 pub fn leaf(path: impl Into<String>) -> Self {
726 Self::handle(path)
727 }
728}
729
730impl<S> Text<S, MapKey> {
731 pub(crate) fn map_key(path: impl Into<String>) -> Self {
734 Self::handle(path)
735 }
736}
737
738pub fn multi_match<S, Sub>(
742 query: impl Into<String>,
743 fields: impl IntoIterator<Item = Text<S, Sub>>,
744) -> MultiMatchQuery<S> {
745 MultiMatchQuery {
746 query: query.into(),
747 fields: fields.into_iter().map(|f| f.field_spec()).collect(),
748 opts: Map::new(),
749 common: Common::default(),
750 _scope: PhantomData,
751 }
752}
753
754#[derive(Debug, Clone)]
758pub struct MultiMatchQuery<S = Root> {
759 query: String,
760 fields: Vec<String>,
761 opts: Map<String, Value>,
762 common: Common,
763 _scope: PhantomData<fn() -> S>,
764}
765
766impl<S> MultiMatchQuery<S> {
767 fn set(mut self, key: &str, value: Value) -> Self {
768 self.opts.insert(key.to_string(), value);
769 self
770 }
771
772 #[must_use]
774 pub fn match_type(self, match_type: MultiMatchType) -> Self {
775 self.set("type", Value::String(match_type.as_str().to_string()))
776 }
777
778 #[must_use]
780 pub fn operator(self, operator: Operator) -> Self {
781 self.set("operator", Value::String(operator.as_str().to_string()))
782 }
783
784 #[must_use]
786 pub fn fuzziness(self, fuzziness: Fuzziness) -> Self {
787 self.set("fuzziness", fuzziness.to_value())
788 }
789
790 #[must_use]
792 pub fn tie_breaker(self, tie_breaker: f32) -> Self {
793 self.set("tie_breaker", Value::from(tie_breaker))
794 }
795
796 #[must_use]
799 pub fn minimum_should_match(self, value: impl Into<MinimumShouldMatch>) -> Self {
800 self.set("minimum_should_match", value.into().to_value())
801 }
802
803 common_opts!(common);
804}
805
806impl<S> AsQuery<S> for MultiMatchQuery<S> {
807 fn into_query(self) -> Option<Query<S>> {
808 let mut body = self.opts;
809 body.insert("query".to_string(), Value::String(self.query));
810 body.insert(
811 "fields".to_string(),
812 Value::Array(self.fields.into_iter().map(Value::String).collect()),
813 );
814 self.common.write(&mut body);
815 Some(wrap("multi_match", body))
816 }
817}