Skip to main content

opensearch_dsl/search/queries/full_text/
query_string_query.rs

1use crate::{search::*, util::*};
2
3/// Returns documents that match a provided text, number, date or boolean value.
4/// The provided text is analyzed before matching.
5///
6/// The `match` query is the standard query for performing a full-text search,
7/// including options for fuzzy matching.
8///
9/// To create a Match query with numeric values:
10/// ```
11/// # use opensearch_dsl::queries::*;
12/// # use opensearch_dsl::queries::params::*;
13/// # let query =
14/// Query::query_string("(new york city) OR (big apple)")
15///   .boost(2)
16///   .name("test");
17/// ```
18/// <https://www.elastic.co/guide/en/opensearch/reference/current/query-dsl-query-string-query.html>
19#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
20#[serde(remote = "Self")]
21pub struct QueryStringQuery {
22    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
23    query: Text,
24
25    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
26    default_field: Option<String>,
27
28    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
29    allow_leading_wildcard: Option<bool>,
30
31    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
32    analyze_wildcard: Option<bool>,
33
34    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
35    analyzer: Option<String>,
36
37    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
38    auto_generate_synonyms_phrase_query: Option<bool>,
39
40    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
41    default_operator: Option<Operator>,
42
43    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
44    enable_position_increments: Option<bool>,
45
46    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
47    fields: Vec<String>,
48
49    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
50    fuzziness: Option<Fuzziness>,
51
52    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
53    fuzzy_max_expansions: Option<u32>,
54
55    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
56    fuzzy_prefix_length: Option<u32>,
57
58    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
59    fuzzy_transpositions: Option<bool>,
60
61    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
62    lenient: Option<bool>,
63
64    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
65    max_determinized_states: Option<u32>,
66
67    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
68    minimum_should_match: Option<String>,
69
70    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
71    quote_analyzer: Option<String>,
72
73    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
74    phrase_slop: Option<u32>,
75
76    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
77    quote_field_suffix: Option<String>,
78
79    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
80    rewrite: Option<Rewrite>,
81
82    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
83    time_zone: Option<String>,
84
85    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
86    boost: Option<f32>,
87
88    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
89    _name: Option<String>,
90}
91
92impl Query {
93    /// Creates an instance of [`QueryStringQuery`]
94    ///
95    /// - `query` - Query string you wish to parse and use for search. See
96    /// [Simple query string syntax](https://www.elastic.co/guide/en/opensearch/reference/current/query-dsl-simple-query-string-query.html#simple-query-string-syntax).
97    pub fn query_string<S>(query: S) -> QueryStringQuery
98    where
99        S: Into<Text>,
100    {
101        QueryStringQuery {
102            query: query.into(),
103            fields: vec![],
104            default_operator: None,
105            analyze_wildcard: None,
106            analyzer: None,
107            auto_generate_synonyms_phrase_query: None,
108            fuzzy_transpositions: None,
109            fuzzy_max_expansions: None,
110            fuzzy_prefix_length: None,
111            quote_field_suffix: None,
112            lenient: None,
113            minimum_should_match: None,
114            allow_leading_wildcard: None,
115            default_field: None,
116            enable_position_increments: None,
117            fuzziness: None,
118            max_determinized_states: None,
119            phrase_slop: None,
120            quote_analyzer: None,
121            rewrite: None,
122            time_zone: None,
123            boost: None,
124            _name: None,
125        }
126    }
127}
128
129impl QueryStringQuery {
130    add_boost_and_name!();
131
132    /// Default field you wish to search if no field is provided in the query
133    /// string.
134    ///
135    /// Defaults to the `index.query.default_field` index setting, which has a
136    /// default value of `*`. The `*` value extracts all fields that are
137    /// eligible for term queries and filters the metadata fields. All
138    /// extracted fields are then combined to build a query if no `prefix`
139    /// is specified.
140    ///
141    /// Searching across all eligible fields does not include
142    /// [nested documents](https://www.elastic.co/guide/en/opensearch/reference/current/nested.html).
143    /// Use a [`nested` query](crate::NestedQuery) to search those documents.
144    ///
145    /// For mappings with a large number of fields, searching across all
146    /// eligible fields could be expensive.
147    ///
148    /// There is a limit on the number of fields that can be queried at once.
149    /// It is defined by the `indices.query.bool.max_clause_count`
150    /// [search setting](https://www.elastic.co/guide/en/opensearch/reference/current/search-settings.html),
151    /// which defaults to 1024.
152    pub fn default_field<S>(mut self, default_field: S) -> Self
153    where
154        S: ToString,
155    {
156        self.default_field = Some(default_field.to_string());
157        self
158    }
159
160    /// If `true`, the wildcard characters `*` and `?` are allowed as the first
161    /// character of the query string.
162    ///
163    /// Defaults to `true`.
164    pub fn allow_leading_wildcard(mut self, allow_leading_wildcard: bool) -> Self {
165        self.allow_leading_wildcard = Some(allow_leading_wildcard);
166        self
167    }
168
169    /// If `true`, the query attempts to analyze wildcard terms in the query
170    /// string.
171    ///
172    /// Defaults to `false`.
173    pub fn analyze_wildcard(mut self, analyze_wildcard: bool) -> Self {
174        self.analyze_wildcard = Some(analyze_wildcard);
175        self
176    }
177
178    /// [Analyzer](https://www.elastic.co/guide/en/opensearch/reference/current/analysis.html)
179    /// used to convert text in the query string into tokens. Defaults to the
180    /// [index-time analyzer](https://www.elastic.co/guide/en/opensearch/reference/current/specify-analyzer.html#specify-index-time-analyzer)
181    /// mapped for the `default_field`. If no analyzer is mapped, the index’s
182    /// default analyzer is used.
183    pub fn analyzer<T>(mut self, analyzer: T) -> Self
184    where
185        T: ToString,
186    {
187        self.analyzer = Some(analyzer.to_string());
188        self
189    }
190
191    /// If `true`, the parser creates a
192    /// [`match_phrase`](https://www.elastic.co/guide/en/opensearch/reference/current/query-dsl-match-query-phrase.html)
193    /// query for each
194    /// [multi-position token](https://www.elastic.co/guide/en/opensearch/reference/current/token-graphs.html#token-graphs-multi-position-tokens).
195    ///
196    /// Defaults to `true`. For examples, see
197    /// [Multi-position tokens](https://www.elastic.co/guide/en/opensearch/reference/current/query-dsl-simple-query-string-query.html#simple-query-string-synonyms).
198    pub fn auto_generate_synonyms_phrase_query(
199        mut self,
200        auto_generate_synonyms_phrase_query: bool,
201    ) -> Self {
202        self.auto_generate_synonyms_phrase_query = Some(auto_generate_synonyms_phrase_query);
203        self
204    }
205
206    /// Default boolean logic used to interpret text in the query string if no
207    /// operators are specified.
208    pub fn default_operator(mut self, default_operator: Operator) -> Self {
209        self.default_operator = Some(default_operator);
210        self
211    }
212
213    /// If `true`, enable position increments in queries constructed from a
214    /// `query_string` search.
215    ///
216    /// Defaults to `true`.
217    pub fn enable_position_increments(mut self, enable_position_increments: bool) -> Self {
218        self.enable_position_increments = Some(enable_position_increments);
219        self
220    }
221
222    /// Array of fields you wish to search.
223    ///
224    /// You can use this parameter query to search across multiple fields. See
225    /// [Search multiple fields](https://www.elastic.co/guide/en/opensearch/reference/current/query-dsl-query-string-query.html#query-string-multi-field).
226    pub fn fields<I>(mut self, fields: I) -> Self
227    where
228        I: IntoIterator,
229        I::Item: ToString,
230    {
231        self.fields = fields.into_iter().map(|x| x.to_string()).collect();
232        self
233    }
234
235    /// Maximum edit distance allowed for fuzzy matching. For fuzzy syntax, see
236    /// [`Fuzziness`].
237    pub fn fuzziness<T>(mut self, fuzziness: T) -> Self
238    where
239        T: Into<Fuzziness>,
240    {
241        self.fuzziness = Some(fuzziness.into());
242        self
243    }
244
245    /// Maximum number of terms to which the query expands for fuzzy matching.
246    ///
247    /// Defaults to `50`.
248    pub fn fuzzy_max_expansions(mut self, fuzzy_max_expansions: u32) -> Self {
249        self.fuzzy_max_expansions = Some(fuzzy_max_expansions);
250        self
251    }
252
253    /// Number of beginning characters left unchanged for fuzzy matching.
254    ///
255    /// Defaults to `0`.
256    pub fn fuzzy_prefix_length(mut self, fuzzy_prefix_length: u32) -> Self {
257        self.fuzzy_prefix_length = Some(fuzzy_prefix_length);
258        self
259    }
260
261    /// If `true`, edits for fuzzy matching include transpositions of two
262    /// adjacent characters (ab → ba).
263    ///
264    /// Defaults to `true`.
265    pub fn fuzzy_transpositions(mut self, fuzzy_transpositions: bool) -> Self {
266        self.fuzzy_transpositions = Some(fuzzy_transpositions);
267        self
268    }
269
270    /// If `true`, format-based errors, such as providing a text `query`
271    /// value for a
272    /// [numeric](https://www.elastic.co/guide/en/opensearch/reference/current/number.html)
273    /// field, are ignored.
274    ///
275    /// Defaults to `false`.
276    pub fn lenient(mut self, lenient: bool) -> Self {
277        self.lenient = Some(lenient);
278        self
279    }
280
281    /// Maximum number of
282    /// [automaton states](https://en.wikipedia.org/wiki/Deterministic_finite_automaton)
283    /// required for the query.
284    ///
285    /// Default is `10000`.
286    ///
287    /// OpenSearch uses [Apache Lucene](https://lucene.apache.org/core/)
288    /// internally to parse regular expressions. Lucene converts each regular
289    /// expression to a finite automaton containing a number of determinized
290    /// states.
291    ///
292    /// You can use this parameter to prevent that conversion from
293    /// unintentionally consuming too many resources. You may need to increase
294    /// this limit to run complex regular expressions.
295    pub fn max_determinized_states(mut self, max_determinized_states: u32) -> Self {
296        self.max_determinized_states = Some(max_determinized_states);
297        self
298    }
299
300    /// Minimum number of clauses that must match for a document to be returned.
301    /// See the `minimum_should_match` parameter for valid values and more
302    /// information.
303    pub fn minimum_should_match<T>(mut self, minimum_should_match: T) -> Self
304    where
305        T: ToString,
306    {
307        self.minimum_should_match = Some(minimum_should_match.to_string());
308        self
309    }
310
311    /// [Analyzer](https://www.elastic.co/guide/en/opensearch/reference/current/analysis.html)
312    /// used to convert quoted text in the query string into tokens.
313    ///
314    /// Defaults to the
315    /// [`search_quote_analyzer`](https://www.elastic.co/guide/en/opensearch/reference/current/analyzer.html#search-quote-analyzer)
316    /// mapped for the `default_field`.
317    ///
318    /// For quoted text, this parameter overrides the analyzer specified in the
319    /// `analyzer` parameter.
320    pub fn quote_analyzer<S>(mut self, quote_analyzer: S) -> Self
321    where
322        S: ToString,
323    {
324        self.quote_analyzer = Some(quote_analyzer.to_string());
325        self
326    }
327
328    /// Maximum number of positions allowed between matching tokens for
329    /// phrases.
330    ///
331    /// Defaults to `0`. If `0`, exact phrase matches are required. Transposed
332    /// terms have a slop of `2`.
333    pub fn phrase_slop(mut self, phrase_slop: u32) -> Self {
334        self.phrase_slop = Some(phrase_slop);
335        self
336    }
337
338    /// Suffix appended to quoted text in the query string.
339    ///
340    /// You can use this suffix to use a different analysis method for exact
341    /// matches. See
342    /// [Mixing exact search with stemming](https://www.elastic.co/guide/en/opensearch/reference/current/mixing-exact-search-with-stemming.html).
343    pub fn quote_field_suffix<S>(mut self, quote_field_suffix: S) -> Self
344    where
345        S: ToString,
346    {
347        self.quote_field_suffix = Some(quote_field_suffix.to_string());
348        self
349    }
350
351    /// Method used to rewrite the query. For valid values and more
352    /// information, see the [`rewrite` parameter](Rewrite).
353    pub fn rewrite(mut self, rewrite: Rewrite) -> Self {
354        self.rewrite = Some(rewrite);
355        self
356    }
357
358    /// [Coordinated Universal Time (UTC) offset](https://en.wikipedia.org/wiki/List_of_UTC_time_offsets)
359    /// or [IANA time zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
360    /// used to convert `date` values in the query string to UTC.
361    ///
362    /// Valid values are ISO 8601 UTC offsets, such as `+01:00` or `-08:00`,
363    /// and IANA time zone IDs, such as `America/Los_Angeles`.
364    pub fn time_zone<S>(mut self, time_zone: S) -> Self
365    where
366        S: ToString,
367    {
368        self.time_zone = Some(time_zone.to_string());
369        self
370    }
371}
372
373impl ShouldSkip for QueryStringQuery {
374    fn should_skip(&self) -> bool {
375        self.query.should_skip()
376    }
377}
378
379serialize_with_root!("query_string": QueryStringQuery);
380deserialize_with_root!("query_string": QueryStringQuery);
381
382#[cfg(test)]
383mod tests {
384    use super::*;
385
386    #[test]
387    fn serialization() {
388        assert_serialize_query(
389            Query::query_string("search text"),
390            json!({
391                "query_string": {
392                    "query": "search text",
393                }
394            }),
395        );
396
397        assert_serialize_query(
398            Query::query_string("search text")
399                .fields(["database"])
400                .default_operator(Operator::And)
401                .analyze_wildcard(true)
402                .analyzer("search_time_analyzer")
403                .auto_generate_synonyms_phrase_query(true)
404                .fuzzy_max_expansions(20)
405                .fuzzy_prefix_length(3)
406                .fuzzy_transpositions(false)
407                .lenient(true)
408                .minimum_should_match("22")
409                .quote_field_suffix("s")
410                .boost(2)
411                .name("test"),
412            json!({
413                "query_string": {
414                    "query": "search text",
415                    "fields": ["database"],
416                    "default_operator": "AND",
417                    "analyze_wildcard": true,
418                    "analyzer": "search_time_analyzer",
419                    "auto_generate_synonyms_phrase_query": true,
420                    "fuzzy_max_expansions": 20,
421                    "fuzzy_prefix_length": 3,
422                    "fuzzy_transpositions": false,
423                    "lenient": true,
424                    "minimum_should_match": "22",
425                    "quote_field_suffix": "s",
426                    "boost": 2.0,
427                    "_name": "test",
428                }
429            }),
430        );
431    }
432}