1use std::marker::PhantomData;
11
12use serde_json::{Map, Value};
13
14use super::{
15 Common, FlussoValue, Fuzziness, MinimumShouldMatch, MultiMatchType, Operator, Sort, SortOrder,
16 TermsQuery, ZeroTermsQuery, common_opts, exists_q, keyed_value_query, 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<Value>,
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: Fuzziness) -> Self {
302 self.fuzziness = Some(fuzziness.to_value());
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(), 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)]
363pub enum WithSubfields {}
364
365#[derive(Debug)]
370pub enum NoSubfields {}
371
372#[derive(Debug, Clone)]
376pub struct Keyword<S = Root, Sub = WithSubfields> {
377 path: String,
378 _marker: PhantomData<fn() -> (S, Sub)>,
379}
380
381impl<S, Sub> Keyword<S, Sub> {
382 fn handle(path: impl Into<String>) -> Self {
383 Self {
384 path: path.into(),
385 _marker: PhantomData,
386 }
387 }
388
389 pub fn eq(&self, value: impl FlussoValue<kind::Keyword>) -> TermQuery<S> {
393 TermQuery::new(&self.path, keyword_term(&value))
394 }
395
396 pub fn any_of(
398 &self,
399 values: impl IntoIterator<Item = impl FlussoValue<kind::Keyword>>,
400 ) -> TermsQuery<S> {
401 let array = values.into_iter().map(|v| keyword_term(&v)).collect();
402 TermsQuery::new(&self.path, array)
403 }
404
405 pub fn prefix(&self, value: impl Into<String>) -> PrefixQuery<S> {
407 PrefixQuery::new(&self.path, value.into())
408 }
409
410 pub fn wildcard(&self, pattern: impl Into<String>) -> WildcardQuery<S> {
412 WildcardQuery::new(&self.path, pattern.into())
413 }
414
415 pub fn regexp(&self, pattern: impl Into<String>) -> RegexpQuery<S> {
417 RegexpQuery::new(&self.path, pattern.into())
418 }
419
420 pub fn fuzzy(&self, value: impl Into<String>) -> FuzzyQuery<S> {
422 FuzzyQuery::new(&self.path, value.into())
423 }
424
425 pub fn exists(&self) -> Query<S> {
427 exists_q(&self.path)
428 }
429
430 pub fn asc(&self) -> Sort {
431 Sort::new(&self.path, SortOrder::Asc)
432 }
433
434 pub fn desc(&self) -> Sort {
435 Sort::new(&self.path, SortOrder::Desc)
436 }
437}
438
439impl<S> Keyword<S, WithSubfields> {
440 pub fn at(path: impl Into<String>) -> Self {
441 Self::handle(path)
442 }
443
444 pub fn text(&self) -> Text<S, NoSubfields> {
448 Text::leaf(format!("{}.text", self.path))
449 }
450
451 pub fn keyword_lowercase(&self) -> Keyword<S, NoSubfields> {
455 Keyword::leaf(format!("{}.keyword_lowercase", self.path))
456 }
457}
458
459impl<S> Keyword<S, NoSubfields> {
460 pub fn leaf(path: impl Into<String>) -> Self {
463 Self::handle(path)
464 }
465}
466
467#[derive(Debug, Clone)]
473pub struct MatchQuery<S = Root> {
474 wrapper: &'static str,
475 path: String,
476 value: String,
477 opts: Map<String, Value>,
478 common: Common,
479 _scope: PhantomData<fn() -> S>,
480}
481
482impl<S> MatchQuery<S> {
483 fn new(wrapper: &'static str, path: &str, value: String) -> Self {
484 Self {
485 wrapper,
486 path: path.to_string(),
487 value,
488 opts: Map::new(),
489 common: Common::default(),
490 _scope: PhantomData,
491 }
492 }
493
494 fn set(mut self, key: &str, value: Value) -> Self {
495 self.opts.insert(key.to_string(), value);
496 self
497 }
498
499 #[must_use]
501 pub fn fuzziness(self, fuzziness: Fuzziness) -> Self {
502 self.set("fuzziness", fuzziness.to_value())
503 }
504
505 #[must_use]
508 pub fn operator(self, operator: Operator) -> Self {
509 self.set("operator", Value::String(operator.as_str().to_string()))
510 }
511
512 #[must_use]
515 pub fn minimum_should_match(self, value: impl Into<MinimumShouldMatch>) -> Self {
516 self.set("minimum_should_match", value.into().to_value())
517 }
518
519 #[must_use]
521 pub fn prefix_length(self, prefix_length: u32) -> Self {
522 self.set("prefix_length", Value::from(prefix_length))
523 }
524
525 #[must_use]
527 pub fn max_expansions(self, max_expansions: u32) -> Self {
528 self.set("max_expansions", Value::from(max_expansions))
529 }
530
531 #[must_use]
533 pub fn analyzer(self, analyzer: impl Into<String>) -> Self {
534 self.set("analyzer", Value::String(analyzer.into()))
535 }
536
537 #[must_use]
539 pub fn slop(self, slop: u32) -> Self {
540 self.set("slop", Value::from(slop))
541 }
542
543 #[must_use]
546 pub fn zero_terms_query(self, value: ZeroTermsQuery) -> Self {
547 self.set(
548 "zero_terms_query",
549 Value::String(value.as_str().to_string()),
550 )
551 }
552
553 #[must_use]
555 pub fn lenient(self, lenient: bool) -> Self {
556 self.set("lenient", Value::Bool(lenient))
557 }
558
559 common_opts!(common);
560}
561
562impl<S> AsQuery<S> for MatchQuery<S> {
563 fn into_query(self) -> Option<Query<S>> {
564 let mut opts = self.opts;
565 self.common.write(&mut opts);
566 Some(keyed_value_query(
567 self.wrapper,
568 &self.path,
569 "query",
570 Value::String(self.value),
571 opts,
572 ))
573 }
574}
575
576#[derive(Debug, Clone)]
580pub struct Text<S = Root, Sub = WithSubfields> {
581 path: String,
582 boost: Option<f32>,
583 _marker: PhantomData<fn() -> (S, Sub)>,
584}
585
586impl<S, Sub> Text<S, Sub> {
587 fn handle(path: impl Into<String>) -> Self {
588 Self {
589 path: path.into(),
590 boost: None,
591 _marker: PhantomData,
592 }
593 }
594
595 #[must_use]
599 pub fn boosted(mut self, weight: f32) -> Self {
600 self.boost = Some(weight);
601 self
602 }
603
604 pub(crate) fn field_spec(&self) -> String {
607 match self.boost {
608 Some(weight) => format!("{}^{weight}", self.path),
609 None => self.path.clone(),
610 }
611 }
612
613 pub fn matches(&self, value: impl Into<String>) -> MatchQuery<S> {
615 MatchQuery::new("match", &self.path, value.into())
616 }
617
618 pub fn match_phrase(&self, value: impl Into<String>) -> MatchQuery<S> {
620 MatchQuery::new("match_phrase", &self.path, value.into())
621 }
622
623 pub fn match_phrase_prefix(&self, value: impl Into<String>) -> MatchQuery<S> {
625 MatchQuery::new("match_phrase_prefix", &self.path, value.into())
626 }
627
628 pub fn match_bool_prefix(&self, value: impl Into<String>) -> MatchQuery<S> {
631 MatchQuery::new("match_bool_prefix", &self.path, value.into())
632 }
633
634 pub fn matches_fuzzy(&self, value: impl Into<String>) -> MatchQuery<S> {
637 self.matches(value).fuzziness(Fuzziness::Auto)
638 }
639
640 pub fn exists(&self) -> Query<S> {
642 exists_q(&self.path)
643 }
644}
645
646impl<S> Text<S, WithSubfields> {
647 pub fn at(path: impl Into<String>) -> Self {
648 Self::handle(path)
649 }
650
651 pub fn any_of(
656 &self,
657 values: impl IntoIterator<Item = impl FlussoValue<kind::Keyword>>,
658 ) -> TermsQuery<S> {
659 self.keyword().any_of(values)
660 }
661
662 pub fn keyword(&self) -> Keyword<S, NoSubfields> {
667 Keyword::leaf(format!("{}.keyword", self.path))
668 }
669
670 pub fn keyword_lowercase(&self) -> Keyword<S, NoSubfields> {
674 Keyword::leaf(format!("{}.keyword_lowercase", self.path))
675 }
676
677 pub fn asc(&self) -> Sort {
681 self.keyword_lowercase().asc()
682 }
683
684 pub fn desc(&self) -> Sort {
686 self.keyword_lowercase().desc()
687 }
688}
689
690impl<S> Text<S, NoSubfields> {
691 pub fn leaf(path: impl Into<String>) -> Self {
694 Self::handle(path)
695 }
696}
697
698pub fn multi_match<S, Sub>(
702 query: impl Into<String>,
703 fields: impl IntoIterator<Item = Text<S, Sub>>,
704) -> MultiMatchQuery<S> {
705 MultiMatchQuery {
706 query: query.into(),
707 fields: fields.into_iter().map(|f| f.field_spec()).collect(),
708 opts: Map::new(),
709 common: Common::default(),
710 _scope: PhantomData,
711 }
712}
713
714#[derive(Debug, Clone)]
718pub struct MultiMatchQuery<S = Root> {
719 query: String,
720 fields: Vec<String>,
721 opts: Map<String, Value>,
722 common: Common,
723 _scope: PhantomData<fn() -> S>,
724}
725
726impl<S> MultiMatchQuery<S> {
727 fn set(mut self, key: &str, value: Value) -> Self {
728 self.opts.insert(key.to_string(), value);
729 self
730 }
731
732 #[must_use]
734 pub fn match_type(self, match_type: MultiMatchType) -> Self {
735 self.set("type", Value::String(match_type.as_str().to_string()))
736 }
737
738 #[must_use]
740 pub fn operator(self, operator: Operator) -> Self {
741 self.set("operator", Value::String(operator.as_str().to_string()))
742 }
743
744 #[must_use]
746 pub fn fuzziness(self, fuzziness: Fuzziness) -> Self {
747 self.set("fuzziness", fuzziness.to_value())
748 }
749
750 #[must_use]
752 pub fn tie_breaker(self, tie_breaker: f32) -> Self {
753 self.set("tie_breaker", Value::from(tie_breaker))
754 }
755
756 #[must_use]
759 pub fn minimum_should_match(self, value: impl Into<MinimumShouldMatch>) -> Self {
760 self.set("minimum_should_match", value.into().to_value())
761 }
762
763 common_opts!(common);
764}
765
766impl<S> AsQuery<S> for MultiMatchQuery<S> {
767 fn into_query(self) -> Option<Query<S>> {
768 let mut body = self.opts;
769 body.insert("query".to_string(), Value::String(self.query));
770 body.insert(
771 "fields".to_string(),
772 Value::Array(self.fields.into_iter().map(Value::String).collect()),
773 );
774 self.common.write(&mut body);
775 Some(wrap("multi_match", body))
776 }
777}