elasticsearch_dsl/search/queries/joining/has_child_query.rs
1use crate::search::*;
2use crate::util::*;
3
4/// Returns parent documents whose joined child documents match a provided query. You can create
5/// parent-child relationships between documents in the same index using a join field mapping.
6///
7/// To create has_child query:
8/// ```
9/// # use elasticsearch_dsl::queries::*;
10/// # use elasticsearch_dsl::queries::params::*;
11/// # let query =
12/// Query::has_child("child", Query::term("tag", "elasticsearch"));
13/// ```
14/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-has-child-query.html>
15#[derive(Debug, Clone, PartialEq, Serialize)]
16#[serde(remote = "Self")]
17pub struct HasChildQuery {
18 r#type: String,
19
20 query: Box<Query>,
21
22 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
23 ignore_unmapped: Option<bool>,
24
25 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
26 max_children: Option<u32>,
27
28 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
29 min_children: Option<u32>,
30
31 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
32 score_mode: Option<HasChildScoreMode>,
33
34 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
35 inner_hits: Option<Box<InnerHits>>,
36
37 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
38 boost: Option<f32>,
39
40 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
41 _name: Option<String>,
42}
43
44impl Query {
45 /// Creates an instance of [`HasChildQuery`]
46 ///
47 /// - `type` - Name of the child relationship mapped for the join field.
48 /// - `query` - Query you wish to run on child documents of the `type` field. If a child
49 /// document matches the search, the query returns the parent document.
50 pub fn has_child<T, U>(r#type: T, query: U) -> HasChildQuery
51 where
52 T: ToString,
53 U: Into<Query>,
54 {
55 HasChildQuery {
56 r#type: r#type.to_string(),
57 query: Box::new(query.into()),
58 ignore_unmapped: None,
59 max_children: None,
60 min_children: None,
61 score_mode: None,
62 inner_hits: None,
63 boost: None,
64 _name: None,
65 }
66 }
67}
68
69impl HasChildQuery {
70 /// Indicates whether to ignore an unmapped `type` and not return any documents instead of an
71 /// error. Defaults to `false`.
72 ///
73 /// If `false`, Elasticsearch returns an error if the `type` is unmapped.
74 ///
75 /// You can use this parameter to query multiple indices that may not contain the `type`.
76 pub fn ignore_unmapped(mut self, ignore_unmapped: bool) -> Self {
77 self.ignore_unmapped = Some(ignore_unmapped);
78 self
79 }
80
81 /// Maximum number of child documents that match the `query` allowed for a returned parent
82 /// document. If the parent document exceeds this limit, it is excluded from the search results.
83 pub fn max_children(mut self, max_children: u32) -> Self {
84 self.max_children = Some(max_children);
85 self
86 }
87
88 /// Minimum number of child documents that match the `query` required to match the query for a
89 /// returned parent document. If the parent document does not meet this limit, it is excluded
90 /// from the search results.
91 pub fn min_children(mut self, min_children: u32) -> Self {
92 self.min_children = Some(min_children);
93 self
94 }
95
96 /// Indicates how scores for matching child documents affect the root parent document’s
97 /// relevance score.
98 pub fn score_mode(mut self, score_mode: HasChildScoreMode) -> Self {
99 self.score_mode = Some(score_mode);
100 self
101 }
102
103 /// The [parent-join](https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html)
104 /// and [nested](https://www.elastic.co/guide/en/elasticsearch/reference/current/nested.html)
105 /// features allow the return of documents that have matches in a different scope. In the
106 /// parent/child case, parent documents are returned based on matches in child documents or
107 /// child documents are returned based on matches in parent documents. In the nested case,
108 /// documents are returned based on matches in nested inner objects.
109 ///
110 /// In both cases, the actual matches in the different scopes that caused a document to be
111 /// returned are hidden. In many cases, it’s very useful to know which inner nested objects
112 /// (in the case of nested) or children/parent documents (in the case of parent/child) caused
113 /// certain information to be returned. The inner hits feature can be used for this. This
114 /// feature returns per search hit in the search response additional nested hits that caused a
115 /// search hit to match in a different scope.
116 ///
117 /// Inner hits can be used by defining an `inner_hits` definition on a `nested`, `has_child`
118 /// or `has_parent` query and filter.
119 ///
120 /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/inner-hits.html>
121 pub fn inner_hits(mut self, inner_hits: InnerHits) -> Self {
122 self.inner_hits = Some(Box::new(inner_hits));
123 self
124 }
125
126 add_boost_and_name!();
127}
128
129impl ShouldSkip for HasChildQuery {
130 fn should_skip(&self) -> bool {
131 self.r#type.should_skip() || self.query.should_skip()
132 }
133}
134
135serialize_with_root!("has_child": HasChildQuery);
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn serialization() {
143 assert_serialize_query(
144 Query::has_child("child", Query::term("tag", "elasticsearch")),
145 json!({
146 "has_child": {
147 "type": "child",
148 "query": {
149 "term": {
150 "tag": {
151 "value": "elasticsearch"
152 }
153 }
154 }
155 }
156 }),
157 );
158
159 assert_serialize_query(
160 Query::has_child("child", Query::term("tag", "elasticsearch"))
161 .boost(2)
162 .name("test")
163 .ignore_unmapped(true)
164 .max_children(3)
165 .min_children(2)
166 .inner_hits(InnerHits::new())
167 .score_mode(HasChildScoreMode::Max),
168 json!({
169 "has_child": {
170 "type": "child",
171 "ignore_unmapped": true,
172 "max_children": 3,
173 "min_children": 2,
174 "score_mode": "max",
175 "query": {
176 "term": {
177 "tag": {
178 "value": "elasticsearch"
179 }
180 }
181 },
182 "inner_hits": {},
183 "boost": 2.0,
184 "_name": "test"
185 }
186 }),
187 );
188 }
189}