Skip to main content

opensearch_dsl/search/queries/joining/
nested_query.rs

1use crate::{search::*, util::*};
2
3/// Wraps another query to search
4/// [nested](https://www.elastic.co/guide/en/opensearch/reference/current/nested.html)
5/// fields.
6///
7/// The `nested` query searches nested field objects as if they were indexed as
8/// separate documents. If an object matches the search, the `nested` query
9/// returns the root parent document.
10///
11/// To create nested query:
12/// ```
13/// # use opensearch_dsl::queries::*;
14/// # use opensearch_dsl::queries::params::*;
15/// # let query =
16/// Query::nested("vehicles", Query::term("vehicles.license", "ABC123"))
17///   .boost(3)
18///   .name("test");
19/// ```
20/// To create multi-level nested query:
21/// ```
22/// # use opensearch_dsl::queries::*;
23/// # use opensearch_dsl::queries::params::*;
24/// # let query =
25/// Query::nested(
26///   "driver",
27///   Query::nested(
28///     "driver.vehicle",
29///     Query::term("driver.vehicle.make", "toyota"),
30///   ),
31/// )
32/// .boost(3)
33/// .name("test");
34/// ```
35/// <https://www.elastic.co/guide/en/opensearch/reference/current/query-dsl-nested-query.html>
36#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
37#[serde(remote = "Self")]
38pub struct NestedQuery {
39    path: String,
40
41    query: Box<Query>,
42
43    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
44    score_mode: Option<NestedQueryScoreMode>,
45
46    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
47    ignore_unmapped: Option<bool>,
48
49    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
50    inner_hits: Option<Box<InnerHits>>,
51
52    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
53    boost: Option<f32>,
54
55    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
56    _name: Option<String>,
57}
58
59impl Query {
60    /// Creates an instance of [`NestedQuery`]
61    ///
62    /// - `path` - Path to the nested object you wish to search.
63    /// - `query` - Query you wish to run on nested objects in the `path`. If an
64    ///   object
65    /// matches the search, the `nested` query returns the root parent
66    /// document.<br/> You can search nested fields using dot notation that
67    /// includes the complete path, such as `obj1.name`.<br/>
68    /// Multi-level nesting is automatically supported, and detected,
69    /// resulting in an inner nested query to automatically match the relevant
70    /// nesting level, rather than root, if it exists within another nested
71    /// query.<br/>
72    /// Multi-level nested queries are also supported.
73    pub fn nested<T, U>(path: T, query: U) -> NestedQuery
74    where
75        T: ToString,
76        U: Into<Query>,
77    {
78        NestedQuery {
79            path: path.to_string(),
80            query: Box::new(query.into()),
81            score_mode: None,
82            ignore_unmapped: None,
83            inner_hits: None,
84            boost: None,
85            _name: None,
86        }
87    }
88}
89
90impl NestedQuery {
91    add_boost_and_name!();
92
93    /// Indicates how scores for matching child objects affect the root parent
94    /// document’s
95    /// [relevance score](https://www.elastic.co/guide/en/opensearch/reference/current/query-filter-context.html#relevance-scores).
96    pub fn score_mode(mut self, score_mode: NestedQueryScoreMode) -> Self {
97        self.score_mode = Some(score_mode);
98        self
99    }
100
101    /// Indicates whether to ignore an unmapped `path` and not return any
102    /// documents instead of an error. Defaults to `false`.
103    ///
104    /// If `false`, OpenSearch returns an error if the `path` is an unmapped
105    /// field.
106    ///
107    /// You can use this parameter to query multiple indices that may not
108    /// contain the field `path`.
109    pub fn ignore_unmapped(mut self, ignore_unmapped: bool) -> Self {
110        self.ignore_unmapped = Some(ignore_unmapped);
111        self
112    }
113
114    /// The [parent-join](https://www.elastic.co/guide/en/opensearch/reference/current/parent-join.html)
115    /// and [nested](https://www.elastic.co/guide/en/opensearch/reference/current/nested.html)
116    /// features allow the return of documents that have matches in a different
117    /// scope. In the parent/child case, parent documents are returned based on
118    /// matches in child documents or child documents are returned based on
119    /// matches in parent documents. In the nested case, documents are returned
120    /// based on matches in nested inner objects.
121    ///
122    /// In both cases, the actual matches in the different scopes that caused a
123    /// document to be returned are hidden. In many cases, it’s very useful to
124    /// know which inner nested objects (in the case of nested) or
125    /// children/parent documents (in the case of parent/child) caused
126    /// certain information to be returned. The inner hits feature can be used for
127    /// this. This feature returns per search hit in the search response
128    /// additional nested hits that caused a search hit to match in a different
129    /// scope.
130    ///
131    /// Inner hits can be used by defining an `inner_hits` definition on a
132    /// `nested`, `has_child` or `has_parent` query and filter.
133    ///
134    /// <https://www.elastic.co/guide/en/opensearch/reference/current/inner-hits.html>
135    pub fn inner_hits(mut self, inner_hits: InnerHits) -> Self {
136        self.inner_hits = Some(Box::new(inner_hits));
137        self
138    }
139}
140
141impl ShouldSkip for NestedQuery {
142    fn should_skip(&self) -> bool {
143        self.query.should_skip()
144    }
145}
146
147serialize_with_root!("nested": NestedQuery);
148deserialize_with_root!("nested": NestedQuery);
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn serialization() {
156        assert_serialize_query(
157            Query::nested("vehicles", Query::term("vehicles.license", "ABC123")),
158            json!({
159                "nested": {
160                    "path": "vehicles",
161                    "query": {
162                        "term": {
163                            "vehicles.license": {
164                                "value": "ABC123"
165                            }
166                        }
167                    }
168                }
169            }),
170        );
171
172        assert_serialize_query(
173            Query::nested("vehicles", Query::term("vehicles.license", "ABC123"))
174                .boost(3)
175                .name("test"),
176            json!({
177                "nested": {
178                    "path": "vehicles",
179                    "query": {
180                        "term": {
181                            "vehicles.license": {
182                                "value": "ABC123"
183                            }
184                        }
185                    },
186                    "boost": 3.0,
187                    "_name": "test",
188                }
189            }),
190        );
191
192        assert_serialize_query(
193            Query::nested(
194                "driver",
195                Query::nested(
196                    "driver.vehicles",
197                    Query::term("driver.vehicles.make.keyword", "toyota"),
198                ),
199            ),
200            json!({
201                "nested": {
202                    "path": "driver",
203                    "query": {
204                        "nested": {
205                            "path": "driver.vehicles",
206                            "query": {
207                                "term": {
208                                    "driver.vehicles.make.keyword": {
209                                        "value": "toyota"
210                                    }
211                                }
212                            }
213                        }
214                    }
215                }
216            }),
217        );
218    }
219}