Skip to content

Implement GeoShape query #956

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: release-branch.v6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ The cat APIs are not implemented as of now. We think they are better suited for
- [x] Has Parent Query
- [x] Parent Id Query
- Geo queries
- [ ] GeoShape Query
- [x] GeoShape Query
- [x] Geo Bounding Box Query
- [x] Geo Distance Query
- [x] Geo Polygon Query
Expand Down
147 changes: 147 additions & 0 deletions search_queries_geo_shape.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

// GeoShapeQuery allows to find documents that have a shape that intersects with the query shape
// a bounding box.
//
// For more details, see:
// https://www.elastic.co/guide/en/elasticsearch/reference/6.2/query-dsl-geo-shape-query.html
type GeoShapeQuery struct {
name string
shape Shape
strategy string
relation string
indexedShape struct {
index string
typ string
path string
id string
routing string
}
ignoreUnmapped bool
queryName string
}

// NewGeoShapeQuery creates and initializes a new GeoShapeQuery.
func NewGeoShapeQuery(name string) *GeoShapeQuery {
return &GeoShapeQuery{
name: name,
}
}

func (q *GeoShapeQuery) Shape(s Shape) *GeoShapeQuery {
q.shape = s
return q
}

func (q *GeoShapeQuery) Strategy(strategy string) *GeoShapeQuery {
q.strategy = strategy
return q
}

func (q *GeoShapeQuery) Relation(relation string) *GeoShapeQuery {
q.relation = relation
return q
}

func (q *GeoShapeQuery) IndexedShape(index, typ, path, id, routing string) *GeoShapeQuery {
q.indexedShape.index = index
q.indexedShape.typ = typ
q.indexedShape.path = path
q.indexedShape.id = id
q.indexedShape.routing = routing
return q
}

func (q *GeoShapeQuery) IndexedShapeIndex(index string) *GeoShapeQuery {
q.indexedShape.index = index
return q
}

func (q *GeoShapeQuery) IndexedShapeType(typ string) *GeoShapeQuery {
q.indexedShape.typ = typ
return q
}

func (q *GeoShapeQuery) IndexedShapePath(path string) *GeoShapeQuery {
q.indexedShape.path = path
return q
}

func (q *GeoShapeQuery) IndexedShapeID(id string) *GeoShapeQuery {
q.indexedShape.id = id
return q
}

func (q *GeoShapeQuery) IndexedShapeRouting(routing string) *GeoShapeQuery {
q.indexedShape.routing = routing
return q
}

func (q *GeoShapeQuery) IgnoreUnmapped(ignoreUnmapped bool) *GeoShapeQuery {
q.ignoreUnmapped = ignoreUnmapped
return q
}

func (q *GeoShapeQuery) QueryName(queryName string) *GeoShapeQuery {
q.queryName = queryName
return q
}

// Source returns JSON for the function score query.
func (q *GeoShapeQuery) Source() (interface{}, error) {
source := make(map[string]interface{})
params := make(map[string]interface{})
source["geo_shape"] = params

shape := make(map[string]interface{})
params[q.name] = shape
params["ignore_unmapped"] = q.ignoreUnmapped

if q.strategy != "" {
shape["strategy"] = q.strategy
}

if q.relation != "" {
shape["relation"] = q.relation
}

if q.indexedShape.id == "" {
var err error

shape["shape"], err = q.shape.Source()
if err != nil {
return nil, err
}
} else {
indexedShape := make(map[string]interface{})
shape["indexed_shape"] = indexedShape
indexedShape["id"] = q.indexedShape.id
indexedShape["type"] = q.indexedShape.typ
if q.indexedShape.index != "" {
indexedShape["index"] = q.indexedShape.index
}
if q.indexedShape.path != "" {
indexedShape["path"] = q.indexedShape.path
}
if q.indexedShape.routing != "" {
indexedShape["routing"] = q.indexedShape.routing
}
}

if q.queryName != "" {
params["_name"] = q.queryName
}

return source, nil
}

type Shape struct {
}

func (s *Shape) Source() (interface{}, error) {
return nil, nil
}
72 changes: 72 additions & 0 deletions search_queries_geo_shape_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
"encoding/json"
"testing"
)

func TestGeoShapeQueryWithPreIndexedShape(t *testing.T) {
q := NewGeoShapeQuery("pin.location")
q = q.IndexedShape("shapes", "_doc", "location", "deu", "")
q = q.Relation("contains")
src, err := q.Source()
if err != nil {
t.Fatal(err)
}
data, err := json.Marshal(src)
if err != nil {
t.Fatalf("marshaling to JSON failed: %v", err)
}
got := string(data)
expected := `{"geo_shape":{"pin.location":{"indexed_shape":{"id":"deu","index":"shapes","path":"location","type":"_doc"}},"relation":"contains"}}`
if got != expected {
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
}
}

func TestGeoShapeQueryWithPoint(t *testing.T) {
q := NewGeoShapeQuery("pin.location")
q = q.Type("point")
q = q.Coordinates([]float64{13.0, 53.0})
q = q.Relation("contains")
src, err := q.Source()
if err != nil {
t.Fatal(err)
}
data, err := json.Marshal(src)
if err != nil {
t.Fatalf("marshaling to JSON failed: %v", err)
}
got := string(data)
expected := `{"geo_shape":{"pin.location":{"shape":{"coordinates":[13,53],"type":"point"}},"relation":"contains"}}`
if got != expected {
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
}
}

func TestGeoShapeQueryWithEnvelope(t *testing.T) {
q := NewGeoShapeQuery("pin.location")
q = q.Type("envelope")
q = q.Coordinates([][]float64{
{13.0, 53.0},
{14.0, 52.0},
})
q = q.Relation("within")
src, err := q.Source()
if err != nil {
t.Fatal(err)
}
data, err := json.Marshal(src)
if err != nil {
t.Fatalf("marshaling to JSON failed: %v", err)
}
got := string(data)
expected := `{"geo_shape":{"pin.location":{"shape":{"coordinates":[[13,53],[14,52]],"type":"envelope"}},"relation":"within"}}`
if got != expected {
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
}
}