493 lines
8.1 KiB
Go
493 lines
8.1 KiB
Go
package query
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
jsoniter "github.com/json-iterator/go"
|
|
)
|
|
|
|
// TODO: elastic/go-elasticsearch is working on Query DSL support.
|
|
// See https://github.com/elastic/go-elasticsearch/issues/42.
|
|
// We need refactor our query body builder when that is ready.
|
|
type Builder struct {
|
|
From int64 `json:"from,omitempty"`
|
|
Size int64 `json:"size,omitempty"`
|
|
Sorts []map[string]string `json:"sort,omitempty"`
|
|
*Query `json:",inline"`
|
|
*Aggregations `json:"aggs,omitempty"`
|
|
}
|
|
|
|
func NewBuilder() *Builder {
|
|
return &Builder{}
|
|
}
|
|
|
|
func (b *Builder) Bytes() ([]byte, error) {
|
|
return jsoniter.Marshal(b)
|
|
}
|
|
|
|
func (b *Builder) WithQuery(q *Query) *Builder {
|
|
|
|
if q == nil || q.Bool == nil || !q.IsValid() {
|
|
return b
|
|
}
|
|
|
|
b.Query = q
|
|
return b
|
|
}
|
|
|
|
func (b *Builder) WithAggregations(aggs *Aggregations) *Builder {
|
|
|
|
b.Aggregations = aggs
|
|
return b
|
|
}
|
|
|
|
func (b *Builder) WithFrom(n int64) *Builder {
|
|
b.From = n
|
|
return b
|
|
}
|
|
|
|
func (b *Builder) WithSize(n int64) *Builder {
|
|
b.Size = n
|
|
return b
|
|
}
|
|
|
|
func (b *Builder) WithSort(key, order string) *Builder {
|
|
if order == "" {
|
|
order = "desc"
|
|
}
|
|
b.Sorts = []map[string]string{{key: order}}
|
|
return b
|
|
}
|
|
|
|
// Query
|
|
|
|
type Query struct {
|
|
*Bool `json:"query,omitempty"`
|
|
}
|
|
|
|
func NewQuery() *Query {
|
|
return &Query{}
|
|
}
|
|
|
|
func (q *Query) WithBool(b *Bool) *Query {
|
|
if b == nil || !b.IsValid() {
|
|
return q
|
|
}
|
|
|
|
q.Bool = b
|
|
return q
|
|
}
|
|
|
|
// Aggregations
|
|
|
|
type Aggregations struct {
|
|
*CardinalityAggregation `json:"cardinality_aggregation,omitempty"`
|
|
*DateHistogramAggregation `json:"date_histogram_aggregation,omitempty"`
|
|
}
|
|
|
|
type CardinalityAggregation struct {
|
|
*Cardinality `json:"cardinality,omitempty"`
|
|
}
|
|
|
|
type Cardinality struct {
|
|
Field string `json:"field,omitempty"`
|
|
}
|
|
|
|
type DateHistogramAggregation struct {
|
|
*DateHistogram `json:"date_histogram,omitempty"`
|
|
}
|
|
|
|
type DateHistogram struct {
|
|
Field string `json:"field,omitempty"`
|
|
Interval string `json:"interval,omitempty"`
|
|
}
|
|
|
|
func NewAggregations() *Aggregations {
|
|
return &Aggregations{}
|
|
}
|
|
|
|
func (a *Aggregations) WithCardinalityAggregation(field string) *Aggregations {
|
|
|
|
a.CardinalityAggregation = &CardinalityAggregation{
|
|
&Cardinality{
|
|
Field: field,
|
|
},
|
|
}
|
|
|
|
return a
|
|
}
|
|
|
|
func (a *Aggregations) WithDateHistogramAggregation(field string, interval string) *Aggregations {
|
|
|
|
a.DateHistogramAggregation = &DateHistogramAggregation{
|
|
&DateHistogram{
|
|
Field: field,
|
|
Interval: interval,
|
|
},
|
|
}
|
|
|
|
return a
|
|
}
|
|
|
|
type Item interface {
|
|
IsValid() bool
|
|
}
|
|
|
|
// Example:
|
|
// {bool: {filter: <[]Match>}}
|
|
// {bool: {should: <[]Match>, minimum_should_match: 1}}
|
|
type Bool struct {
|
|
*Parameter `json:"bool,omitempty"`
|
|
}
|
|
|
|
type Parameter struct {
|
|
Filter []interface{} `json:"filter,omitempty"`
|
|
Should []interface{} `json:"should,omitempty"`
|
|
MustNot []interface{} `json:"must_not,omitempty"`
|
|
MinimumShouldMatch int32 `json:"minimum_should_match,omitempty"`
|
|
}
|
|
|
|
func NewBool() *Bool {
|
|
return &Bool{
|
|
&Parameter{},
|
|
}
|
|
}
|
|
|
|
func (b *Bool) IsValid() bool {
|
|
if (b.Filter == nil || len(b.Filter) == 0) &&
|
|
(b.Should == nil || len(b.Should) == 0) &&
|
|
(b.MustNot == nil || len(b.MustNot) == 0) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (b *Bool) AppendFilter(item Item) *Bool {
|
|
|
|
if reflect.ValueOf(item).IsNil() || !item.IsValid() {
|
|
return b
|
|
}
|
|
|
|
b.Filter = append(b.Filter, item)
|
|
return b
|
|
}
|
|
|
|
func (b *Bool) AppendMultiFilter(items []Item) *Bool {
|
|
|
|
if items == nil || len(items) == 0 {
|
|
return b
|
|
}
|
|
|
|
for _, item := range items {
|
|
if item.IsValid() {
|
|
b.Filter = append(b.Filter, item)
|
|
}
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *Bool) AppendShould(item Item) *Bool {
|
|
|
|
if reflect.ValueOf(item).IsNil() || !item.IsValid() {
|
|
return b
|
|
}
|
|
|
|
b.Should = append(b.Should, item)
|
|
return b
|
|
}
|
|
|
|
func (b *Bool) AppendMultiShould(items []Item) *Bool {
|
|
|
|
if items == nil || len(items) == 0 {
|
|
return b
|
|
}
|
|
|
|
for _, item := range items {
|
|
if item.IsValid() {
|
|
b.Should = append(b.Should, item)
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (b *Bool) AppendMustNot(item Item) *Bool {
|
|
|
|
if reflect.ValueOf(item).IsNil() || !item.IsValid() {
|
|
return b
|
|
}
|
|
|
|
b.MustNot = append(b.MustNot, item)
|
|
return b
|
|
}
|
|
|
|
func (b *Bool) AppendMultiMustNot(items []Item) *Bool {
|
|
|
|
if items == nil || len(items) == 0 {
|
|
return b
|
|
}
|
|
|
|
for _, item := range items {
|
|
if item.IsValid() {
|
|
b.MustNot = append(b.MustNot, item)
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (b *Bool) WithMinimumShouldMatch(min int32) *Bool {
|
|
|
|
b.MinimumShouldMatch = min
|
|
return b
|
|
}
|
|
|
|
type MatchPhrase struct {
|
|
MatchPhrase map[string]string `json:"match_phrase,omitempty"`
|
|
}
|
|
|
|
func (m *MatchPhrase) IsValid() bool {
|
|
|
|
if m.MatchPhrase == nil || len(m.MatchPhrase) == 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func NewMatchPhrase(key, val string) *MatchPhrase {
|
|
return &MatchPhrase{
|
|
MatchPhrase: map[string]string{
|
|
key: val,
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewMultiMatchPhrase(key string, val []string) []Item {
|
|
|
|
var array []Item
|
|
|
|
if val == nil || len(val) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, v := range val {
|
|
array = append(array, &MatchPhrase{
|
|
MatchPhrase: map[string]string{
|
|
key: v,
|
|
},
|
|
})
|
|
}
|
|
|
|
return array
|
|
}
|
|
|
|
type MatchPhrasePrefix struct {
|
|
MatchPhrasePrefix map[string]string `json:"match_phrase_prefix,omitempty"`
|
|
}
|
|
|
|
func (m *MatchPhrasePrefix) IsValid() bool {
|
|
|
|
if m.MatchPhrasePrefix == nil || len(m.MatchPhrasePrefix) == 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func NewMatchPhrasePrefix(key, val string) *MatchPhrasePrefix {
|
|
return &MatchPhrasePrefix{
|
|
MatchPhrasePrefix: map[string]string{
|
|
key: val,
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewMultiMatchPhrasePrefix(key string, val []string) []Item {
|
|
|
|
var array []Item
|
|
|
|
if val == nil || len(val) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, v := range val {
|
|
array = append(array, &MatchPhrasePrefix{
|
|
MatchPhrasePrefix: map[string]string{
|
|
key: v,
|
|
},
|
|
})
|
|
}
|
|
|
|
return array
|
|
}
|
|
|
|
type Regexp struct {
|
|
Regexp map[string]string `json:"regexp,omitempty"`
|
|
}
|
|
|
|
func (m *Regexp) IsValid() bool {
|
|
|
|
if m.Regexp == nil || len(m.Regexp) == 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func NewRegex(key, val string) *Regexp {
|
|
return &Regexp{
|
|
Regexp: map[string]string{
|
|
key: val,
|
|
},
|
|
}
|
|
}
|
|
|
|
type Range struct {
|
|
Range map[string]map[string]interface{} `json:"range,omitempty"`
|
|
}
|
|
|
|
func NewRange(key string) *Range {
|
|
return &Range{
|
|
Range: map[string]map[string]interface{}{
|
|
key: make(map[string]interface{}),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *Range) WithGT(val interface{}) *Range {
|
|
r.withRange("gt", val)
|
|
return r
|
|
}
|
|
|
|
func (r *Range) WithGTE(val interface{}) *Range {
|
|
r.withRange("gte", val)
|
|
return r
|
|
}
|
|
|
|
func (r *Range) WithLT(val interface{}) *Range {
|
|
r.withRange("lt", val)
|
|
return r
|
|
}
|
|
|
|
func (r *Range) WithLTE(val interface{}) *Range {
|
|
r.withRange("lte", val)
|
|
return r
|
|
}
|
|
|
|
func (r *Range) IsValid() bool {
|
|
if r.Range == nil {
|
|
return false
|
|
}
|
|
|
|
if len(r.Range) == 0 {
|
|
return false
|
|
}
|
|
|
|
for _, v := range r.Range {
|
|
if len(v) != 0 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (r *Range) withRange(operator string, val interface{}) {
|
|
if r.Range == nil {
|
|
return
|
|
}
|
|
|
|
for _, v := range r.Range {
|
|
v[operator] = val
|
|
}
|
|
}
|
|
|
|
type Wildcard struct {
|
|
Wildcard map[string]string `json:"wildcard,omitempty"`
|
|
}
|
|
|
|
func (m *Wildcard) IsValid() bool {
|
|
|
|
if m.Wildcard == nil || len(m.Wildcard) == 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func NewWildcard(key, val string) *Wildcard {
|
|
|
|
return &Wildcard{
|
|
Wildcard: map[string]string{
|
|
key: val,
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewMultiWildcard(key string, val []string) []Item {
|
|
|
|
var array []Item
|
|
|
|
if val == nil || len(val) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, v := range val {
|
|
array = append(array, &Wildcard{
|
|
Wildcard: map[string]string{
|
|
key: v,
|
|
},
|
|
})
|
|
}
|
|
|
|
return array
|
|
}
|
|
|
|
type Terms struct {
|
|
Terms map[string]interface{} `json:"terms,omitempty"`
|
|
}
|
|
|
|
func (m *Terms) IsValid() bool {
|
|
|
|
if m.Terms == nil || len(m.Terms) == 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func NewTerms(key string, val interface{}) *Terms {
|
|
|
|
if reflect.ValueOf(val).IsNil() {
|
|
return nil
|
|
}
|
|
|
|
return &Terms{
|
|
Terms: map[string]interface{}{
|
|
key: val,
|
|
},
|
|
}
|
|
}
|
|
|
|
type Exists struct {
|
|
Exists map[string]string `json:"exists,omitempty"`
|
|
}
|
|
|
|
func (m *Exists) IsValid() bool {
|
|
|
|
if m.Exists == nil || len(m.Exists) == 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func NewExists(key, val string) *Exists {
|
|
return &Exists{
|
|
Exists: map[string]string{
|
|
key: val,
|
|
},
|
|
}
|
|
}
|