mirror of
https://github.com/grafana/grafana.git
synced 2025-01-27 00:37:04 -06:00
9fb8339f87
* Use integers for time range filter Previously it was passed as a string which is automatically converted by Elastic to a number only if the field type is "date". For other types (e.g. "long") such conversion doesn't work. In theory "date" could be passed as a formatted string but we don't use it this way and always pass it as a number so it is safe to always pass numbers, not strings. * Fix time_series_query_test * Retrigger build
464 lines
11 KiB
Go
464 lines
11 KiB
Go
package es
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/Masterminds/semver"
|
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
|
)
|
|
|
|
// SearchRequestBuilder represents a builder which can build a search request
|
|
type SearchRequestBuilder struct {
|
|
version *semver.Version
|
|
interval intervalv2.Interval
|
|
index string
|
|
size int
|
|
sort map[string]interface{}
|
|
queryBuilder *QueryBuilder
|
|
aggBuilders []AggBuilder
|
|
customProps map[string]interface{}
|
|
}
|
|
|
|
// NewSearchRequestBuilder create a new search request builder
|
|
func NewSearchRequestBuilder(version *semver.Version, interval intervalv2.Interval) *SearchRequestBuilder {
|
|
builder := &SearchRequestBuilder{
|
|
version: version,
|
|
interval: interval,
|
|
sort: make(map[string]interface{}),
|
|
customProps: make(map[string]interface{}),
|
|
aggBuilders: make([]AggBuilder, 0),
|
|
}
|
|
return builder
|
|
}
|
|
|
|
// Build builds and return a search request
|
|
func (b *SearchRequestBuilder) Build() (*SearchRequest, error) {
|
|
sr := SearchRequest{
|
|
Index: b.index,
|
|
Interval: b.interval,
|
|
Size: b.size,
|
|
Sort: b.sort,
|
|
CustomProps: b.customProps,
|
|
}
|
|
|
|
if b.queryBuilder != nil {
|
|
q, err := b.queryBuilder.Build()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sr.Query = q
|
|
}
|
|
|
|
if len(b.aggBuilders) > 0 {
|
|
sr.Aggs = make(AggArray, 0)
|
|
|
|
for _, ab := range b.aggBuilders {
|
|
aggArray, err := ab.Build()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sr.Aggs = append(sr.Aggs, aggArray...)
|
|
}
|
|
}
|
|
|
|
return &sr, nil
|
|
}
|
|
|
|
// Size sets the size of the search request
|
|
func (b *SearchRequestBuilder) Size(size int) *SearchRequestBuilder {
|
|
b.size = size
|
|
return b
|
|
}
|
|
|
|
// SortDesc adds a sort to the search request
|
|
func (b *SearchRequestBuilder) SortDesc(field, unmappedType string) *SearchRequestBuilder {
|
|
props := map[string]string{
|
|
"order": "desc",
|
|
}
|
|
|
|
if unmappedType != "" {
|
|
props["unmapped_type"] = unmappedType
|
|
}
|
|
|
|
b.sort[field] = props
|
|
|
|
return b
|
|
}
|
|
|
|
// AddDocValueField adds a doc value field to the search request
|
|
func (b *SearchRequestBuilder) AddDocValueField(field string) *SearchRequestBuilder {
|
|
// fields field not supported on version >= 5
|
|
if b.version.Major() < 5 {
|
|
b.customProps["fields"] = []string{"*", "_source"}
|
|
b.customProps["fielddata_fields"] = []string{field}
|
|
} else {
|
|
b.customProps["docvalue_fields"] = []string{field}
|
|
}
|
|
|
|
b.customProps["script_fields"] = make(map[string]interface{})
|
|
|
|
return b
|
|
}
|
|
|
|
// Query creates and return a query builder
|
|
func (b *SearchRequestBuilder) Query() *QueryBuilder {
|
|
if b.queryBuilder == nil {
|
|
b.queryBuilder = NewQueryBuilder()
|
|
}
|
|
return b.queryBuilder
|
|
}
|
|
|
|
// Agg initiate and returns a new aggregation builder
|
|
func (b *SearchRequestBuilder) Agg() AggBuilder {
|
|
aggBuilder := newAggBuilder(b.version)
|
|
b.aggBuilders = append(b.aggBuilders, aggBuilder)
|
|
return aggBuilder
|
|
}
|
|
|
|
// MultiSearchRequestBuilder represents a builder which can build a multi search request
|
|
type MultiSearchRequestBuilder struct {
|
|
version *semver.Version
|
|
requestBuilders []*SearchRequestBuilder
|
|
}
|
|
|
|
// NewMultiSearchRequestBuilder creates a new multi search request builder
|
|
func NewMultiSearchRequestBuilder(version *semver.Version) *MultiSearchRequestBuilder {
|
|
return &MultiSearchRequestBuilder{
|
|
version: version,
|
|
}
|
|
}
|
|
|
|
// Search initiates and returns a new search request builder
|
|
func (m *MultiSearchRequestBuilder) Search(interval intervalv2.Interval) *SearchRequestBuilder {
|
|
b := NewSearchRequestBuilder(m.version, interval)
|
|
m.requestBuilders = append(m.requestBuilders, b)
|
|
return b
|
|
}
|
|
|
|
// Build builds and return a multi search request
|
|
func (m *MultiSearchRequestBuilder) Build() (*MultiSearchRequest, error) {
|
|
requests := []*SearchRequest{}
|
|
for _, sb := range m.requestBuilders {
|
|
searchRequest, err := sb.Build()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
requests = append(requests, searchRequest)
|
|
}
|
|
|
|
return &MultiSearchRequest{
|
|
Requests: requests,
|
|
}, nil
|
|
}
|
|
|
|
// QueryBuilder represents a query builder
|
|
type QueryBuilder struct {
|
|
boolQueryBuilder *BoolQueryBuilder
|
|
}
|
|
|
|
// NewQueryBuilder create a new query builder
|
|
func NewQueryBuilder() *QueryBuilder {
|
|
return &QueryBuilder{}
|
|
}
|
|
|
|
// Build builds and return a query builder
|
|
func (b *QueryBuilder) Build() (*Query, error) {
|
|
q := Query{}
|
|
|
|
if b.boolQueryBuilder != nil {
|
|
b, err := b.boolQueryBuilder.Build()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
q.Bool = b
|
|
}
|
|
|
|
return &q, nil
|
|
}
|
|
|
|
// Bool creates and return a query builder
|
|
func (b *QueryBuilder) Bool() *BoolQueryBuilder {
|
|
if b.boolQueryBuilder == nil {
|
|
b.boolQueryBuilder = NewBoolQueryBuilder()
|
|
}
|
|
return b.boolQueryBuilder
|
|
}
|
|
|
|
// BoolQueryBuilder represents a bool query builder
|
|
type BoolQueryBuilder struct {
|
|
filterQueryBuilder *FilterQueryBuilder
|
|
}
|
|
|
|
// NewBoolQueryBuilder create a new bool query builder
|
|
func NewBoolQueryBuilder() *BoolQueryBuilder {
|
|
return &BoolQueryBuilder{}
|
|
}
|
|
|
|
// Filter creates and return a filter query builder
|
|
func (b *BoolQueryBuilder) Filter() *FilterQueryBuilder {
|
|
if b.filterQueryBuilder == nil {
|
|
b.filterQueryBuilder = NewFilterQueryBuilder()
|
|
}
|
|
return b.filterQueryBuilder
|
|
}
|
|
|
|
// Build builds and return a bool query builder
|
|
func (b *BoolQueryBuilder) Build() (*BoolQuery, error) {
|
|
boolQuery := BoolQuery{}
|
|
|
|
if b.filterQueryBuilder != nil {
|
|
filters, err := b.filterQueryBuilder.Build()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
boolQuery.Filters = filters
|
|
}
|
|
|
|
return &boolQuery, nil
|
|
}
|
|
|
|
// FilterQueryBuilder represents a filter query builder
|
|
type FilterQueryBuilder struct {
|
|
filters []Filter
|
|
}
|
|
|
|
// NewFilterQueryBuilder creates a new filter query builder
|
|
func NewFilterQueryBuilder() *FilterQueryBuilder {
|
|
return &FilterQueryBuilder{
|
|
filters: make([]Filter, 0),
|
|
}
|
|
}
|
|
|
|
// Build builds and return a filter query builder
|
|
func (b *FilterQueryBuilder) Build() ([]Filter, error) {
|
|
return b.filters, nil
|
|
}
|
|
|
|
// AddDateRangeFilter adds a new time range filter
|
|
func (b *FilterQueryBuilder) AddDateRangeFilter(timeField string, lte, gte int64, format string) *FilterQueryBuilder {
|
|
b.filters = append(b.filters, &RangeFilter{
|
|
Key: timeField,
|
|
Lte: lte,
|
|
Gte: gte,
|
|
Format: format,
|
|
})
|
|
return b
|
|
}
|
|
|
|
// AddQueryStringFilter adds a new query string filter
|
|
func (b *FilterQueryBuilder) AddQueryStringFilter(querystring string, analyseWildcard bool) *FilterQueryBuilder {
|
|
if len(strings.TrimSpace(querystring)) == 0 {
|
|
return b
|
|
}
|
|
|
|
b.filters = append(b.filters, &QueryStringFilter{
|
|
Query: querystring,
|
|
AnalyzeWildcard: analyseWildcard,
|
|
})
|
|
return b
|
|
}
|
|
|
|
// AggBuilder represents an aggregation builder
|
|
type AggBuilder interface {
|
|
Histogram(key, field string, fn func(a *HistogramAgg, b AggBuilder)) AggBuilder
|
|
DateHistogram(key, field string, fn func(a *DateHistogramAgg, b AggBuilder)) AggBuilder
|
|
Terms(key, field string, fn func(a *TermsAggregation, b AggBuilder)) AggBuilder
|
|
Filters(key string, fn func(a *FiltersAggregation, b AggBuilder)) AggBuilder
|
|
GeoHashGrid(key, field string, fn func(a *GeoHashGridAggregation, b AggBuilder)) AggBuilder
|
|
Metric(key, metricType, field string, fn func(a *MetricAggregation)) AggBuilder
|
|
Pipeline(key, pipelineType string, bucketPath interface{}, fn func(a *PipelineAggregation)) AggBuilder
|
|
Build() (AggArray, error)
|
|
}
|
|
|
|
type aggBuilderImpl struct {
|
|
AggBuilder
|
|
aggDefs []*aggDef
|
|
version *semver.Version
|
|
}
|
|
|
|
func newAggBuilder(version *semver.Version) *aggBuilderImpl {
|
|
return &aggBuilderImpl{
|
|
aggDefs: make([]*aggDef, 0),
|
|
version: version,
|
|
}
|
|
}
|
|
|
|
func (b *aggBuilderImpl) Build() (AggArray, error) {
|
|
aggs := make(AggArray, 0)
|
|
|
|
for _, aggDef := range b.aggDefs {
|
|
agg := &Agg{
|
|
Key: aggDef.key,
|
|
Aggregation: aggDef.aggregation,
|
|
}
|
|
|
|
for _, cb := range aggDef.builders {
|
|
childAggs, err := cb.Build()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
agg.Aggregation.Aggs = append(agg.Aggregation.Aggs, childAggs...)
|
|
}
|
|
|
|
aggs = append(aggs, agg)
|
|
}
|
|
|
|
return aggs, nil
|
|
}
|
|
|
|
func (b *aggBuilderImpl) Histogram(key, field string, fn func(a *HistogramAgg, b AggBuilder)) AggBuilder {
|
|
innerAgg := &HistogramAgg{
|
|
Field: field,
|
|
}
|
|
aggDef := newAggDef(key, &aggContainer{
|
|
Type: "histogram",
|
|
Aggregation: innerAgg,
|
|
})
|
|
|
|
if fn != nil {
|
|
builder := newAggBuilder(b.version)
|
|
aggDef.builders = append(aggDef.builders, builder)
|
|
fn(innerAgg, builder)
|
|
}
|
|
|
|
b.aggDefs = append(b.aggDefs, aggDef)
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *aggBuilderImpl) DateHistogram(key, field string, fn func(a *DateHistogramAgg, b AggBuilder)) AggBuilder {
|
|
innerAgg := &DateHistogramAgg{
|
|
Field: field,
|
|
}
|
|
aggDef := newAggDef(key, &aggContainer{
|
|
Type: "date_histogram",
|
|
Aggregation: innerAgg,
|
|
})
|
|
|
|
if fn != nil {
|
|
builder := newAggBuilder(b.version)
|
|
aggDef.builders = append(aggDef.builders, builder)
|
|
fn(innerAgg, builder)
|
|
}
|
|
|
|
if b.version.Major() >= 8 {
|
|
innerAgg.FixedInterval = innerAgg.Interval
|
|
innerAgg.Interval = ""
|
|
}
|
|
|
|
b.aggDefs = append(b.aggDefs, aggDef)
|
|
|
|
return b
|
|
}
|
|
|
|
const termsOrderTerm = "_term"
|
|
|
|
func (b *aggBuilderImpl) Terms(key, field string, fn func(a *TermsAggregation, b AggBuilder)) AggBuilder {
|
|
innerAgg := &TermsAggregation{
|
|
Field: field,
|
|
Order: make(map[string]interface{}),
|
|
}
|
|
aggDef := newAggDef(key, &aggContainer{
|
|
Type: "terms",
|
|
Aggregation: innerAgg,
|
|
})
|
|
|
|
if fn != nil {
|
|
builder := newAggBuilder(b.version)
|
|
aggDef.builders = append(aggDef.builders, builder)
|
|
fn(innerAgg, builder)
|
|
}
|
|
|
|
if b.version.Major() >= 6 && len(innerAgg.Order) > 0 {
|
|
if orderBy, exists := innerAgg.Order[termsOrderTerm]; exists {
|
|
innerAgg.Order["_key"] = orderBy
|
|
delete(innerAgg.Order, termsOrderTerm)
|
|
}
|
|
}
|
|
|
|
b.aggDefs = append(b.aggDefs, aggDef)
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *aggBuilderImpl) Filters(key string, fn func(a *FiltersAggregation, b AggBuilder)) AggBuilder {
|
|
innerAgg := &FiltersAggregation{
|
|
Filters: make(map[string]interface{}),
|
|
}
|
|
aggDef := newAggDef(key, &aggContainer{
|
|
Type: "filters",
|
|
Aggregation: innerAgg,
|
|
})
|
|
if fn != nil {
|
|
builder := newAggBuilder(b.version)
|
|
aggDef.builders = append(aggDef.builders, builder)
|
|
fn(innerAgg, builder)
|
|
}
|
|
|
|
b.aggDefs = append(b.aggDefs, aggDef)
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *aggBuilderImpl) GeoHashGrid(key, field string, fn func(a *GeoHashGridAggregation, b AggBuilder)) AggBuilder {
|
|
innerAgg := &GeoHashGridAggregation{
|
|
Field: field,
|
|
Precision: 5,
|
|
}
|
|
aggDef := newAggDef(key, &aggContainer{
|
|
Type: "geohash_grid",
|
|
Aggregation: innerAgg,
|
|
})
|
|
|
|
if fn != nil {
|
|
builder := newAggBuilder(b.version)
|
|
aggDef.builders = append(aggDef.builders, builder)
|
|
fn(innerAgg, builder)
|
|
}
|
|
|
|
b.aggDefs = append(b.aggDefs, aggDef)
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *aggBuilderImpl) Metric(key, metricType, field string, fn func(a *MetricAggregation)) AggBuilder {
|
|
innerAgg := &MetricAggregation{
|
|
Type: metricType,
|
|
Field: field,
|
|
Settings: make(map[string]interface{}),
|
|
}
|
|
|
|
aggDef := newAggDef(key, &aggContainer{
|
|
Type: metricType,
|
|
Aggregation: innerAgg,
|
|
})
|
|
|
|
if fn != nil {
|
|
fn(innerAgg)
|
|
}
|
|
|
|
b.aggDefs = append(b.aggDefs, aggDef)
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *aggBuilderImpl) Pipeline(key, pipelineType string, bucketPath interface{}, fn func(a *PipelineAggregation)) AggBuilder {
|
|
innerAgg := &PipelineAggregation{
|
|
BucketPath: bucketPath,
|
|
Settings: make(map[string]interface{}),
|
|
}
|
|
aggDef := newAggDef(key, &aggContainer{
|
|
Type: pipelineType,
|
|
Aggregation: innerAgg,
|
|
})
|
|
|
|
if fn != nil {
|
|
fn(innerAgg)
|
|
}
|
|
|
|
b.aggDefs = append(b.aggDefs, aggDef)
|
|
|
|
return b
|
|
}
|