Skip to content

Commit 990647c

Browse files
committed
add geo_polygon filter
1 parent 3b6ef19 commit 990647c

5 files changed

+171
-1
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ Here's the current API status.
245245
* `geo_bbox` (missing)
246246
* `geo_distance` (missing)
247247
* `geo_distance_range` (missing)
248-
* `geo_polygon` (missing)
248+
* `geo_polygon` (ok)
249249
* `geo_shape` (missing)
250250
* `has_child` (ok)
251251
* `has_parent` (ok)

geo_point.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package elastic
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
)
8+
9+
// GeoPoint is a geographic position described via latitude and longitude.
10+
type GeoPoint struct {
11+
Lat, Lon float64
12+
}
13+
14+
// Source returns the object to be serialized in Elasticsearch DSL.
15+
func (pt *GeoPoint) Source() map[string]float64 {
16+
return map[string]float64{
17+
"lat": pt.Lat,
18+
"lon": pt.Lon,
19+
}
20+
}
21+
22+
// GeoPointFromLatLon initializes a new GeoPoint by latitude and longitude.
23+
func GeoPointFromLatLon(lat, lon float64) *GeoPoint {
24+
return &GeoPoint{Lat: lat, Lon: lon}
25+
}
26+
27+
// GeoPointFromString initializes a new GeoPoint by a string that is
28+
// formatted as "{latitude},{longitude}", e.g. "40.10210,-70.12091".
29+
func GeoPointFromString(latLon string) (*GeoPoint, error) {
30+
latlon := strings.SplitN(latLon, ",", 2)
31+
if len(latlon) != 2 {
32+
return nil, fmt.Errorf("elastic: %s is not a valid geo point string", latLon)
33+
}
34+
lat, err := strconv.ParseFloat(latlon[0], 64)
35+
if err != nil {
36+
return nil, err
37+
}
38+
lon, err := strconv.ParseFloat(latlon[1], 64)
39+
if err != nil {
40+
return nil, err
41+
}
42+
return &GeoPoint{Lat: lat, Lon: lon}, nil
43+
}

geo_point_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package elastic
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
)
7+
8+
func TestGeoPointSource(t *testing.T) {
9+
pt := GeoPoint{Lat: 40, Lon: -70}
10+
11+
data, err := json.Marshal(pt.Source())
12+
if err != nil {
13+
t.Fatalf("marshaling to JSON failed: %v", err)
14+
}
15+
got := string(data)
16+
expected := `{"lat":40,"lon":-70}`
17+
if got != expected {
18+
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
19+
}
20+
}

search_filters_geo_polygon.go

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2012 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+
5+
package elastic
6+
7+
// A filter allowing to include hits that only fall within a polygon of points.
8+
// For details, see:
9+
// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-geo-polygon-filter.html
10+
type GeoPolygonFilter struct {
11+
Filter
12+
name string
13+
points []*GeoPoint
14+
cache *bool
15+
cacheKey string
16+
filterName string
17+
}
18+
19+
func NewGeoPolygonFilter(name string) GeoPolygonFilter {
20+
f := GeoPolygonFilter{name: name, points: make([]*GeoPoint, 0)}
21+
return f
22+
}
23+
24+
func (f GeoPolygonFilter) Cache(cache bool) GeoPolygonFilter {
25+
f.cache = &cache
26+
return f
27+
}
28+
29+
func (f GeoPolygonFilter) CacheKey(cacheKey string) GeoPolygonFilter {
30+
f.cacheKey = cacheKey
31+
return f
32+
}
33+
34+
func (f GeoPolygonFilter) FilterName(filterName string) GeoPolygonFilter {
35+
f.filterName = filterName
36+
return f
37+
}
38+
39+
func (f GeoPolygonFilter) AddPoint(point *GeoPoint) GeoPolygonFilter {
40+
f.points = append(f.points, point)
41+
return f
42+
}
43+
44+
func (f GeoPolygonFilter) Source() interface{} {
45+
// "geo_polygon" : {
46+
// "person.location" : {
47+
// "points" : [
48+
// {"lat" : 40, "lon" : -70},
49+
// {"lat" : 30, "lon" : -80},
50+
// {"lat" : 20, "lon" : -90}
51+
// ]
52+
// }
53+
// }
54+
source := make(map[string]interface{})
55+
56+
params := make(map[string]interface{})
57+
source["geo_polygon"] = params
58+
59+
polygon := make(map[string]interface{})
60+
params[f.name] = polygon
61+
62+
points := make([]interface{}, 0)
63+
for _, point := range f.points {
64+
points = append(points, point.Source())
65+
}
66+
polygon["points"] = points
67+
68+
if f.filterName != "" {
69+
params["_name"] = f.filterName
70+
}
71+
72+
if f.cache != nil {
73+
params["_cache"] = *f.cache
74+
}
75+
76+
if f.cacheKey != "" {
77+
params["_cache_key"] = f.cacheKey
78+
}
79+
80+
return source
81+
}

search_filters_geo_polygon_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package elastic
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
)
7+
8+
func TestGeoPolygonFilter(t *testing.T) {
9+
f := NewGeoPolygonFilter("person.location")
10+
f = f.AddPoint(&GeoPoint{Lat: 40, Lon: -70})
11+
f = f.AddPoint(GeoPointFromLatLon(30, -80))
12+
point, err := GeoPointFromString("20,-90")
13+
if err != nil {
14+
t.Fatalf("GeoPointFromString failed: %v", err)
15+
}
16+
f = f.AddPoint(point)
17+
data, err := json.Marshal(f.Source())
18+
if err != nil {
19+
t.Fatalf("marshaling to JSON failed: %v", err)
20+
}
21+
got := string(data)
22+
expected := `{"geo_polygon":{"person.location":{"points":[{"lat":40,"lon":-70},{"lat":30,"lon":-80},{"lat":20,"lon":-90}]}}}`
23+
if got != expected {
24+
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
25+
}
26+
}

0 commit comments

Comments
 (0)