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
7import "errors"
8
9// GeoBoundingBoxQuery allows to filter hits based on a point location using
10// a bounding box.
11//
12// For more details, see:
13// https://www.elastic.co/guide/en/elasticsearch/reference/6.7/query-dsl-geo-bounding-box-query.html
14type GeoBoundingBoxQuery struct {
15	name      string
16	top       *float64
17	left      *float64
18	bottom    *float64
19	right     *float64
20	typ       string
21	queryName string
22}
23
24// NewGeoBoundingBoxQuery creates and initializes a new GeoBoundingBoxQuery.
25func NewGeoBoundingBoxQuery(name string) *GeoBoundingBoxQuery {
26	return &GeoBoundingBoxQuery{
27		name: name,
28	}
29}
30
31func (q *GeoBoundingBoxQuery) TopLeft(top, left float64) *GeoBoundingBoxQuery {
32	q.top = &top
33	q.left = &left
34	return q
35}
36
37func (q *GeoBoundingBoxQuery) TopLeftFromGeoPoint(point *GeoPoint) *GeoBoundingBoxQuery {
38	return q.TopLeft(point.Lat, point.Lon)
39}
40
41func (q *GeoBoundingBoxQuery) BottomRight(bottom, right float64) *GeoBoundingBoxQuery {
42	q.bottom = &bottom
43	q.right = &right
44	return q
45}
46
47func (q *GeoBoundingBoxQuery) BottomRightFromGeoPoint(point *GeoPoint) *GeoBoundingBoxQuery {
48	return q.BottomRight(point.Lat, point.Lon)
49}
50
51func (q *GeoBoundingBoxQuery) BottomLeft(bottom, left float64) *GeoBoundingBoxQuery {
52	q.bottom = &bottom
53	q.left = &left
54	return q
55}
56
57func (q *GeoBoundingBoxQuery) BottomLeftFromGeoPoint(point *GeoPoint) *GeoBoundingBoxQuery {
58	return q.BottomLeft(point.Lat, point.Lon)
59}
60
61func (q *GeoBoundingBoxQuery) TopRight(top, right float64) *GeoBoundingBoxQuery {
62	q.top = &top
63	q.right = &right
64	return q
65}
66
67func (q *GeoBoundingBoxQuery) TopRightFromGeoPoint(point *GeoPoint) *GeoBoundingBoxQuery {
68	return q.TopRight(point.Lat, point.Lon)
69}
70
71// Type sets the type of executing the geo bounding box. It can be either
72// memory or indexed. It defaults to memory.
73func (q *GeoBoundingBoxQuery) Type(typ string) *GeoBoundingBoxQuery {
74	q.typ = typ
75	return q
76}
77
78func (q *GeoBoundingBoxQuery) QueryName(queryName string) *GeoBoundingBoxQuery {
79	q.queryName = queryName
80	return q
81}
82
83// Source returns JSON for the function score query.
84func (q *GeoBoundingBoxQuery) Source() (interface{}, error) {
85	// {
86	//   "geo_bounding_box" : {
87	//     ...
88	//   }
89	// }
90
91	if q.top == nil {
92		return nil, errors.New("geo_bounding_box requires top latitude to be set")
93	}
94	if q.bottom == nil {
95		return nil, errors.New("geo_bounding_box requires bottom latitude to be set")
96	}
97	if q.right == nil {
98		return nil, errors.New("geo_bounding_box requires right longitude to be set")
99	}
100	if q.left == nil {
101		return nil, errors.New("geo_bounding_box requires left longitude to be set")
102	}
103
104	source := make(map[string]interface{})
105	params := make(map[string]interface{})
106	source["geo_bounding_box"] = params
107
108	box := make(map[string]interface{})
109	box["top_left"] = []float64{*q.left, *q.top}
110	box["bottom_right"] = []float64{*q.right, *q.bottom}
111	params[q.name] = box
112
113	if q.typ != "" {
114		params["type"] = q.typ
115	}
116	if q.queryName != "" {
117		params["_name"] = q.queryName
118	}
119
120	return source, nil
121}
122