Features: SearchAfter、Term Aggregation Order、Aggregation Include Filter Values (#14)
* Add support for multi_match queries * Add support for highlights * Add support for nested aggregations and filtered aggregations * Update README * Fix formatting * fix * feat: add support for search after * fix:set search after []string to []interface * fix:add .gitignore * feat:Support for term aggs order * Feat: Support include filter for termAggs * Update aggregations_test.go Fix conflict. * Update go.mod Co-authored-by: Caleb Champlin <caleb.champlin@gmail.com> Co-authored-by: Hardy <caoxiaozhen@secnium.com> Co-authored-by: Oran Moshai <12291998+oranmoshai@users.noreply.github.com>
This commit is contained in:
parent
fc16427405
commit
49a92fc25e
|
@ -0,0 +1 @@
|
||||||
|
.idea/
|
|
@ -61,8 +61,8 @@ func TestAggregations(t *testing.T) {
|
||||||
{
|
{
|
||||||
"a complex, multi-aggregation, nested",
|
"a complex, multi-aggregation, nested",
|
||||||
Aggregate(
|
Aggregate(
|
||||||
NestedAgg("categories","categories").
|
NestedAgg("categories", "categories").
|
||||||
Aggs(TermsAgg("type","outdoors")),
|
Aggs(TermsAgg("type", "outdoors")),
|
||||||
FilterAgg("filtered",
|
FilterAgg("filtered",
|
||||||
Term("type", "t-shirt")),
|
Term("type", "t-shirt")),
|
||||||
),
|
),
|
||||||
|
@ -72,9 +72,9 @@ func TestAggregations(t *testing.T) {
|
||||||
"nested": map[string]interface{}{
|
"nested": map[string]interface{}{
|
||||||
"path": "categories",
|
"path": "categories",
|
||||||
},
|
},
|
||||||
"aggs": map[string]interface{} {
|
"aggs": map[string]interface{}{
|
||||||
"type": map[string]interface{} {
|
"type": map[string]interface{}{
|
||||||
"terms": map[string]interface{} {
|
"terms": map[string]interface{}{
|
||||||
"field": "outdoors",
|
"field": "outdoors",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -83,7 +83,7 @@ func TestAggregations(t *testing.T) {
|
||||||
"filtered": map[string]interface{}{
|
"filtered": map[string]interface{}{
|
||||||
"filter": map[string]interface{}{
|
"filter": map[string]interface{}{
|
||||||
"term": map[string]interface{}{
|
"term": map[string]interface{}{
|
||||||
"type": map[string]interface{} {
|
"type": map[string]interface{}{
|
||||||
"value": "t-shirt",
|
"value": "t-shirt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -92,5 +92,69 @@ func TestAggregations(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"order for termsAggs",
|
||||||
|
//eq.Aggregate(eq.TermsAgg("a1", "FIELD1").Size(0).Aggs(eq.Sum("a2", "FIELD2.SUBFIELD")))
|
||||||
|
Aggregate(
|
||||||
|
TermsAgg("categories", "categories").
|
||||||
|
Order(map[string]string{"priceSum": "desc"}).
|
||||||
|
Size(5).Aggs(Sum("priceSum", "price"))),
|
||||||
|
map[string]interface{}{
|
||||||
|
"aggs": map[string]interface{}{
|
||||||
|
"categories": map[string]interface{}{
|
||||||
|
"terms": map[string]interface{}{
|
||||||
|
"field": "categories",
|
||||||
|
"order": map[string]interface{}{
|
||||||
|
"priceSum": "desc",
|
||||||
|
},
|
||||||
|
"size": 5,
|
||||||
|
},
|
||||||
|
"aggs": map[string]interface{}{
|
||||||
|
"priceSum": map[string]interface{}{
|
||||||
|
"sum": map[string]interface{}{
|
||||||
|
"field": "price",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Single include for termsAggs",
|
||||||
|
//eq.Aggregate(eq.TermsAgg("a1", "FIELD1").Size(0).Aggs(eq.Sum("a2", "FIELD2.SUBFIELD")))
|
||||||
|
Aggregate(
|
||||||
|
TermsAgg("categories", "categories").
|
||||||
|
Include("red.*|blue.*"),
|
||||||
|
),
|
||||||
|
map[string]interface{}{
|
||||||
|
"aggs": map[string]interface{}{
|
||||||
|
"categories": map[string]interface{}{
|
||||||
|
"terms": map[string]interface{}{
|
||||||
|
"field": "categories",
|
||||||
|
"include": "red.*|blue.*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Multi include for termsAggs",
|
||||||
|
//eq.Aggregate(eq.TermsAgg("a1", "FIELD1").Size(0).Aggs(eq.Sum("a2", "FIELD2.SUBFIELD")))
|
||||||
|
Aggregate(
|
||||||
|
TermsAgg("categories", "categories").
|
||||||
|
Include("red", "blue"),
|
||||||
|
),
|
||||||
|
map[string]interface{}{
|
||||||
|
"aggs": map[string]interface{}{
|
||||||
|
"categories": map[string]interface{}{
|
||||||
|
"terms": map[string]interface{}{
|
||||||
|
"field": "categories",
|
||||||
|
"include": []string{"red", "blue"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ type TermsAggregation struct {
|
||||||
shardSize *float64
|
shardSize *float64
|
||||||
showTermDoc *bool
|
showTermDoc *bool
|
||||||
aggs []Aggregation
|
aggs []Aggregation
|
||||||
|
order map[string]string
|
||||||
|
include []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TermsAgg creates a new aggregation of type "terms". The method name includes
|
// TermsAgg creates a new aggregation of type "terms". The method name includes
|
||||||
|
@ -54,6 +56,18 @@ func (agg *TermsAggregation) Aggs(aggs ...Aggregation) *TermsAggregation {
|
||||||
return agg
|
return agg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Order sets the sort for terms agg
|
||||||
|
func (agg *TermsAggregation) Order(order map[string]string) *TermsAggregation {
|
||||||
|
agg.order = order
|
||||||
|
return agg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include filter the values for buckets
|
||||||
|
func (agg *TermsAggregation) Include(include ...string) *TermsAggregation {
|
||||||
|
agg.include = include
|
||||||
|
return agg
|
||||||
|
}
|
||||||
|
|
||||||
// Map returns a map representation of the aggregation, thus implementing the
|
// Map returns a map representation of the aggregation, thus implementing the
|
||||||
// Mappable interface.
|
// Mappable interface.
|
||||||
func (agg *TermsAggregation) Map() map[string]interface{} {
|
func (agg *TermsAggregation) Map() map[string]interface{} {
|
||||||
|
@ -70,6 +84,18 @@ func (agg *TermsAggregation) Map() map[string]interface{} {
|
||||||
if agg.showTermDoc != nil {
|
if agg.showTermDoc != nil {
|
||||||
innerMap["show_term_doc_count_error"] = *agg.showTermDoc
|
innerMap["show_term_doc_count_error"] = *agg.showTermDoc
|
||||||
}
|
}
|
||||||
|
if agg.order != nil {
|
||||||
|
innerMap["order"] = agg.order
|
||||||
|
}
|
||||||
|
|
||||||
|
if agg.include != nil {
|
||||||
|
if len(agg.include) <= 1 {
|
||||||
|
innerMap["include"] = agg.include[0]
|
||||||
|
} else {
|
||||||
|
innerMap["include"] = agg.include
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
outerMap := map[string]interface{}{
|
outerMap := map[string]interface{}{
|
||||||
"terms": innerMap,
|
"terms": innerMap,
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1,9 +1,5 @@
|
||||||
github.com/elastic/go-elasticsearch v0.0.0 h1:Pd5fqOuBxKxv83b0+xOAJDAkziWYwFinWnBO0y+TZaA=
|
|
||||||
github.com/elastic/go-elasticsearch v0.0.0/go.mod h1:TkBSJBuTyFdBnrNqoPc54FN0vKf5c04IdM4zuStJ7xg=
|
|
||||||
github.com/elastic/go-elasticsearch/v7 v7.6.0 h1:sYpGLpEFHgLUKLsZUBfuaVI9QgHjS3JdH9fX4/z8QI8=
|
github.com/elastic/go-elasticsearch/v7 v7.6.0 h1:sYpGLpEFHgLUKLsZUBfuaVI9QgHjS3JdH9fX4/z8QI8=
|
||||||
github.com/elastic/go-elasticsearch/v7 v7.6.0/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4=
|
github.com/elastic/go-elasticsearch/v7 v7.6.0/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4=
|
||||||
github.com/elastic/go-elasticsearch/v8 v8.0.0-20200210103600-aff00e5adfde h1:Y9SZx8RQqFycLxi5W5eFmxMqnmijULVc3LMjBTtZQdM=
|
|
||||||
github.com/elastic/go-elasticsearch/v8 v8.0.0-20200210103600-aff00e5adfde/go.mod h1:xe9a/L2aeOgFKKgrO3ibQTnMdpAeL0GC+5/HpGScSa4=
|
|
||||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/jgroeneveld/schema v1.0.0 h1:J0E10CrOkiSEsw6dfb1IfrDJD14pf6QLVJ3tRPl/syI=
|
github.com/jgroeneveld/schema v1.0.0 h1:J0E10CrOkiSEsw6dfb1IfrDJD14pf6QLVJ3tRPl/syI=
|
||||||
|
|
33
search.go
33
search.go
|
@ -15,16 +15,18 @@ import (
|
||||||
// Not all features of the search API are currently supported, but a request can
|
// Not all features of the search API are currently supported, but a request can
|
||||||
// currently include a query, aggregations, and more.
|
// currently include a query, aggregations, and more.
|
||||||
type SearchRequest struct {
|
type SearchRequest struct {
|
||||||
aggs []Aggregation
|
aggs []Aggregation
|
||||||
explain *bool
|
explain *bool
|
||||||
from *uint64
|
from *uint64
|
||||||
highlight Mappable
|
highlight Mappable
|
||||||
postFilter Mappable
|
searchAfter []interface{}
|
||||||
query Mappable
|
postFilter Mappable
|
||||||
size *uint64
|
query Mappable
|
||||||
sort Sort
|
size *uint64
|
||||||
source Source
|
sort Sort
|
||||||
timeout *time.Duration
|
source Source
|
||||||
|
timeout *time.Duration
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search creates a new SearchRequest object, to be filled via method chaining.
|
// Search creates a new SearchRequest object, to be filled via method chaining.
|
||||||
|
@ -74,6 +76,12 @@ func (req *SearchRequest) Sort(name string, order Order) *SearchRequest {
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchAfter retrieve the sorted result
|
||||||
|
func (req *SearchRequest) SearchAfter(s ...interface{}) *SearchRequest {
|
||||||
|
req.searchAfter = append(req.searchAfter, s...)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
// Explain sets whether the ElasticSearch API should return an explanation for
|
// Explain sets whether the ElasticSearch API should return an explanation for
|
||||||
// how each hit's score was calculated.
|
// how each hit's score was calculated.
|
||||||
func (req *SearchRequest) Explain(b bool) *SearchRequest {
|
func (req *SearchRequest) Explain(b bool) *SearchRequest {
|
||||||
|
@ -106,6 +114,7 @@ func (req *SearchRequest) Highlight(highlight Mappable) *SearchRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Map implements the Mappable interface. It converts the request to into a
|
// Map implements the Mappable interface. It converts the request to into a
|
||||||
// nested map[string]interface{}, as expected by the go-elasticsearch library.
|
// nested map[string]interface{}, as expected by the go-elasticsearch library.
|
||||||
func (req *SearchRequest) Map() map[string]interface{} {
|
func (req *SearchRequest) Map() map[string]interface{} {
|
||||||
|
@ -142,6 +151,10 @@ func (req *SearchRequest) Map() map[string]interface{} {
|
||||||
if req.highlight != nil {
|
if req.highlight != nil {
|
||||||
m["highlight"] = req.highlight.Map()
|
m["highlight"] = req.highlight.Map()
|
||||||
}
|
}
|
||||||
|
if req.searchAfter != nil {
|
||||||
|
m["search_after"] = req.searchAfter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
source := req.source.Map()
|
source := req.source.Map()
|
||||||
if len(source) > 0 {
|
if len(source) > 0 {
|
||||||
|
|
|
@ -7,6 +7,13 @@ import (
|
||||||
|
|
||||||
func TestSearchMaps(t *testing.T) {
|
func TestSearchMaps(t *testing.T) {
|
||||||
runMapTests(t, []mapTest{
|
runMapTests(t, []mapTest{
|
||||||
|
{
|
||||||
|
"a simple query with search after",
|
||||||
|
Search().SearchAfter("_id", "name"),
|
||||||
|
map[string]interface{}{
|
||||||
|
"search_after": []string{"_id", "name"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"a simple match_all query with a size and no aggs",
|
"a simple match_all query with a size and no aggs",
|
||||||
Search().Query(MatchAll()).Size(20),
|
Search().Query(MatchAll()).Size(20),
|
||||||
|
|
Loading…
Reference in New Issue