elasticsearch_dsl/search/queries/full_text/multi_match_query.rs
1use crate::search::*;
2use crate::util::*;
3
4/// Returns documents that match a provided text, number, date or boolean value.
5/// The provided text is analyzed before matching.
6///
7/// The `match` query is the standard query for performing a full-text search,
8/// including options for fuzzy matching.
9///
10/// To create a Match query with numeric values:
11/// ```
12/// # use elasticsearch_dsl::queries::*;
13/// # use elasticsearch_dsl::queries::params::*;
14/// # let query =
15/// Query::multi_match(vec!["test"], "search text")
16/// .boost(2)
17/// .name("test");
18/// ```
19/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html>
20#[derive(Debug, Clone, PartialEq, Serialize)]
21#[serde(remote = "Self")]
22pub struct MultiMatchQuery {
23 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
24 fields: Vec<String>,
25
26 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
27 r#type: Option<TextQueryType>,
28
29 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
30 tie_breaker: Option<f32>,
31
32 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
33 query: Text,
34
35 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
36 analyzer: Option<String>,
37
38 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
39 auto_generate_synonyms_phrase_query: Option<bool>,
40
41 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
42 fuzziness: Option<Fuzziness>,
43
44 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
45 max_expansions: Option<u8>,
46
47 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
48 prefix_length: Option<u8>,
49
50 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
51 fuzzy_transpositions: Option<bool>,
52
53 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
54 fuzzy_rewrite: Option<Rewrite>,
55
56 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
57 lenient: Option<bool>,
58
59 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
60 operator: Option<Operator>,
61
62 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
63 minimum_should_match: Option<String>,
64
65 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
66 zero_terms_query: Option<ZeroTermsQuery>,
67
68 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
69 boost: Option<f32>,
70
71 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
72 _name: Option<String>,
73}
74
75impl Query {
76 /// Creates an instance of [`MultiMatchQuery`]
77 ///
78 /// - `fields` - Fields you wish to search.
79 /// - `query` - Text, number, boolean value or date you wish to find in the provided
80 /// `<field>`. The `match` query
81 /// [analyzes](https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis.html)
82 /// any provided text before performing a search. This means the `match`
83 /// query can search
84 /// [`text`](https://www.elastic.co/guide/en/elasticsearch/reference/current/text.html)
85 /// fields for analyzed tokens rather than an exact term.
86 pub fn multi_match<F, S>(fields: F, query: S) -> MultiMatchQuery
87 where
88 F: IntoIterator,
89 F::Item: ToString,
90 S: Into<Text>,
91 {
92 MultiMatchQuery {
93 fields: fields.into_iter().map(|s| s.to_string()).collect(),
94 r#type: None,
95 tie_breaker: None,
96 query: query.into(),
97 analyzer: None,
98 auto_generate_synonyms_phrase_query: None,
99 fuzziness: None,
100 max_expansions: None,
101 prefix_length: None,
102 fuzzy_transpositions: None,
103 fuzzy_rewrite: None,
104 lenient: None,
105 operator: None,
106 minimum_should_match: None,
107 zero_terms_query: None,
108 boost: None,
109 _name: None,
110 }
111 }
112}
113
114impl MultiMatchQuery {
115 /// The way the multi_match query is executed internally depends on the
116 /// type parameter
117 pub fn r#type(mut self, r#type: TextQueryType) -> Self {
118 self.r#type = Some(r#type);
119 self
120 }
121
122 /// Floating point number between `0` and `1.0` used to increase the
123 /// [relevance scores](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html#relevance-scores)
124 /// of documents matching multiple query clauses. Defaults to `0.0`.
125 ///
126 /// You can use the `tie_breaker` value to assign higher relevance scores to
127 /// documents that contain the same term in multiple fields than documents that
128 /// contain this term in only the best of those multiple fields, without
129 /// confusing this with the better case of two different terms in the multiple
130 /// fields.
131 ///
132 /// If a document matches multiple clauses, the `dis_max` query calculates
133 /// the relevance score for the document as follows:
134 /// 1. Take the relevance score from a matching clause with the highest score.
135 /// 2. Multiply the score from any other matching clauses by the tie_breaker value.
136 /// 3. Add the highest score to the multiplied scores.
137 ///
138 /// If the `tie_breaker` value is greater than `0.0`, all matching clauses
139 /// count, but the clause with the highest score counts most.
140 pub fn tie_breaker(mut self, tie_breaker: f32) -> Self {
141 self.tie_breaker = Some(tie_breaker);
142 self
143 }
144
145 /// [Analyzer](https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis.html)
146 /// used to convert the text in the `query` value into tokens. Defaults to the
147 /// [index-time analyzer](https://www.elastic.co/guide/en/elasticsearch/reference/current/specify-analyzer.html#specify-index-time-analyzer)
148 /// mapped for the `<field>`. If no analyzer is mapped, the index’s default analyzer is used.
149 pub fn analyzer<T>(mut self, analyzer: T) -> Self
150 where
151 T: ToString,
152 {
153 self.analyzer = Some(analyzer.to_string());
154 self
155 }
156
157 /// If `true`,
158 /// [match phrase](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query-phrase.html)
159 /// queries are automatically created for multi-term synonyms. Defaults to `true`.
160 ///
161 /// See [Use synonyms with match query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html#query-dsl-match-query-synonyms)
162 /// for an example.
163 pub fn auto_generate_synonyms_phrase_query(
164 mut self,
165 auto_generate_synonyms_phrase_query: bool,
166 ) -> Self {
167 self.auto_generate_synonyms_phrase_query = Some(auto_generate_synonyms_phrase_query);
168 self
169 }
170
171 /// Maximum edit distance allowed for matching.
172 /// See [Fuzziness](Fuzziness)
173 /// for valid values and more information. See
174 /// [Fuzziness in the match query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html#query-dsl-match-query-fuzziness)
175 /// for an example.
176 pub fn fuzziness<T>(mut self, fuzziness: T) -> Self
177 where
178 T: Into<Fuzziness>,
179 {
180 self.fuzziness = Some(fuzziness.into());
181 self
182 }
183
184 /// Maximum number of terms to which the query will expand.
185 /// Defaults to `50`.
186 pub fn max_expansions(mut self, max_expansions: u8) -> Self {
187 self.max_expansions = Some(max_expansions);
188 self
189 }
190
191 /// Number of beginning characters left unchanged for fuzzy matching.
192 /// Defaults to `0`.
193 pub fn prefix_length(mut self, prefix_length: u8) -> Self {
194 self.prefix_length = Some(prefix_length);
195 self
196 }
197
198 /// If `true`, edits for fuzzy matching include transpositions of two
199 /// adjacent characters (ab → ba). Defaults to `true`.
200 pub fn fuzzy_transpositions(mut self, fuzzy_transpositions: bool) -> Self {
201 self.fuzzy_transpositions = Some(fuzzy_transpositions);
202 self
203 }
204
205 /// Method used to rewrite the query. See the
206 /// [`rewrite` parameter](Rewrite) for valid values and
207 /// more information.
208 ///
209 /// If the `fuzziness` parameter is not `0`, the match query uses a
210 /// `fuzzy_rewrite` method of `top_terms_blended_freqs_${max_expansions}`
211 /// by default.
212 pub fn fuzzy_rewrite(mut self, fuzzy_rewrite: Rewrite) -> Self {
213 self.fuzzy_rewrite = Some(fuzzy_rewrite);
214 self
215 }
216
217 /// If `true`, format-based errors, such as providing a text `query`
218 /// value for a
219 /// [numeric](https://www.elastic.co/guide/en/elasticsearch/reference/current/number.html)
220 /// field, are ignored. Defaults to `false`.
221 pub fn lenient(mut self, lenient: bool) -> Self {
222 self.lenient = Some(lenient);
223 self
224 }
225
226 /// Boolean logic used to interpret text in the `query` value
227 pub fn operator(mut self, operator: Operator) -> Self {
228 self.operator = Some(operator);
229 self
230 }
231
232 /// Minimum number of clauses that must match for a document to be returned. See the
233 /// `minimum_should_match` parameter for valid values and more information.
234 pub fn minimum_should_match<T>(mut self, minimum_should_match: T) -> Self
235 where
236 T: ToString,
237 {
238 self.minimum_should_match = Some(minimum_should_match.to_string());
239 self
240 }
241
242 /// Indicates whether no documents are returned if the `analyzer` removes
243 /// all tokens, such as when using a `stop` filter.
244 pub fn zero_terms_query(mut self, zero_terms_query: ZeroTermsQuery) -> Self {
245 self.zero_terms_query = Some(zero_terms_query);
246 self
247 }
248
249 add_boost_and_name!();
250}
251
252impl ShouldSkip for MultiMatchQuery {
253 fn should_skip(&self) -> bool {
254 self.query.should_skip()
255 }
256}
257
258serialize_with_root!("multi_match": MultiMatchQuery);
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 #[test]
265 fn serialization() {
266 assert_serialize_query(
267 Query::multi_match(["test"], "search text"),
268 json!({
269 "multi_match": {
270 "query": "search text",
271 "fields": ["test"],
272 }
273 }),
274 );
275
276 assert_serialize_query(
277 Query::multi_match(["test"], "search text")
278 .r#type(TextQueryType::BestFields)
279 .tie_breaker(0.2)
280 .analyzer("search_time_analyzer")
281 .auto_generate_synonyms_phrase_query(true)
282 .fuzziness(23)
283 .max_expansions(2)
284 .prefix_length(3)
285 .fuzzy_transpositions(false)
286 .fuzzy_rewrite(Rewrite::ConstantScoreBoolean)
287 .lenient(true)
288 .operator(Operator::And)
289 .minimum_should_match("22")
290 .zero_terms_query(ZeroTermsQuery::None)
291 .boost(2)
292 .name("test"),
293 json!({
294 "multi_match": {
295 "query": "search text",
296 "fields": ["test"],
297 "type": "best_fields",
298 "tie_breaker": 0.2,
299 "analyzer": "search_time_analyzer",
300 "auto_generate_synonyms_phrase_query": true,
301 "fuzziness": 23,
302 "max_expansions": 2,
303 "prefix_length": 3,
304 "fuzzy_transpositions": false,
305 "fuzzy_rewrite": "constant_score_boolean",
306 "lenient": true,
307 "operator": "AND",
308 "minimum_should_match": "22",
309 "zero_terms_query": "none",
310 "boost": 2.0,
311 "_name": "test",
312 }
313 }),
314 );
315 }
316}