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}