mirror of
https://github.com/buger/goreplay.git
synced 2024-04-21 12:32:02 +00:00
263 lines
6.7 KiB
Go
263 lines
6.7 KiB
Go
// Copyright 2013 Matthew Baird
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package elastigo
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
//"log"
|
|
"strings"
|
|
)
|
|
|
|
// Query creates a new Query Dsl
|
|
func Query() *QueryDsl {
|
|
return &QueryDsl{}
|
|
}
|
|
|
|
/*
|
|
|
|
some ways to serialize
|
|
"query": {
|
|
"filtered": {
|
|
"query": {
|
|
"query_string": {
|
|
"default_operator": "OR",
|
|
"default_field": "_all",
|
|
"query": " actor:\"bob\" AND type:\"EventType\""
|
|
}
|
|
},
|
|
"filter": {
|
|
"range": {
|
|
"@timestamp": {
|
|
"from": "2012-12-29T16:52:48+00:00",
|
|
"to": "2012-12-29T17:52:48+00:00"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
"query" : {
|
|
"term" : { "user" : "kimchy" }
|
|
}
|
|
|
|
"query" : {
|
|
"match_all" : {}
|
|
},
|
|
*/
|
|
type QueryDsl struct {
|
|
QueryEmbed
|
|
FilterVal *FilterOp `json:"filter,omitempty"`
|
|
}
|
|
|
|
// The core Query Syntax can be embedded as a child of a variety of different parents
|
|
type QueryEmbed struct {
|
|
MatchAll *MatchAll `json:"match_all,omitempty"`
|
|
Terms map[string]string `json:"term,omitempty"`
|
|
Qs *QueryString `json:"query_string,omitempty"`
|
|
MultiMatch *MultiMatch `json:"multi_match,omitempty"`
|
|
FunctionScore map[string]interface{} `json:"function_score,omitempty"`
|
|
//Exist string `json:"_exists_,omitempty"`
|
|
}
|
|
|
|
// MarshalJSON provides custom marshalling to support the query dsl which is a conditional
|
|
// json format, not always the same parent/children
|
|
func (qd *QueryDsl) MarshalJSON() ([]byte, error) {
|
|
q := qd.QueryEmbed
|
|
hasQuery := false
|
|
if q.Qs != nil || len(q.Terms) > 0 || q.MatchAll != nil || q.MultiMatch != nil {
|
|
hasQuery = true
|
|
}
|
|
// If a query has a
|
|
if qd.FilterVal != nil && hasQuery {
|
|
queryB, err := json.Marshal(q)
|
|
if err != nil {
|
|
return queryB, err
|
|
}
|
|
filterB, err := json.Marshal(qd.FilterVal)
|
|
if err != nil {
|
|
return filterB, err
|
|
}
|
|
return []byte(fmt.Sprintf(`{"filtered":{"query":%s,"filter":%s}}`, queryB, filterB)), nil
|
|
}
|
|
return json.Marshal(q)
|
|
}
|
|
|
|
// get all
|
|
func (q *QueryDsl) All() *QueryDsl {
|
|
q.MatchAll = &MatchAll{""}
|
|
return q
|
|
}
|
|
|
|
// Range adds a RANGE FilterOp to the search query
|
|
// Legacy. Use the Filter() function instead
|
|
func (q *QueryDsl) Range(fop *FilterOp) *QueryDsl {
|
|
if q.FilterVal == nil {
|
|
q.FilterVal = fop
|
|
return q
|
|
}
|
|
|
|
return q
|
|
}
|
|
|
|
// Add a term search for a specific field
|
|
// Term("user","kimchy")
|
|
func (q *QueryDsl) Term(name, value string) *QueryDsl {
|
|
if len(q.Terms) == 0 {
|
|
q.Terms = make(map[string]string)
|
|
}
|
|
q.Terms[name] = value
|
|
return q
|
|
}
|
|
|
|
// FunctionScore sets functions to use to score the documents.
|
|
// http://www.elastic.co/guide/en/elasticsearch/reference/1.x/query-dsl-function-score-query.html
|
|
func (q *QueryDsl) FunctionScore(mode string, functions ...map[string]interface{}) *QueryDsl {
|
|
q.QueryEmbed.FunctionScore = map[string]interface{}{
|
|
"functions": functions,
|
|
"score_mode": mode,
|
|
}
|
|
return q
|
|
}
|
|
|
|
// The raw search strings (lucene valid)
|
|
func (q *QueryDsl) Search(searchFor string) *QueryDsl {
|
|
//I don't think this is right, it is not a filter.query, it should be q query?
|
|
qs := NewQueryString("", "")
|
|
q.QueryEmbed.Qs = &qs
|
|
q.QueryEmbed.Qs.Query = searchFor
|
|
return q
|
|
}
|
|
|
|
// Querystring operations
|
|
func (q *QueryDsl) Qs(qs *QueryString) *QueryDsl {
|
|
q.QueryEmbed.Qs = qs
|
|
return q
|
|
}
|
|
|
|
// SetLenient sets whether the query should ignore format based failures,
|
|
// such as passing in text to a number field.
|
|
func (q *QueryDsl) SetLenient(lenient bool) *QueryDsl {
|
|
q.QueryEmbed.Qs.Lenient = lenient
|
|
return q
|
|
}
|
|
|
|
// Fields in query_string search
|
|
// Fields("fieldname","search_for","","")
|
|
//
|
|
// Fields("fieldname,field2,field3","search_for","","")
|
|
//
|
|
// Fields("fieldname,field2,field3","search_for","field_exists","")
|
|
func (q *QueryDsl) Fields(fields, search, exists, missing string) *QueryDsl {
|
|
fieldList := strings.Split(fields, ",")
|
|
qs := NewQueryString("", "")
|
|
q.QueryEmbed.Qs = &qs
|
|
q.QueryEmbed.Qs.Query = search
|
|
if len(fieldList) == 1 {
|
|
q.QueryEmbed.Qs.DefaultField = fields
|
|
} else {
|
|
q.QueryEmbed.Qs.Fields = fieldList
|
|
}
|
|
q.QueryEmbed.Qs.Exists = exists
|
|
q.QueryEmbed.Qs.Missing = missing
|
|
return q
|
|
}
|
|
|
|
// Filter this query
|
|
func (q *QueryDsl) Filter(f *FilterOp) *QueryDsl {
|
|
q.FilterVal = f
|
|
return q
|
|
}
|
|
|
|
// MultiMatch allows searching against multiple fields.
|
|
func (q *QueryDsl) MultiMatch(s string, fields []string) *QueryDsl {
|
|
q.QueryEmbed.MultiMatch = &MultiMatch{Query: s, Fields: fields}
|
|
return q
|
|
}
|
|
|
|
type MultiMatch struct {
|
|
Query string `json:"query"`
|
|
Fields []string `json:"fields"`
|
|
}
|
|
|
|
type MatchAll struct {
|
|
All string `json:"-"`
|
|
}
|
|
|
|
// should we reuse QueryDsl here?
|
|
type QueryWrap struct {
|
|
Qs QueryString `json:"query_string,omitempty"`
|
|
}
|
|
|
|
// QueryString based search
|
|
func NewQueryString(field, query string) QueryString {
|
|
return QueryString{"", field, query, "", "", nil, false}
|
|
}
|
|
|
|
type QueryString struct {
|
|
DefaultOperator string `json:"default_operator,omitempty"`
|
|
DefaultField string `json:"default_field,omitempty"`
|
|
Query string `json:"query,omitempty"`
|
|
Exists string `json:"_exists_,omitempty"`
|
|
Missing string `json:"_missing_,omitempty"`
|
|
Fields []string `json:"fields,omitempty"`
|
|
Lenient bool `json:"lenient,omitempty"`
|
|
//_exists_:field1,
|
|
//_missing_:field1,
|
|
}
|
|
|
|
//I don't know how any of the Term stuff below is supposed to work. -mikeyoon
|
|
|
|
// Generic Term based (used in query, facet, filter)
|
|
type Term struct {
|
|
Terms Terms `json:"terms,omitempty"`
|
|
FilterVal *FilterWrap `json:"facet_filter,omitempty"`
|
|
}
|
|
|
|
type Terms struct {
|
|
Fields []string `json:"field,omitempty"`
|
|
Size string `json:"size,omitempty"`
|
|
Regex string `json:"regex,omitempty"`
|
|
}
|
|
|
|
func NewTerm(fields ...string) *Term {
|
|
m := &Term{Terms{Fields: fields}, nil}
|
|
return m
|
|
}
|
|
|
|
func (s *Term) Filter(fl ...interface{}) *Term {
|
|
if s.FilterVal == nil {
|
|
s.FilterVal = NewFilterWrap()
|
|
}
|
|
|
|
s.FilterVal.addFilters(fl)
|
|
return s
|
|
}
|
|
|
|
// Custom marshalling
|
|
func (t *Terms) MarshalJSON() ([]byte, error) {
|
|
m := make(map[string]interface{})
|
|
// TODO: this isn't getting called!?
|
|
if len(t.Fields) == 1 {
|
|
m["field"] = t.Fields[0]
|
|
} else if len(t.Fields) > 1 {
|
|
m["fields"] = t.Fields
|
|
}
|
|
if len(t.Regex) > 0 {
|
|
m["regex"] = t.Regex
|
|
}
|
|
if len(t.Size) > 0 {
|
|
m["size"] = t.Size
|
|
}
|
|
return json.Marshal(m)
|
|
}
|