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}