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
463#[derive(Debug, Clone)]
476pub struct Enum<S = Root, Sub = WithSubfields> {
477 path: String,
478 _marker: PhantomData<fn() -> (S, Sub)>,
479}
480
481impl<S, Sub> Enum<S, Sub> {
482 fn handle(path: impl Into<String>) -> Self {
483 Self {
484 path: path.into(),
485 _marker: PhantomData,
486 }
487 }
488
489 pub fn keyword(&self) -> Keyword<S, Sub> {
494 Keyword::handle(self.path.clone())
495 }
496
497 pub fn eq(&self, value: impl FlussoValue<kind::Keyword>) -> TermQuery<S> {
500 self.keyword().eq(value)
501 }
502
503 pub fn any_of(
505 &self,
506 values: impl IntoIterator<Item = impl FlussoValue<kind::Keyword>>,
507 ) -> TermsQuery<S> {
508 self.keyword().any_of(values)
509 }
510
511 pub fn exists(&self) -> Query<S> {
513 self.keyword().exists()
514 }
515}
516
517impl<S> Enum<S, WithSubfields> {
518 pub fn at(path: impl Into<String>) -> Self {
520 Self::handle(path)
521 }
522}
523
524impl<S> Enum<S, NoSubfields> {
525 pub fn leaf(path: impl Into<String>) -> Self {
527 Self::handle(path)
528 }
529}
530
531impl<S: FlussoDocument, Sub> Sortable for Enum<S, Sub> {
534 fn asc(&self) -> Sort {
535 Sort::field::<S>(
536 &format!("{}.{ENUM_SORT_SUBFIELD}", self.path),
537 SortOrder::Asc,
538 )
539 }
540 fn desc(&self) -> Sort {
541 Sort::field::<S>(
542 &format!("{}.{ENUM_SORT_SUBFIELD}", self.path),
543 SortOrder::Desc,
544 )
545 }
546}
547
548const ENUM_SORT_SUBFIELD: &str = "sort";
551
552impl<S> Keyword<S, MapKey> {
553 pub(crate) fn map_key(path: impl Into<String>) -> Self {
556 Self::handle(path)
557 }
558}
559
560impl<S> Keyword<S, WithSubfields> {
561 pub fn at(path: impl Into<String>) -> Self {
562 Self::handle(path)
563 }
564
565 pub fn text(&self) -> Text<S, NoSubfields> {
569 Text::leaf(format!("{}.text", self.path))
570 }
571
572 pub fn keyword_lowercase(&self) -> Keyword<S, NoSubfields> {
576 Keyword::leaf(format!("{}.keyword_lowercase", self.path))
577 }
578}
579
580impl<S> Keyword<S, NoSubfields> {
581 pub fn leaf(path: impl Into<String>) -> Self {
584 Self::handle(path)
585 }
586}
587
588#[derive(Debug, Clone)]
594pub struct MatchQuery<S = Root> {
595 wrapper: &'static str,
596 path: String,
597 value: String,
598 opts: Map<String, Value>,
599 common: Common,
600 _scope: PhantomData<fn() -> S>,
601}
602
603impl<S> MatchQuery<S> {
604 fn new(wrapper: &'static str, path: &str, value: String) -> Self {
605 Self {
606 wrapper,
607 path: path.to_string(),
608 value,
609 opts: Map::new(),
610 common: Common::default(),
611 _scope: PhantomData,
612 }
613 }
614
615 fn set(mut self, key: &str, value: Value) -> Self {
616 self.opts.insert(key.to_string(), value);
617 self
618 }
619
620 #[must_use]
622 pub fn fuzziness(self, fuzziness: Fuzziness) -> Self {
623 self.set("fuzziness", fuzziness.to_value())
624 }
625
626 #[must_use]
629 pub fn operator(self, operator: Operator) -> Self {
630 self.set("operator", Value::String(operator.as_str().to_string()))
631 }
632
633 #[must_use]
636 pub fn minimum_should_match(self, value: impl Into<MinimumShouldMatch>) -> Self {
637 self.set("minimum_should_match", value.into().to_value())
638 }
639
640 #[must_use]
642 pub fn prefix_length(self, prefix_length: u32) -> Self {
643 self.set("prefix_length", Value::from(prefix_length))
644 }
645
646 #[must_use]
648 pub fn max_expansions(self, max_expansions: u32) -> Self {
649 self.set("max_expansions", Value::from(max_expansions))
650 }
651
652 #[must_use]
654 pub fn analyzer(self, analyzer: impl Into<String>) -> Self {
655 self.set("analyzer", Value::String(analyzer.into()))
656 }
657
658 #[must_use]
660 pub fn slop(self, slop: u32) -> Self {
661 self.set("slop", Value::from(slop))
662 }
663
664 #[must_use]
667 pub fn zero_terms_query(self, value: ZeroTermsQuery) -> Self {
668 self.set(
669 "zero_terms_query",
670 Value::String(value.as_str().to_string()),
671 )
672 }
673
674 #[must_use]
676 pub fn lenient(self, lenient: bool) -> Self {
677 self.set("lenient", Value::Bool(lenient))
678 }
679
680 common_opts!(common);
681}
682
683impl<S> AsQuery<S> for MatchQuery<S> {
684 fn into_query(self) -> Option<Query<S>> {
685 let mut opts = self.opts;
686 self.common.write(&mut opts);
687 Some(keyed_value_query(
688 self.wrapper,
689 &self.path,
690 "query",
691 Value::String(self.value),
692 opts,
693 ))
694 }
695}
696
697#[derive(Debug, Clone)]
701pub struct Text<S = Root, Sub = WithSubfields> {
702 path: String,
703 boost: Option<f32>,
704 _marker: PhantomData<fn() -> (S, Sub)>,
705}
706
707impl<S, Sub> Text<S, Sub> {
708 fn handle(path: impl Into<String>) -> Self {
709 Self {
710 path: path.into(),
711 boost: None,
712 _marker: PhantomData,
713 }
714 }
715
716 #[must_use]
720 pub fn boosted(mut self, weight: f32) -> Self {
721 self.boost = Some(weight);
722 self
723 }
724
725 pub(crate) fn field_spec(&self) -> String {
728 match self.boost {
729 Some(weight) => format!("{}^{weight}", self.path),
730 None => self.path.clone(),
731 }
732 }
733
734 pub fn matches(&self, value: impl Into<String>) -> MatchQuery<S> {
736 MatchQuery::new("match", &self.path, value.into())
737 }
738
739 pub fn match_phrase(&self, value: impl Into<String>) -> MatchQuery<S> {
741 MatchQuery::new("match_phrase", &self.path, value.into())
742 }
743
744 pub fn match_phrase_prefix(&self, value: impl Into<String>) -> MatchQuery<S> {
746 MatchQuery::new("match_phrase_prefix", &self.path, value.into())
747 }
748
749 pub fn match_bool_prefix(&self, value: impl Into<String>) -> MatchQuery<S> {
752 MatchQuery::new("match_bool_prefix", &self.path, value.into())
753 }
754
755 pub fn matches_fuzzy(&self, value: impl Into<String>) -> MatchQuery<S> {
758 self.matches(value).fuzziness(Fuzziness::Auto)
759 }
760
761 pub fn exists(&self) -> Query<S> {
763 exists_q(&self.path)
764 }
765}
766
767impl<S> Text<S, WithSubfields> {
768 pub fn at(path: impl Into<String>) -> Self {
769 Self::handle(path)
770 }
771
772 pub fn any_of(
777 &self,
778 values: impl IntoIterator<Item = impl FlussoValue<kind::Keyword>>,
779 ) -> TermsQuery<S> {
780 self.keyword().any_of(values)
781 }
782
783 pub fn keyword(&self) -> Keyword<S, NoSubfields> {
788 Keyword::leaf(format!("{}.keyword", self.path))
789 }
790
791 pub fn keyword_lowercase(&self) -> Keyword<S, NoSubfields> {
795 Keyword::leaf(format!("{}.keyword_lowercase", self.path))
796 }
797}
798
799impl<S: FlussoDocument> Sortable for Text<S, WithSubfields> {
803 fn asc(&self) -> Sort {
804 self.keyword_lowercase().asc()
805 }
806 fn desc(&self) -> Sort {
807 self.keyword_lowercase().desc()
808 }
809}
810
811impl<S> Text<S, NoSubfields> {
812 pub fn leaf(path: impl Into<String>) -> Self {
815 Self::handle(path)
816 }
817}
818
819impl<S> Text<S, MapKey> {
820 pub(crate) fn map_key(path: impl Into<String>) -> Self {
823 Self::handle(path)
824 }
825}
826
827pub fn multi_match<S, Sub>(
831 query: impl Into<String>,
832 fields: impl IntoIterator<Item = Text<S, Sub>>,
833) -> MultiMatchQuery<S> {
834 MultiMatchQuery {
835 query: query.into(),
836 fields: fields.into_iter().map(|f| f.field_spec()).collect(),
837 opts: Map::new(),
838 common: Common::default(),
839 _scope: PhantomData,
840 }
841}
842
843#[derive(Debug, Clone)]
847pub struct MultiMatchQuery<S = Root> {
848 query: String,
849 fields: Vec<String>,
850 opts: Map<String, Value>,
851 common: Common,
852 _scope: PhantomData<fn() -> S>,
853}
854
855impl<S> MultiMatchQuery<S> {
856 fn set(mut self, key: &str, value: Value) -> Self {
857 self.opts.insert(key.to_string(), value);
858 self
859 }
860
861 #[must_use]
863 pub fn match_type(self, match_type: MultiMatchType) -> Self {
864 self.set("type", Value::String(match_type.as_str().to_string()))
865 }
866
867 #[must_use]
869 pub fn operator(self, operator: Operator) -> Self {
870 self.set("operator", Value::String(operator.as_str().to_string()))
871 }
872
873 #[must_use]
875 pub fn fuzziness(self, fuzziness: Fuzziness) -> Self {
876 self.set("fuzziness", fuzziness.to_value())
877 }
878
879 #[must_use]
881 pub fn tie_breaker(self, tie_breaker: f32) -> Self {
882 self.set("tie_breaker", Value::from(tie_breaker))
883 }
884
885 #[must_use]
888 pub fn minimum_should_match(self, value: impl Into<MinimumShouldMatch>) -> Self {
889 self.set("minimum_should_match", value.into().to_value())
890 }
891
892 common_opts!(common);
893}
894
895impl<S> AsQuery<S> for MultiMatchQuery<S> {
896 fn into_query(self) -> Option<Query<S>> {
897 let mut body = self.opts;
898 body.insert("query".to_string(), Value::String(self.query));
899 body.insert(
900 "fields".to_string(),
901 Value::Array(self.fields.into_iter().map(Value::String).collect()),
902 );
903 self.common.write(&mut body);
904 Some(wrap("multi_match", body))
905 }
906}