summa_core/query/
wildcard.rs1use super::term_pattern::{TermPatternQuery, check_length};
3#[cfg(feature = "sync")]
4use super::traits::Scorer;
5use super::traits::{CountFuture, Query, ScorerFuture};
6use crate::dsl::Field;
7use crate::segment::SegmentReader;
8use crate::{Error, Result};
9
10#[derive(Debug, Clone)]
17pub struct WildcardQuery(TermPatternQuery);
18
19impl WildcardQuery {
20 pub fn new(field: Field, pattern: impl AsRef<str>) -> Result<Self> {
22 let source = pattern.as_ref();
23 check_length(source, "wildcard")?;
24 let mut expression = String::new();
25 let mut prefix = String::new();
26 let mut suffix = String::new();
27 let mut stars = 0usize;
28 let mut questions = false;
29 let mut literal_prefix = true;
30 let mut characters = source.chars();
31 while let Some(character) = characters.next() {
32 match character {
33 '*' => {
34 stars += 1;
35 expression.push_str(".*");
36 literal_prefix = false;
37 }
38 '?' => {
39 questions = true;
40 expression.push('.');
41 literal_prefix = false;
42 }
43 _ => {
44 let literal = if character == '\\' {
45 characters.next().ok_or_else(|| {
46 Error::Query("wildcard pattern ends with an escape".into())
47 })?
48 } else {
49 character
50 };
51 expression.push_str(®ex::escape(literal.encode_utf8(&mut [0; 4])));
52 if literal_prefix {
53 prefix.push(literal);
54 } else {
55 suffix.push(literal);
56 }
57 }
58 }
59 }
60 if stars == 1 && !questions {
61 return Ok(Self(TermPatternQuery::single_star(
62 field,
63 source,
64 prefix.into_bytes(),
65 suffix.into_bytes(),
66 )));
67 }
68 Ok(Self(TermPatternQuery::compile(
69 field,
70 source,
71 &expression,
72 vec![prefix.into_bytes()],
73 "wildcard",
74 )?))
75 }
76
77 pub fn text(field: Field, pattern: &str) -> Result<Self> {
79 check_length(pattern, "wildcard")?;
80 Self::new(field, pattern.to_lowercase())
81 }
82}
83
84impl std::fmt::Display for WildcardQuery {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 self.0.fmt(f)
87 }
88}
89impl Query for WildcardQuery {
90 fn scorer<'a>(&self, reader: &'a SegmentReader, limit: usize) -> ScorerFuture<'a> {
91 self.0.scorer(reader, limit)
92 }
93 #[cfg(feature = "sync")]
94 fn scorer_sync<'a>(
95 &self,
96 reader: &'a SegmentReader,
97 limit: usize,
98 ) -> Result<Box<dyn Scorer + 'a>> {
99 self.0.scorer_sync(reader, limit)
100 }
101 fn count_estimate<'a>(&self, reader: &'a SegmentReader) -> CountFuture<'a> {
102 self.0.count_estimate(reader)
103 }
104 fn is_filter(&self) -> bool {
105 true
106 }
107}