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}