elasticsearch_dsl/search/queries/full_text/
combined_fields_query.rs

1use crate::search::*;
2use crate::util::*;
3
4/// The combined_fields query supports searching multiple text fields as if
5/// their contents had been indexed into one combined field. The query takes a
6/// term-centric view of the input string: first it analyzes the query string
7/// into individual terms, then looks for each term in any of the fields. This
8/// query is particularly useful when a match could span multiple text fields.
9///
10/// To create a combined fields query with:
11/// ```
12/// # use elasticsearch_dsl::queries::*;
13/// # use elasticsearch_dsl::queries::params::*;
14/// # let query =
15/// Query::combined_fields(["title", "abstract", "body"], "database systems")
16///     .boost(2)
17///     .name("test");
18/// ```
19/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-combined-fields-query.html>
20#[derive(Debug, Default, Clone, PartialEq, Serialize)]
21#[serde(remote = "Self")]
22pub struct CombinedFieldsQuery {
23    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
24    fields: Vec<String>,
25
26    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
27    query: Text,
28
29    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
30    auto_generate_synonyms_phrase_query: Option<bool>,
31
32    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
33    operator: Option<Operator>,
34
35    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
36    minimum_should_match: Option<String>,
37
38    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
39    zero_terms_query: Option<ZeroTermsQuery>,
40
41    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
42    boost: Option<f32>,
43
44    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
45    _name: Option<String>,
46}
47
48impl Query {
49    /// Creates an instance of [`CombinedFieldsQuery`]
50    ///
51    /// - `fields` - List of fields to search. Field wildcard patterns are
52    ///   allowed. Only text fields are supported, and they must all have the
53    ///   same search analyzer.
54    /// - `query` - Text to search for in the provided `<fields>`.
55    ///   The combined_fields query analyzes the provided text before performing a search.
56    pub fn combined_fields<F, S>(fields: F, query: S) -> CombinedFieldsQuery
57    where
58        F: IntoIterator,
59        F::Item: ToString,
60        S: Into<Text>,
61    {
62        CombinedFieldsQuery {
63            fields: fields.into_iter().map(|s| s.to_string()).collect(),
64            query: query.into(),
65            auto_generate_synonyms_phrase_query: None,
66            operator: None,
67            minimum_should_match: None,
68            zero_terms_query: None,
69            boost: None,
70            _name: None,
71        }
72    }
73}
74
75impl CombinedFieldsQuery {
76    /// If `true`,
77    /// [match phrase](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query-phrase.html)
78    /// queries are automatically created for multi-term synonyms. Defaults to `true`.
79    ///
80    /// 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)
81    /// for an example.
82    pub fn auto_generate_synonyms_phrase_query(
83        mut self,
84        auto_generate_synonyms_phrase_query: bool,
85    ) -> Self {
86        self.auto_generate_synonyms_phrase_query = Some(auto_generate_synonyms_phrase_query);
87        self
88    }
89
90    /// Boolean logic used to interpret text in the `query` value
91    pub fn operator(mut self, operator: Operator) -> Self {
92        self.operator = Some(operator);
93        self
94    }
95
96    /// Minimum number of clauses that must match for a document to be returned. See the
97    /// `minimum_should_match` parameter for valid values and more information.
98    pub fn minimum_should_match<T>(mut self, minimum_should_match: T) -> Self
99    where
100        T: ToString,
101    {
102        self.minimum_should_match = Some(minimum_should_match.to_string());
103        self
104    }
105
106    /// Indicates whether no documents are returned if the `analyzer` removes
107    /// all tokens, such as when using a `stop` filter.
108    pub fn zero_terms_query(mut self, zero_terms_query: ZeroTermsQuery) -> Self {
109        self.zero_terms_query = Some(zero_terms_query);
110        self
111    }
112
113    add_boost_and_name!();
114}
115
116impl ShouldSkip for CombinedFieldsQuery {
117    fn should_skip(&self) -> bool {
118        self.fields.should_skip() || self.query.should_skip()
119    }
120}
121
122serialize_with_root!("combined_fields": CombinedFieldsQuery);
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn serialization() {
130        assert_serialize_query(
131            Query::combined_fields(["test"], "search text"),
132            json!({
133                "combined_fields": {
134                    "query": "search text",
135                    "fields": ["test"],
136                }
137            }),
138        );
139
140        assert_serialize_query(
141            Query::combined_fields(["test"], "search text")
142                .auto_generate_synonyms_phrase_query(true)
143                .operator(Operator::And)
144                .minimum_should_match("22")
145                .zero_terms_query(ZeroTermsQuery::None)
146                .boost(2)
147                .name("test"),
148            json!({
149                "combined_fields": {
150                    "query": "search text",
151                    "fields": ["test"],
152                    "auto_generate_synonyms_phrase_query": true,
153                    "operator": "AND",
154                    "minimum_should_match": "22",
155                    "zero_terms_query": "none",
156                    "boost": 2.0,
157                    "_name": "test",
158                }
159            }),
160        );
161    }
162}