Refactor API, add aggregations and custom queries
This commit introduces a refactor of the codebase and the API, to make
it more user friendly. Queries can now directly be executed via the
`Run()` method. Internally, the library no longer uses JSON generation
as a major mechanism, instead all types need to implement a `Mappable`
interface which simply turns each type in a `map[string]interface{}`,
which is what the ElasticSearch client expects. This makes the code
easier to write, and makes writing tests less error prone, as JSON need
not be written directly.
Support for metrics aggregations is also added. However, aggregations of
type bucket, pipeline and matrix are not supported yet.
To make the library more useful in its current state, support is added
for running custom queries and aggregations, via the `CustomQuery()` and
`CustomAgg()` functions, which both accepts an arbitrary
`map[string]interface{}`.
2020-02-19 11:35:21 +00:00
|
|
|
package esquery
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
|
|
|
|
"github.com/elastic/go-elasticsearch/v7"
|
|
|
|
"github.com/elastic/go-elasticsearch/v7/esapi"
|
|
|
|
)
|
|
|
|
|
2020-02-27 14:19:07 +00:00
|
|
|
// AggregationRequest represents a complete request of type "aggregations"
|
|
|
|
// (a.k.a "aggs") to ElasticSearch's search API. It simply wraps a map of named
|
|
|
|
// aggregations, which are values of a type that implements the Mappable
|
|
|
|
// interface.
|
|
|
|
type AggregationRequest struct {
|
|
|
|
Aggs map[string]Mappable
|
|
|
|
}
|
|
|
|
|
|
|
|
// Aggregation is an interface that each aggregation type must implement. It
|
|
|
|
// is simply an extension of the Mappable interface to include a Named function,
|
|
|
|
// which returns the name of the aggregation.
|
Refactor API, add aggregations and custom queries
This commit introduces a refactor of the codebase and the API, to make
it more user friendly. Queries can now directly be executed via the
`Run()` method. Internally, the library no longer uses JSON generation
as a major mechanism, instead all types need to implement a `Mappable`
interface which simply turns each type in a `map[string]interface{}`,
which is what the ElasticSearch client expects. This makes the code
easier to write, and makes writing tests less error prone, as JSON need
not be written directly.
Support for metrics aggregations is also added. However, aggregations of
type bucket, pipeline and matrix are not supported yet.
To make the library more useful in its current state, support is added
for running custom queries and aggregations, via the `CustomQuery()` and
`CustomAgg()` functions, which both accepts an arbitrary
`map[string]interface{}`.
2020-02-19 11:35:21 +00:00
|
|
|
type Aggregation interface {
|
|
|
|
Mappable
|
|
|
|
Name() string
|
|
|
|
}
|
|
|
|
|
2020-02-27 14:19:07 +00:00
|
|
|
// Aggregate generates a search request of type "aggs", represented by a
|
|
|
|
// *AggregationRequest object. It receives a variadic amount of values that
|
|
|
|
// implement the Aggregation interface, whether provided internally by the
|
|
|
|
// library or custom aggregations provided by consuming code.
|
Refactor API, add aggregations and custom queries
This commit introduces a refactor of the codebase and the API, to make
it more user friendly. Queries can now directly be executed via the
`Run()` method. Internally, the library no longer uses JSON generation
as a major mechanism, instead all types need to implement a `Mappable`
interface which simply turns each type in a `map[string]interface{}`,
which is what the ElasticSearch client expects. This makes the code
easier to write, and makes writing tests less error prone, as JSON need
not be written directly.
Support for metrics aggregations is also added. However, aggregations of
type bucket, pipeline and matrix are not supported yet.
To make the library more useful in its current state, support is added
for running custom queries and aggregations, via the `CustomQuery()` and
`CustomAgg()` functions, which both accepts an arbitrary
`map[string]interface{}`.
2020-02-19 11:35:21 +00:00
|
|
|
func Aggregate(aggs ...Aggregation) *AggregationRequest {
|
|
|
|
req := &AggregationRequest{
|
|
|
|
Aggs: make(map[string]Mappable),
|
|
|
|
}
|
|
|
|
for _, agg := range aggs {
|
|
|
|
req.Aggs[agg.Name()] = agg
|
|
|
|
}
|
|
|
|
|
|
|
|
return req
|
|
|
|
}
|
|
|
|
|
2020-02-27 14:19:07 +00:00
|
|
|
// Map implements the Mappable interface. It converts the "aggs" request into a
|
|
|
|
// (potentially nested) map[string]interface{}.
|
Refactor API, add aggregations and custom queries
This commit introduces a refactor of the codebase and the API, to make
it more user friendly. Queries can now directly be executed via the
`Run()` method. Internally, the library no longer uses JSON generation
as a major mechanism, instead all types need to implement a `Mappable`
interface which simply turns each type in a `map[string]interface{}`,
which is what the ElasticSearch client expects. This makes the code
easier to write, and makes writing tests less error prone, as JSON need
not be written directly.
Support for metrics aggregations is also added. However, aggregations of
type bucket, pipeline and matrix are not supported yet.
To make the library more useful in its current state, support is added
for running custom queries and aggregations, via the `CustomQuery()` and
`CustomAgg()` functions, which both accepts an arbitrary
`map[string]interface{}`.
2020-02-19 11:35:21 +00:00
|
|
|
func (req *AggregationRequest) Map() map[string]interface{} {
|
|
|
|
m := make(map[string]interface{})
|
|
|
|
|
|
|
|
for name, agg := range req.Aggs {
|
|
|
|
m[name] = agg.Map()
|
|
|
|
}
|
|
|
|
|
|
|
|
return map[string]interface{}{
|
|
|
|
"aggs": m,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 14:19:07 +00:00
|
|
|
// MarshalJSON implements the json.Marshaler interface, it simply encodes the
|
|
|
|
// map representation of the request (provided by the Map method) as JSON.
|
2020-02-20 14:45:08 +00:00
|
|
|
func (req *AggregationRequest) MarshalJSON() ([]byte, error) {
|
|
|
|
return json.Marshal(req.Map())
|
|
|
|
}
|
|
|
|
|
2020-02-27 14:19:07 +00:00
|
|
|
// Run executes the request using the provided ElasticSearch client. Zero or
|
|
|
|
// more search options can be provided as well. It returns the standard Response
|
|
|
|
// type of the official Go client.
|
Refactor API, add aggregations and custom queries
This commit introduces a refactor of the codebase and the API, to make
it more user friendly. Queries can now directly be executed via the
`Run()` method. Internally, the library no longer uses JSON generation
as a major mechanism, instead all types need to implement a `Mappable`
interface which simply turns each type in a `map[string]interface{}`,
which is what the ElasticSearch client expects. This makes the code
easier to write, and makes writing tests less error prone, as JSON need
not be written directly.
Support for metrics aggregations is also added. However, aggregations of
type bucket, pipeline and matrix are not supported yet.
To make the library more useful in its current state, support is added
for running custom queries and aggregations, via the `CustomQuery()` and
`CustomAgg()` functions, which both accepts an arbitrary
`map[string]interface{}`.
2020-02-19 11:35:21 +00:00
|
|
|
func (req *AggregationRequest) Run(
|
|
|
|
api *elasticsearch.Client,
|
|
|
|
o ...func(*esapi.SearchRequest),
|
|
|
|
) (res *esapi.Response, err error) {
|
|
|
|
var b bytes.Buffer
|
|
|
|
err = json.NewEncoder(&b).Encode(req.Map())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := append([]func(*esapi.SearchRequest){api.Search.WithBody(&b)}, o...)
|
|
|
|
|
|
|
|
return api.Search(opts...)
|
|
|
|
}
|
2020-02-27 14:19:07 +00:00
|
|
|
|
|
|
|
// RunSearch is the same as the Run method, except that it accepts a value of
|
|
|
|
// type esapi.Search (usually this is the Search field of an elasticsearch.Client
|
|
|
|
// object). Since the ElasticSearch client does not provide an interface type
|
|
|
|
// for its API (which would allow implementation of mock clients), this provides
|
|
|
|
// a workaround. The Search function in the ES client is actually a field of a
|
|
|
|
// function type.
|
|
|
|
func (req *AggregationRequest) RunSearch(
|
|
|
|
search esapi.Search,
|
|
|
|
o ...func(*esapi.SearchRequest),
|
|
|
|
) (res *esapi.Response, err error) {
|
|
|
|
var b bytes.Buffer
|
|
|
|
err = json.NewEncoder(&b).Encode(req.Map())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := append([]func(*esapi.SearchRequest){search.WithBody(&b)}, o...)
|
|
|
|
|
|
|
|
return search(opts...)
|
|
|
|
}
|