diff --git a/go.mod b/go.mod index d17acbf..7f6ca00 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/aquasecurity/esquery +module github.com/okdanta/esquery go 1.13 diff --git a/query_joining.go b/query_joining.go new file mode 100644 index 0000000..6655de9 --- /dev/null +++ b/query_joining.go @@ -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} +} diff --git a/query_joining_test.go b/query_joining_test.go new file mode 100644 index 0000000..7b4636b --- /dev/null +++ b/query_joining_test.go @@ -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, + }, + }, + }, + }) +} diff --git a/search.go b/search.go index 5c661a2..cb406da 100644 --- a/search.go +++ b/search.go @@ -25,8 +25,8 @@ type SearchRequest struct { 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. @@ -40,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...) @@ -113,8 +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{} { @@ -155,6 +161,9 @@ func (req *SearchRequest) Map() map[string]interface{} { m["search_after"] = req.searchAfter } + if req.collapse != nil { + m["collapse"] = req.collapse + } source := req.source.Map() if len(source) > 0 {