1// Copyright 2012-present Oliver Eilhard. All rights reserved.
2// Use of this source code is governed by a MIT-license.
3// See http://olivere.mit-license.org/license.txt for details.
4
5package elastic
6
7// HasChildQuery accepts a query and the child type to run against, and results
8// in parent documents that have child docs matching the query.
9//
10// For more details, see
11// https://www.elastic.co/guide/en/elasticsearch/reference/5.2/query-dsl-has-child-query.html
12type HasChildQuery struct {
13	query              Query
14	childType          string
15	boost              *float64
16	scoreMode          string
17	minChildren        *int
18	maxChildren        *int
19	shortCircuitCutoff *int
20	queryName          string
21	innerHit           *InnerHit
22}
23
24// NewHasChildQuery creates and initializes a new has_child query.
25func NewHasChildQuery(childType string, query Query) *HasChildQuery {
26	return &HasChildQuery{
27		query:     query,
28		childType: childType,
29	}
30}
31
32// Boost sets the boost for this query.
33func (q *HasChildQuery) Boost(boost float64) *HasChildQuery {
34	q.boost = &boost
35	return q
36}
37
38// ScoreMode defines how the scores from the matching child documents
39// are mapped into the parent document. Allowed values are: min, max,
40// avg, or none.
41func (q *HasChildQuery) ScoreMode(scoreMode string) *HasChildQuery {
42	q.scoreMode = scoreMode
43	return q
44}
45
46// MinChildren defines the minimum number of children that are required
47// to match for the parent to be considered a match.
48func (q *HasChildQuery) MinChildren(minChildren int) *HasChildQuery {
49	q.minChildren = &minChildren
50	return q
51}
52
53// MaxChildren defines the maximum number of children that are required
54// to match for the parent to be considered a match.
55func (q *HasChildQuery) MaxChildren(maxChildren int) *HasChildQuery {
56	q.maxChildren = &maxChildren
57	return q
58}
59
60// ShortCircuitCutoff configures what cut off point only to evaluate
61// parent documents that contain the matching parent id terms instead
62// of evaluating all parent docs.
63func (q *HasChildQuery) ShortCircuitCutoff(shortCircuitCutoff int) *HasChildQuery {
64	q.shortCircuitCutoff = &shortCircuitCutoff
65	return q
66}
67
68// QueryName specifies the query name for the filter that can be used when
69// searching for matched filters per hit.
70func (q *HasChildQuery) QueryName(queryName string) *HasChildQuery {
71	q.queryName = queryName
72	return q
73}
74
75// InnerHit sets the inner hit definition in the scope of this query and
76// reusing the defined type and query.
77func (q *HasChildQuery) InnerHit(innerHit *InnerHit) *HasChildQuery {
78	q.innerHit = innerHit
79	return q
80}
81
82// Source returns JSON for the function score query.
83func (q *HasChildQuery) Source() (interface{}, error) {
84	// {
85	//   "has_child" : {
86	//       "type" : "blog_tag",
87	//       "score_mode" : "min",
88	//       "query" : {
89	//           "term" : {
90	//               "tag" : "something"
91	//           }
92	//       }
93	//   }
94	// }
95	source := make(map[string]interface{})
96	query := make(map[string]interface{})
97	source["has_child"] = query
98
99	src, err := q.query.Source()
100	if err != nil {
101		return nil, err
102	}
103	query["query"] = src
104	query["type"] = q.childType
105	if q.boost != nil {
106		query["boost"] = *q.boost
107	}
108	if q.scoreMode != "" {
109		query["score_mode"] = q.scoreMode
110	}
111	if q.minChildren != nil {
112		query["min_children"] = *q.minChildren
113	}
114	if q.maxChildren != nil {
115		query["max_children"] = *q.maxChildren
116	}
117	if q.shortCircuitCutoff != nil {
118		query["short_circuit_cutoff"] = *q.shortCircuitCutoff
119	}
120	if q.queryName != "" {
121		query["_name"] = q.queryName
122	}
123	if q.innerHit != nil {
124		src, err := q.innerHit.Source()
125		if err != nil {
126			return nil, err
127		}
128		query["inner_hits"] = src
129	}
130	return source, nil
131}
132