-
Notifications
You must be signed in to change notification settings - Fork 6
/
query.go
110 lines (105 loc) · 2.87 KB
/
query.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package es
import (
"github.com/rs/rest-layer/resource"
"github.com/rs/rest-layer/schema/query"
"gopkg.in/olivere/elastic.v5"
)
// getField translate a schema field into a ES field:
//
// - id -> _id with in order to tape on the ES _id key
// - keyword=true -> appends .keyword to the field name
func getField(f string, keyword bool) string {
if f == "id" {
return "_id"
} else if keyword {
return f + ".keyword"
}
return f
}
// getQuery transform a resource.Lookup into a ES query
func getQuery(q *query.Query) (elastic.Query, error) {
qs, err := translatePredicate(q.Predicate)
if err != nil {
return nil, err
}
switch len(qs) {
case 0:
return nil, nil
case 1:
// If a single root query is returned, do not wrap
return qs[0], nil
default:
bq := elastic.NewBoolQuery()
bq.Must(qs...)
return bq, nil
}
}
// getSort transform a resource.Lookup into an ES sort list.
func getSort(q *query.Query) []elastic.Sorter {
if len(q.Sort) == 0 {
return nil
}
s := make([]elastic.Sorter, len(q.Sort))
for i, sort := range q.Sort {
if sort.Reversed {
s[i] = elastic.NewFieldSort(getField(sort.Name, true)).Desc()
} else {
s[i] = elastic.NewFieldSort(getField(sort.Name, true)).Asc()
}
}
return s
}
func translatePredicate(q query.Predicate) ([]elastic.Query, error) {
qs := []elastic.Query{}
for _, exp := range q {
switch t := exp.(type) {
case *query.And:
and := elastic.NewBoolQuery()
for _, subExp := range *t {
sq, err := translatePredicate(query.Predicate{subExp})
if err != nil {
return nil, err
}
and.Must(sq...)
}
qs = append(qs, and)
case *query.Or:
or := elastic.NewBoolQuery()
for _, subExp := range *t {
sq, err := translatePredicate(query.Predicate{subExp})
if err != nil {
return nil, err
}
or.Should(sq...)
}
qs = append(qs, or)
case *query.In:
qs = append(qs, elastic.NewTermsQuery(getField(t.Field, true), valuesToInterface(t.Values)...))
case *query.NotIn:
b := elastic.NewBoolQuery()
b.MustNot(elastic.NewTermsQuery(getField(t.Field, true), valuesToInterface(t.Values)...))
qs = append(qs, b)
case *query.Equal:
qs = append(qs, elastic.NewTermQuery(getField(t.Field, true), t.Value))
case *query.NotEqual:
b := elastic.NewBoolQuery()
b.MustNot(elastic.NewTermQuery(getField(t.Field, true), t.Value))
qs = append(qs, b)
case *query.GreaterThan:
r := elastic.NewRangeQuery(getField(t.Field, false)).Gt(t.Value)
qs = append(qs, r)
case *query.GreaterOrEqual:
r := elastic.NewRangeQuery(getField(t.Field, false)).Gte(t.Value)
qs = append(qs, r)
case *query.LowerThan:
r := elastic.NewRangeQuery(getField(t.Field, false)).Lt(t.Value)
qs = append(qs, r)
case *query.LowerOrEqual:
r := elastic.NewRangeQuery(getField(t.Field, false)).Lte(t.Value)
qs = append(qs, r)
default:
return nil, resource.ErrNotImplemented
}
}
return qs, nil
}