Compare commits

...

36 Commits

Author SHA1 Message Date
04bc4c24dc highlight fix 2023-11-02 16:12:39 +03:00
3b53a45f75 module change 2023-11-02 11:32:36 +03:00
031ad4ea23 Merge pull request 'Support Elasticsearch v8' (#7) from es8 into master
Reviewed-on: #7
2023-11-01 21:30:54 +00:00
90226c236a FIX import & go.sum & simple issues 2023-11-02 00:30:33 +03:00
bc3cec49c2 Merge remote-tracking branch 'origin/master' into es8 2023-11-02 00:14:33 +03:00
313fe61951 Merge pull request 'search-collapse' (#6) from search-collapse into master
Reviewed-on: #6
2023-11-01 21:11:51 +00:00
40cf057ca5 Merge remote-tracking branch 'origin/master' into search-collapse 2023-11-02 00:11:13 +03:00
1dc40acff9 Merge pull request 'Add support for combined_fields query' (#5) from combined-fields-query into master
Reviewed-on: #5
2023-10-30 12:08:39 +00:00
f1e864f6fe Merge pull request 'boost support added into match statement' (#4) from match-boost into master
Reviewed-on: #4
2023-10-30 12:08:17 +00:00
237a7c8fe5 Merge pull request 'date-histogram-agg' (#3) from date-histogram-agg into master
Reviewed-on: #3
2023-10-30 12:07:00 +00:00
0ccce02135 Merge pull request 'add support for geo_distance' (#2) from geo-distance into master
Reviewed-on: #2
2023-10-30 12:06:35 +00:00
99537b589e Merge pull request 'Update query_multi_match.go' (#1) from query_multi_match into master
Reviewed-on: #1
2023-10-30 12:00:22 +00:00
Bin
37bbc43bba
Update query_multi_match.go
MultiMatchType eq MatchTypeBestFields, field Can be empty
2023-07-12 10:44:59 +08:00
aliheydarabadii
a8129fde5a add support for geo_distance 2023-05-12 18:41:47 +03:30
Roman
5761bdcd5d Support Elasticsearch v8 2022-09-26 16:38:45 +03:00
Jacky Wu
2a3074fe89 doc: add date histogram agg in supported agg function list. 2022-01-21 14:15:54 +08:00
Jacky Wu
c2693c6401 feat: add date_histogram agg. 2022-01-21 14:09:06 +08:00
PesTospertnyj
5bad0e6d3e boost support added into match statement 2022-01-06 22:54:24 +02:00
k-yomo
11bb2b31eb Add support for combined_fields query 2021-12-21 22:00:39 +09:00
danta
4951f7774d feat: add support for collapse 2021-06-11 10:18:13 +08:00
Hardy
00431fc79f feat: Support Nested for joining queries. 2021-05-27 22:06:02 +08:00
Hardy
49a92fc25e
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>
2021-03-15 09:43:59 +02:00
Oran Moshai
0064054bc7
Update go.mod 2021-03-15 09:20:44 +02:00
Hardy
ca0cfb3224
Update aggregations_test.go
Fix conflict.
2021-03-15 10:42:44 +08:00
Hardy
c2b5f62823
Merge branch 'master' into master 2021-03-15 10:32:29 +08:00
Hardy
12616dd9d3 Feat: Support include filter for termAggs 2021-03-12 20:54:48 +08:00
Hardy
cc685d325e feat:Support for term aggs order 2021-03-12 19:37:19 +08:00
Hardy
e3c77e0849 fix:add .gitignore 2021-03-02 17:09:29 +08:00
Hardy
bdfe2df171 fix:set search after []string to []interface 2021-03-02 13:30:39 +08:00
Hardy
09acfd6a3d feat: add support for search after 2021-03-02 11:51:41 +08:00
Hardy
8661572bd5 fix 2021-03-01 16:21:23 +08:00
Caleb Champlin
37dac903cc Fix formatting 2020-10-17 13:46:37 -06:00
Caleb Champlin
52ccf20965 Update README 2020-10-17 13:43:02 -06:00
Caleb Champlin
39f0dd59c1 Add support for nested aggregations and filtered aggregations 2020-10-17 13:42:50 -06:00
Caleb Champlin
f7d496389e Add support for highlights 2020-10-17 13:42:23 -06:00
Caleb Champlin
cf3d8d5827 Add support for multi_match queries 2020-10-17 13:41:45 -06:00
25 changed files with 614 additions and 85 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea/

View File

@ -35,7 +35,7 @@ This is an early release, API may still change.
`esquery` is a Go module. To install, simply run this in your project's root directory:
```bash
go get github.com/aquasecurity/esquery
go get github.com/aquasecurity/esquery/v8
```
## Usage
@ -51,8 +51,8 @@ import (
"context"
"log"
"github.com/aquasecurity/esquery"
"github.com/elastic/go-elasticsearch/v7"
"github.com/aquasecurity/esquery/v8"
"github.com/elastic/go-elasticsearch/v8"
)
func main() {
@ -158,6 +158,7 @@ The following aggregations are currently supported:
| `"string_stats"` | `StringStats()` |
| `"top_hits"` | `TopHits()` |
| `"terms"` | `TermsAgg()` |
| `"date_histogram"` | `DateHistogramAgg()` |
### Supported Top Level Options

View File

@ -61,8 +61,15 @@ func TestAggregations(t *testing.T) {
{
"a complex, multi-aggregation, nested",
Aggregate(
NestedAgg("categories","categories").
Aggs(TermsAgg("type","outdoors")),
NestedAgg("categories", "categories").
Aggs(
TermsAgg("type", "outdoors").Aggs(
DateHistogramAgg("time", "timestamp").
Fixedinterval("3m").MinDocCount(0).Aggs(
Sum("sumPeople", "people"),
),
),
),
FilterAgg("filtered",
Term("type", "t-shirt")),
),
@ -72,18 +79,34 @@ func TestAggregations(t *testing.T) {
"nested": map[string]interface{}{
"path": "categories",
},
"aggs": map[string]interface{} {
"type": map[string]interface{} {
"terms": map[string]interface{} {
"aggs": map[string]interface{}{
"type": map[string]interface{}{
"terms": map[string]interface{}{
"field": "outdoors",
},
"aggs": map[string]interface{}{
"time": map[string]interface{}{
"date_histogram": map[string]interface{}{
"field": "timestamp",
"fixed_interval": "3m",
"min_doc_count": 0,
},
"aggs": map[string]interface{}{
"sumPeople": map[string]interface{}{
"sum": map[string]interface{}{
"field": "people",
},
},
},
},
},
},
},
},
"filtered": map[string]interface{}{
"filter": map[string]interface{}{
"term": map[string]interface{}{
"type": map[string]interface{} {
"type": map[string]interface{}{
"value": "t-shirt",
},
},
@ -92,5 +115,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"},
},
},
},
},
},
})
}

View File

@ -12,6 +12,8 @@ type TermsAggregation struct {
shardSize *float64
showTermDoc *bool
aggs []Aggregation
order map[string]string
include []string
}
// 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
}
// 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
// Mappable interface.
func (agg *TermsAggregation) Map() map[string]interface{} {
@ -70,6 +84,18 @@ func (agg *TermsAggregation) Map() map[string]interface{} {
if agg.showTermDoc != nil {
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{}{
"terms": innerMap,
@ -84,3 +110,144 @@ func (agg *TermsAggregation) Map() map[string]interface{} {
return outerMap
}
//----------------------------------------------------------------------------//
// DateHistogramAggregation represents an aggregation of type "date_histogram", as described in
// https://www.elastic.co/guide/en/elasticsearch/reference/current/
// search-aggregations-bucket-datehistogram-aggregation.html
type DateHistogramAggregation struct {
name string
field string
calendarInterval string
fixedInterval string
format string
offset string
keyed *bool
minDocCount *uint64
missing string
order map[string]string
aggs []Aggregation
}
// DateHistogramAgg creates a new aggregation of type "date_histogram".
func DateHistogramAgg(name, field string) *DateHistogramAggregation {
return &DateHistogramAggregation{
name: name,
field: field,
}
}
// Name returns the name of the aggregation.
func (agg *DateHistogramAggregation) Name() string {
return agg.name
}
// Aggs sets sub-aggregations for the aggregation.
func (agg *DateHistogramAggregation) Aggs(aggs ...Aggregation) *DateHistogramAggregation {
agg.aggs = aggs
return agg
}
// CalendarInterval sets calendarInterval
func (agg *DateHistogramAggregation) CalendarInterval(interval string) *DateHistogramAggregation {
agg.calendarInterval = interval
return agg
}
// Fixedinterval sets fixedInterval
func (agg *DateHistogramAggregation) Fixedinterval(interval string) *DateHistogramAggregation {
agg.fixedInterval = interval
return agg
}
// Format sets format
func (agg *DateHistogramAggregation) Format(format string) *DateHistogramAggregation {
agg.format = format
return agg
}
// Offset sets offset
func (agg *DateHistogramAggregation) Offset(offset string) *DateHistogramAggregation {
agg.offset = offset
return agg
}
// Order sets the sort for terms agg
func (agg *DateHistogramAggregation) Order(order map[string]string) *DateHistogramAggregation {
agg.order = order
return agg
}
// Keyed sets keyed is true or false
func (agg *DateHistogramAggregation) Keyed(keyed bool) *DateHistogramAggregation {
agg.keyed = &keyed
return agg
}
// Missing sets missing value
func (agg *DateHistogramAggregation) Missing(missing string) *DateHistogramAggregation {
agg.missing = missing
return agg
}
// MinDocCount sets min doc count
func (agg *DateHistogramAggregation) MinDocCount(minDocCount uint64) *DateHistogramAggregation {
agg.minDocCount = &minDocCount
return agg
}
// Map returns a map representation of the aggregation, thus implementing the
// Mappable interface.
func (agg *DateHistogramAggregation) Map() map[string]interface{} {
innerMap := map[string]interface{}{
"field": agg.field,
}
if agg.calendarInterval != "" {
innerMap["calendar_interval"] = agg.calendarInterval
}
if agg.fixedInterval != "" {
innerMap["fixed_interval"] = agg.fixedInterval
}
if agg.format != "" {
innerMap["format"] = agg.format
}
if agg.offset != "" {
innerMap["offset"] = agg.offset
}
if agg.missing != "" {
innerMap["missing"] = agg.missing
}
if agg.minDocCount != nil {
innerMap["min_doc_count"] = agg.minDocCount
}
if agg.keyed != nil {
innerMap["keyed"] = *agg.keyed
}
if agg.order != nil {
innerMap["order"] = agg.order
}
outerMap := map[string]interface{}{
"date_histogram": innerMap,
}
if len(agg.aggs) > 0 {
subAggs := make(map[string]map[string]interface{})
for _, sub := range agg.aggs {
subAggs[sub.Name()] = sub.Map()
}
outerMap["aggs"] = subAggs
}
return outerMap
}

View File

@ -6,7 +6,7 @@ type FilterAggregation struct {
aggs []Aggregation
}
// Filter creates a new aggregation of type "filter". The method name includes
// FilterAgg creates a new aggregation of type "filter". The method name includes
// the "Agg" suffix to prevent conflict with the "filter" query.
func FilterAgg(name string, filter Mappable) *FilterAggregation {
return &FilterAggregation{

View File

@ -44,8 +44,7 @@ func (agg *BaseAgg) Map() map[string]interface{} {
}
// AvgAgg represents an aggregation of type "avg", as described in
// https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-avg-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-avg-aggregation.html
type AvgAgg struct {
*BaseAgg `structs:",flatten"`
}
@ -68,8 +67,7 @@ func (agg *AvgAgg) Missing(val interface{}) *AvgAgg {
//----------------------------------------------------------------------------//
// WeightedAvgAgg represents an aggregation of type "weighted_avg", as described
// in https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-weight-avg-aggregation.html
// in https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-weight-avg-aggregation.html
type WeightedAvgAgg struct {
name string
apiName string
@ -106,7 +104,7 @@ func (agg *WeightedAvgAgg) Value(field string, missing ...interface{}) *Weighted
return agg
}
// Value sets the weight field and optionally a value to use when records are
// Weight sets the weight field and optionally a value to use when records are
// missing a value for the field.
func (agg *WeightedAvgAgg) Weight(field string, missing ...interface{}) *WeightedAvgAgg {
agg.Weig = new(BaseAggParams)
@ -128,8 +126,7 @@ func (agg *WeightedAvgAgg) Map() map[string]interface{} {
//----------------------------------------------------------------------------//
// CardinalityAgg represents an aggregation of type "cardinality", as described
// in https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-cardinality-aggregation.html
// in https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-cardinality-aggregation.html
type CardinalityAgg struct {
*BaseAgg `structs:",flatten"`
@ -169,8 +166,7 @@ func (agg *CardinalityAgg) Map() map[string]interface{} {
//----------------------------------------------------------------------------//
// MaxAgg represents an aggregation of type "max", as described in:
// https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-max-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-max-aggregation.html
type MaxAgg struct {
*BaseAgg `structs:",flatten"`
}
@ -193,8 +189,7 @@ func (agg *MaxAgg) Missing(val interface{}) *MaxAgg {
//----------------------------------------------------------------------------//
// MinAgg represents an aggregation of type "min", as described in:
// https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-min-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-min-aggregation.html
type MinAgg struct {
*BaseAgg `structs:",flatten"`
}
@ -217,8 +212,7 @@ func (agg *MinAgg) Missing(val interface{}) *MinAgg {
//----------------------------------------------------------------------------//
// SumAgg represents an aggregation of type "sum", as described in:
// https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-sum-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-sum-aggregation.html
type SumAgg struct {
*BaseAgg `structs:",flatten"`
}
@ -241,8 +235,7 @@ func (agg *SumAgg) Missing(val interface{}) *SumAgg {
//----------------------------------------------------------------------------//
// ValueCountAgg represents an aggregation of type "value_count", as described
// in https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-valuecount-aggregation.html
// in https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html
type ValueCountAgg struct {
*BaseAgg `structs:",flatten"`
}
@ -258,8 +251,7 @@ func ValueCount(name, field string) *ValueCountAgg {
//----------------------------------------------------------------------------//
// PercentilesAgg represents an aggregation of type "percentiles", as described
// in https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-percentile-aggregation.html
// in https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-percentile-aggregation.html
type PercentilesAgg struct {
*BaseAgg `structs:",flatten"`
@ -334,8 +326,7 @@ func (agg *PercentilesAgg) Map() map[string]interface{} {
//----------------------------------------------------------------------------//
// StatsAgg represents an aggregation of type "stats", as described in:
// https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-stats-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-stats-aggregation.html
type StatsAgg struct {
*BaseAgg `structs:",flatten"`
}
@ -357,8 +348,7 @@ func (agg *StatsAgg) Missing(val interface{}) *StatsAgg {
// ---------------------------------------------------------------------------//
// StringStatsAgg represents an aggregation of type "string_stats", as described
// in https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-string-stats-aggregation.html
// in https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-string-stats-aggregation.html
type StringStatsAgg struct {
*BaseAgg `structs:",flatten"`
@ -399,8 +389,7 @@ func (agg *StringStatsAgg) Map() map[string]interface{} {
// ---------------------------------------------------------------------------//
// TopHitsAgg represents an aggregation of type "top_hits", as described
// in https://www.elastic.co/guide/en/elasticsearch/reference/
// current/search-aggregations-metrics-top-hits-aggregation.html
// in https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-top-hits-aggregation.html
type TopHitsAgg struct {
name string
from uint64

View File

@ -20,7 +20,7 @@ func (agg *NestedAggregation) Name() string {
return agg.name
}
// NumberOfFragments sets the aggregations path
// Path sets the aggregations path
func (agg *NestedAggregation) Path(p string) *NestedAggregation {
agg.path = p
return agg

View File

@ -4,8 +4,8 @@ import (
"bytes"
"encoding/json"
"github.com/elastic/go-elasticsearch/v7"
"github.com/elastic/go-elasticsearch/v7/esapi"
"github.com/elastic/go-elasticsearch/v8"
"github.com/elastic/go-elasticsearch/v8/esapi"
)
// CountRequest represents a request to get the number of matches for a search

View File

@ -1,8 +1,8 @@
package esquery
import (
"github.com/elastic/go-elasticsearch/v7"
"github.com/elastic/go-elasticsearch/v7/esapi"
"github.com/elastic/go-elasticsearch/v8"
"github.com/elastic/go-elasticsearch/v8/esapi"
)
// CustomQueryMap represents an arbitrary query map for custom queries.
@ -21,7 +21,7 @@ func CustomQuery(m map[string]interface{}) *CustomQueryMap {
// Map returns the custom query as a map[string]interface{}, thus implementing
// the Mappable interface.
func (m *CustomQueryMap) Map() map[string]interface{} {
return map[string]interface{}(*m)
return *m
}
// Run executes the custom query using the provided ElasticSearch client. Zero

View File

@ -4,8 +4,8 @@ import (
"bytes"
"encoding/json"
"github.com/elastic/go-elasticsearch/v7"
"github.com/elastic/go-elasticsearch/v7/esapi"
"github.com/elastic/go-elasticsearch/v8"
"github.com/elastic/go-elasticsearch/v8/esapi"
)
// DeleteRequest represents a request to ElasticSearch's Delete By Query API,

4
es.go
View File

@ -32,8 +32,8 @@
// "context"
// "log"
//
// "github.com/aquasecurity/esquery"
// "github.com/elastic/go-elasticsearch/v7"
// "github.com/aquasecurity/esquery/v8"
// "github.com/elastic/go-elasticsearch/v8"
// )
//
// func main() {

12
go.mod
View File

@ -1,10 +1,14 @@
module github.com/aquasecurity/esquery
module git.wilix.dev/golib/esquery
go 1.13
go 1.20
require (
github.com/elastic/go-elasticsearch/v7 v7.6.0
github.com/elastic/go-elasticsearch/v8 v8.10.1
github.com/fatih/structs v1.1.0
github.com/jgroeneveld/schema v1.0.0 // indirect
github.com/jgroeneveld/trial v2.0.0+incompatible
)
require (
github.com/elastic/elastic-transport-go/v8 v8.3.0 // indirect
github.com/jgroeneveld/schema v1.0.0 // indirect
)

10
go.sum
View File

@ -1,9 +1,7 @@
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/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/elastic/elastic-transport-go/v8 v8.3.0 h1:DJGxovyQLXGr62e9nDMPSxRyWION0Bh6d9eCFBriiHo=
github.com/elastic/elastic-transport-go/v8 v8.3.0/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI=
github.com/elastic/go-elasticsearch/v8 v8.10.1 h1:JJ3i2DimYTsJcUoEGbg6tNB0eehTNdid9c5kTR1TGuI=
github.com/elastic/go-elasticsearch/v8 v8.10.1/go.mod h1:GU1BJHO7WeamP7UhuElYwzzHtvf9SDmeVpSSy9+o6Qg=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/jgroeneveld/schema v1.0.0 h1:J0E10CrOkiSEsw6dfb1IfrDJD14pf6QLVJ3tRPl/syI=

View File

@ -9,7 +9,7 @@ import (
func (q *QueryHighlight) Map() map[string]interface{} {
results := structs.Map(q.params)
if q.highlightQuery != nil {
results["query"] = q.highlightQuery.Map()
results["highlight_query"] = q.highlightQuery.Map()
}
if q.fields != nil && len(q.fields) > 0 {
fields := make(map[string]interface{})
@ -252,8 +252,9 @@ func (a HighlightBoundaryScanner) String() string {
return "sentence"
case BoundaryScannerWord:
return "word"
default:
return ""
}
return ""
}
type HighlightEncoder uint8
@ -281,7 +282,7 @@ func (a HighlightEncoder) String() string {
type HighlightFragmenter uint8
const (
// FragmentSpan is the "span" value
// FragmenterSpan is the "span" value
FragmenterSpan HighlightFragmenter = iota
// FragmenterSimple is the "simple" value
@ -336,6 +337,7 @@ func (a HighlightTagsSchema) String() string {
switch a {
case TagsSchemaStyled:
return "styled"
default:
return ""
}
return ""
}

View File

@ -32,7 +32,7 @@ func (q *BoolQuery) Filter(filter ...Mappable) *BoolQuery {
return q
}
// Must adds one or more queries of type "must_not" to the bool query. MustNot
// MustNot adds one or more queries of type "must_not" to the bool query. MustNot
// can be called multiple times, queries will be appended to existing ones.
func (q *BoolQuery) MustNot(mustnot ...Mappable) *BoolQuery {
q.mustNot = append(q.mustNot, mustnot...)

85
query_combined_fields.go Normal file
View File

@ -0,0 +1,85 @@
package esquery
import "github.com/fatih/structs"
type CombinedFieldsQuery struct {
params combinedFieldsParams
}
// Map returns a map representation of the query; implementing the
// Mappable interface.
func (q *CombinedFieldsQuery) Map() map[string]interface{} {
return map[string]interface{}{
"combined_fields": structs.Map(q.params),
}
}
type combinedFieldsParams struct {
Qry interface{} `structs:"query"`
Fields []string `structs:"fields"`
Boost float32 `structs:"boost,omitempty"`
AutoGenerate *bool `structs:"auto_generate_synonyms_phrase_query,omitempty"`
Op MatchOperator `structs:"operator,string,omitempty"`
MinMatch string `structs:"minimum_should_match,omitempty"`
ZeroTerms ZeroTerms `structs:"zero_terms_query,string,omitempty"`
}
// CombinedFields creates a new query of type "combined_fields"
func CombinedFields(simpleQuery interface{}) *CombinedFieldsQuery {
return newCombinedFields(simpleQuery)
}
func newCombinedFields(simpleQuery interface{}) *CombinedFieldsQuery {
return &CombinedFieldsQuery{
params: combinedFieldsParams{
Qry: simpleQuery,
},
}
}
// Query sets the data to find in the query's field (it is the "query" component
// of the query).
func (q *CombinedFieldsQuery) Query(data interface{}) *CombinedFieldsQuery {
q.params.Qry = data
return q
}
// Fields sets the fields used in the query
func (q *CombinedFieldsQuery) Fields(a ...string) *CombinedFieldsQuery {
q.params.Fields = append(q.params.Fields, a...)
return q
}
// AutoGenerateSynonymsPhraseQuery sets the "auto_generate_synonyms_phrase_query"
// boolean.
func (q *CombinedFieldsQuery) AutoGenerateSynonymsPhraseQuery(b bool) *CombinedFieldsQuery {
q.params.AutoGenerate = &b
return q
}
// Boost sets the query boost
func (q *CombinedFieldsQuery) Boost(l float32) *CombinedFieldsQuery {
q.params.Boost = l
return q
}
// Operator sets the boolean logic used to interpret text in the query value.
func (q *CombinedFieldsQuery) Operator(op MatchOperator) *CombinedFieldsQuery {
q.params.Op = op
return q
}
// MinimumShouldMatch sets the minimum number of clauses that must match for a
// document to be returned.
func (q *CombinedFieldsQuery) MinimumShouldMatch(s string) *CombinedFieldsQuery {
q.params.MinMatch = s
return q
}
// ZeroTermsQuery sets the "zero_terms_query" option to use. This indicates
// whether no documents are returned if the analyzer removes all tokens, such as
// when using a stop filter.
func (q *CombinedFieldsQuery) ZeroTermsQuery(s ZeroTerms) *CombinedFieldsQuery {
q.params.ZeroTerms = s
return q
}

View File

@ -0,0 +1,43 @@
package esquery
import (
"testing"
)
func TestCombinedFields(t *testing.T) {
runMapTests(t, []mapTest{
{
"simple combined_fields",
CombinedFields("value1").Fields("title"),
map[string]interface{}{
"combined_fields": map[string]interface{}{
"fields": []string{"title"},
"query": "value1",
},
},
},
{
"combined_fields all params",
CombinedFields("original").
Query("test").
Fields("title", "body").
AutoGenerateSynonymsPhraseQuery(true).
Boost(6.4).
Operator(OperatorAnd).
MinimumShouldMatch("3<90%").
ZeroTermsQuery(ZeroTermsAll),
map[string]interface{}{
"combined_fields": map[string]interface{}{
"auto_generate_synonyms_phrase_query": true,
"boost": 6.4,
"minimum_should_match": "3<90%",
"operator": "AND",
"zero_terms_query": "all",
"query": "test",
"fields": []string{"title", "body"},
},
},
},
})
}

40
query_joining.go Normal file
View File

@ -0,0 +1,40 @@
package esquery
// NestedQuery represents a query of type nested as described in:
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-nested-query.html
type NestedQuery struct {
path string
query Mappable
scoreMode string
ignoreUnmapped bool
}
func Nested(path string, query Mappable) *NestedQuery {
return &NestedQuery{
path: path,
query: query,
}
}
func (n *NestedQuery) ScoreMode(mode string) *NestedQuery {
n.scoreMode = mode
return n
}
func (n *NestedQuery) IgnoreUnmapped(val bool) *NestedQuery {
n.ignoreUnmapped = val
return n
}
// Map returns a map representation of the query, thus implementing the
// Mappable interface.
func (n *NestedQuery) Map() map[string]interface{} {
innerMap := map[string]interface{}{"path": n.path, "query": n.query.Map()}
if n.scoreMode != "" {
innerMap["score_mode"] = n.scoreMode
}
if n.ignoreUnmapped == true {
innerMap["ignore_unmapped"] = n.ignoreUnmapped
}
return map[string]interface{}{"nested": innerMap}
}

22
query_joining_test.go Normal file
View File

@ -0,0 +1,22 @@
package esquery
import (
"testing"
)
func TestNested(t *testing.T) {
runMapTests(t, []mapTest{
{
"Nested Query",
Nested("dns_values", Term("dns_values.type", "A")).ScoreMode("max").IgnoreUnmapped(true),
map[string]interface{}{
"nested": map[string]interface{}{
"path": "dns_values",
"query": Term("dns_values.type", "A").Map(),
"score_mode": "max",
"ignore_unmapped": true,
},
},
},
})
}

View File

@ -10,7 +10,7 @@ const (
// TypeMatch denotes a query of type "match"
TypeMatch matchType = iota
// TypeMatchBool denotes a query of type "match_bool_prefix"
// TypeMatchBoolPrefix denotes a query of type "match_bool_prefix"
TypeMatchBoolPrefix
// TypeMatchPhrase denotes a query of type "match_phrase"
@ -71,6 +71,7 @@ type matchParams struct {
MinMatch string `structs:"minimum_should_match,omitempty"`
ZeroTerms ZeroTerms `structs:"zero_terms_query,string,omitempty"`
Slp uint16 `structs:"slop,omitempty"` // only relevant for match_phrase query
Boost float32 `structs:"boost,omitempty"`
}
// Match creates a new query of type "match" with the provided field name.
@ -202,6 +203,12 @@ func (q *MatchQuery) ZeroTermsQuery(s ZeroTerms) *MatchQuery {
return q
}
// Boost sets the boost value of the query.
func (q *MatchQuery) Boost(b float32) *MatchQuery {
q.params.Boost = b
return q
}
// MatchOperator is an enumeration type representing supported values for a
// match query's "operator" parameter.
type MatchOperator uint8

View File

@ -17,6 +17,18 @@ func TestMatch(t *testing.T) {
},
},
},
{
"simple match",
Match("title", "sample text").Boost(50),
map[string]interface{}{
"match": map[string]interface{}{
"title": map[string]interface{}{
"query": "sample text",
"boost": 50,
},
},
},
},
{
"match with more params",
Match("issue_number").Query(16).Transpositions(false).MaxExpansions(32).Operator(OperatorAnd),

View File

@ -18,7 +18,7 @@ func (q *MultiMatchQuery) Map() map[string]interface{} {
type multiMatchParams struct {
Qry interface{} `structs:"query"`
Fields []string `structs:"fields"`
Fields []string `structs:"fields,omitempty"`
Type MultiMatchType `structs:"type,string,omitempty"`
TieBrk float32 `structs:"tie_breaker,omitempty"`
Boost float32 `structs:"boost,omitempty"`
@ -100,13 +100,13 @@ func (q *MultiMatchQuery) PrefixLength(l uint16) *MultiMatchQuery {
return q
}
// TieBreaker
// TieBreaker sets the query tiebreaker
func (q *MultiMatchQuery) TieBreaker(l float32) *MultiMatchQuery {
q.params.TieBrk = l
return q
}
// Boost
// Boost sets the query boost
func (q *MultiMatchQuery) Boost(l float32) *MultiMatchQuery {
q.params.Boost = l
return q
@ -164,27 +164,27 @@ func (q *MultiMatchQuery) ZeroTermsQuery(s ZeroTerms) *MultiMatchQuery {
return q
}
// MatchType is an enumeration type representing supported values for a
// MultiMatchType is an enumeration type representing supported values for a
// multi match query's "type" parameter.
type MultiMatchType uint8
const (
// TypeBestFields is the "best_fields" type
// MatchTypeBestFields is the "best_fields" type
MatchTypeBestFields MultiMatchType = iota
// TypeMostFields is the "most_fields" type
// MatchTypeMostFields is the "most_fields" type
MatchTypeMostFields
// TypeMostFields is the "cross_fields" type
// MatchTypeCrossFields is the "cross_fields" type
MatchTypeCrossFields
// TypeMostFields is the "phrase" type
// MatchTypePhrase is the "phrase" type
MatchTypePhrase
// TypeMostFields is the "phrase_prefix" type
// MatchTypePhrasePrefix is the "phrase_prefix" type
MatchTypePhrasePrefix
// TypeMostFields is the "bool_prefix" type
// MatchTypeBoolPrefix is the "bool_prefix" type
MatchTypeBoolPrefix
)

View File

@ -122,7 +122,7 @@ func (a *RangeQuery) Gt(val interface{}) *RangeQuery {
return a
}
// Gt sets that the value of field must be greater than or equal to the provided
// Gte sets that the value of field must be greater than or equal to the provided
// value
func (a *RangeQuery) Gte(val interface{}) *RangeQuery {
a.params.Gte = val
@ -454,7 +454,7 @@ func (q *TermsQuery) Boost(b float32) *TermsQuery {
// Map returns a map representation of the query, thus implementing the
// Mappable interface.
func (q TermsQuery) Map() map[string]interface{} {
func (q *TermsQuery) Map() map[string]interface{} {
innerMap := map[string]interface{}{q.field: q.values}
if q.boost > 0 {
innerMap["boost"] = q.boost
@ -511,10 +511,52 @@ func (q *TermsSetQuery) MinimumShouldMatchScript(script string) *TermsSetQuery {
// Map returns a map representation of the query, thus implementing the
// Mappable interface.
func (q TermsSetQuery) Map() map[string]interface{} {
func (q *TermsSetQuery) Map() map[string]interface{} {
return map[string]interface{}{
"terms_set": map[string]interface{}{
q.field: structs.Map(q.params),
},
}
}
// GeoFilter geoFilterParams represents a query of type "geo_distance", as described in:
// https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-geo-distance-query.html
type GeoFilter struct {
params geoFilterParams
filed string
}
func GeoFilterFunc(distance string, MiddleCentroid []float64, filed string) *GeoFilter {
return &GeoFilter{
params: geoFilterParams{
Distance: distance,
MiddleCentroid: MiddleCentroid,
},
filed: filed,
}
}
type geoFilterParams struct {
Distance string `structs:"distance,omitempty"`
MiddleCentroid []float64 `structs:"location,omitempty"`
}
func (g *GeoFilter) Distance(distance string) *GeoFilter {
g.params.Distance = distance
return g
}
func (g *GeoFilter) MiddleCentroid(middleCentroid []float64) *GeoFilter {
g.params.MiddleCentroid = middleCentroid
return g
}
func (g *GeoFilter) Map() map[string]interface{} {
m := structs.Map(g.params)
m[g.filed] = m["location"]
delete(m, "location")
response := map[string]interface{}{
"geo_distance": m}
return response
}

View File

@ -4,10 +4,10 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/elastic/go-elasticsearch/v8"
"time"
"github.com/elastic/go-elasticsearch/v7"
"github.com/elastic/go-elasticsearch/v7/esapi"
"github.com/elastic/go-elasticsearch/v8/esapi"
)
// SearchRequest represents a request to ElasticSearch's Search API, described
@ -15,16 +15,18 @@ import (
// Not all features of the search API are currently supported, but a request can
// currently include a query, aggregations, and more.
type SearchRequest struct {
aggs []Aggregation
explain *bool
from *uint64
highlight Mappable
postFilter Mappable
query Mappable
size *uint64
sort Sort
source Source
timeout *time.Duration
aggs []Aggregation
explain *bool
from *uint64
highlight Mappable
searchAfter []interface{}
postFilter Mappable
query Mappable
size *uint64
sort Sort
source Source
collapse map[string]interface{}
timeout *time.Duration
}
// Search creates a new SearchRequest object, to be filled via method chaining.
@ -38,6 +40,14 @@ func (req *SearchRequest) Query(q Mappable) *SearchRequest {
return req
}
// Collapse sets one field to collapse for the request.
func (req *SearchRequest) Collapse(field string) *SearchRequest {
req.collapse = map[string]interface{}{
"field": field,
}
return req
}
// Aggs sets one or more aggregations for the request.
func (req *SearchRequest) Aggs(aggs ...Aggregation) *SearchRequest {
req.aggs = append(req.aggs, aggs...)
@ -74,6 +84,12 @@ func (req *SearchRequest) Sort(name string, order Order) *SearchRequest {
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
// how each hit's score was calculated.
func (req *SearchRequest) Explain(b bool) *SearchRequest {
@ -105,7 +121,6 @@ func (req *SearchRequest) Highlight(highlight Mappable) *SearchRequest {
return req
}
// Map implements the Mappable interface. It converts the request to into a
// nested map[string]interface{}, as expected by the go-elasticsearch library.
func (req *SearchRequest) Map() map[string]interface{} {
@ -142,6 +157,13 @@ func (req *SearchRequest) Map() map[string]interface{} {
if req.highlight != nil {
m["highlight"] = req.highlight.Map()
}
if req.searchAfter != nil {
m["search_after"] = req.searchAfter
}
if req.collapse != nil {
m["collapse"] = req.collapse
}
source := req.source.Map()
if len(source) > 0 {

View File

@ -7,6 +7,13 @@ import (
func TestSearchMaps(t *testing.T) {
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",
Search().Query(MatchAll()).Size(20),