Add support for compound queries

This commit adds support for the compound queries "bool", "boosting",
"constant_score" and "dis_max". The "function_score" query is not
supported yet.

Compound queries are simple. They act just like simple queries, except
that they are recursive, wrapping other simple/compound queries.

For example:

    esquery.Bool().
        Must(Term("user", "kimchy"), Term("author", "kimchy")).
        Filter(Term("tag", "tech"))
This commit is contained in:
Ido Perlmuter 2020-02-18 17:40:57 +02:00
parent 9ef149ec94
commit 6c8e71c188
8 changed files with 248 additions and 0 deletions

61
boolean.go Normal file
View File

@ -0,0 +1,61 @@
package esquery
import "encoding/json"
/*******************************************************************************
* Boolean Queries
* https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html
******************************************************************************/
type BoolQuery struct {
params boolQueryParams
}
type boolQueryParams struct {
Must []json.Marshaler `json:"must,omitempty"`
Filter []json.Marshaler `json:"filter,omitempty"`
MustNot []json.Marshaler `json:"must_not,omitempty"`
Should []json.Marshaler `json:"should,omitempty"`
MinimumShouldMatch int16 `json:"minimum_should_match,omitempty"`
Boost float32 `json:"boost,omitempty"`
}
func Bool() *BoolQuery {
return &BoolQuery{}
}
func (q *BoolQuery) Must(must ...json.Marshaler) *BoolQuery {
q.params.Must = append(q.params.Must, must...)
return q
}
func (q *BoolQuery) Filter(filter ...json.Marshaler) *BoolQuery {
q.params.Filter = append(q.params.Filter, filter...)
return q
}
func (q *BoolQuery) MustNot(mustnot ...json.Marshaler) *BoolQuery {
q.params.MustNot = append(q.params.MustNot, mustnot...)
return q
}
func (q *BoolQuery) Should(should ...json.Marshaler) *BoolQuery {
q.params.Should = append(q.params.Should, should...)
return q
}
func (q *BoolQuery) MinimumShouldMatch(val int16) *BoolQuery {
q.params.MinimumShouldMatch = val
return q
}
func (q *BoolQuery) Boost(val float32) *BoolQuery {
q.params.Boost = val
return q
}
func (q BoolQuery) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]boolQueryParams{
"bool": q.params,
})
}

31
boolean_test.go Normal file
View File

@ -0,0 +1,31 @@
package esquery
import (
"testing"
)
func TestBool(t *testing.T) {
runTests(t, []queryTest{
{
"bool with only a simple must",
Bool().Must(Term("tag", "tech")),
"{\"bool\":{\"must\":[{\"term\":{\"tag\":{\"value\":\"tech\"}}}]}}\n",
},
{
"bool which must match_all and filter",
Bool().Must(MatchAll()).Filter(Term("status", "active")),
"{\"bool\":{\"must\":[{\"match_all\":{}}],\"filter\":[{\"term\":{\"status\":{\"value\":\"active\"}}}]}}\n",
},
{
"bool with a lot of stuff",
Bool().
Must(Term("user", "kimchy")).
Filter(Term("tag", "tech")).
MustNot(Range("age").Gte(10).Lte(20)).
Should(Term("tag", "wow"), Term("tag", "elasticsearch")).
MinimumShouldMatch(1).
Boost(1.1),
"{\"bool\":{\"must\":[{\"term\":{\"user\":{\"value\":\"kimchy\"}}}],\"filter\":[{\"term\":{\"tag\":{\"value\":\"tech\"}}}],\"must_not\":[{\"range\":{\"age\":{\"gte\":10,\"lte\":20}}}],\"should\":[{\"term\":{\"tag\":{\"value\":\"wow\"}}},{\"term\":{\"tag\":{\"value\":\"elasticsearch\"}}}],\"minimum_should_match\":1,\"boost\":1.1}}\n",
},
})
}

43
boosting.go Normal file
View File

@ -0,0 +1,43 @@
package esquery
import "encoding/json"
/*******************************************************************************
* Boosting Queries
* https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-boosting-query.html
******************************************************************************/
type BoostingQuery struct {
params boostingQueryParams
}
type boostingQueryParams struct {
Positive json.Marshaler `json:"positive"`
Negative json.Marshaler `json:"negative"`
NegativeBoost float32 `json:"negative_boost"`
}
func Boosting() *BoostingQuery {
return &BoostingQuery{}
}
func (q *BoostingQuery) Positive(p json.Marshaler) *BoostingQuery {
q.params.Positive = p
return q
}
func (q *BoostingQuery) Negative(p json.Marshaler) *BoostingQuery {
q.params.Negative = p
return q
}
func (q *BoostingQuery) NegativeBoost(b float32) *BoostingQuery {
q.params.NegativeBoost = b
return q
}
func (q *BoostingQuery) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]boostingQueryParams{
"boosting": q.params,
})
}

18
boosting_test.go Normal file
View File

@ -0,0 +1,18 @@
package esquery
import (
"testing"
)
func TestBoost(t *testing.T) {
runTests(t, []queryTest{
{
"boosting query",
Boosting().
Positive(Term("text", "apple")).
Negative(Term("text", "pie tart")).
NegativeBoost(0.5),
"{\"boosting\":{\"positive\":{\"term\":{\"text\":{\"value\":\"apple\"}}},\"negative\":{\"term\":{\"text\":{\"value\":\"pie tart\"}}},\"negative_boost\":0.5}}\n",
},
})
}

29
constant_score.go Normal file
View File

@ -0,0 +1,29 @@
package esquery
import "encoding/json"
type ConstantScoreQuery struct {
params constantScoreParams
}
type constantScoreParams struct {
Filter json.Marshaler `json:"filter"`
Boost float32 `json:"boost,omitempty"`
}
func ConstantScore(filter json.Marshaler) *ConstantScoreQuery {
return &ConstantScoreQuery{
params: constantScoreParams{Filter: filter},
}
}
func (q *ConstantScoreQuery) Boost(b float32) *ConstantScoreQuery {
q.params.Boost = b
return q
}
func (q ConstantScoreQuery) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]constantScoreParams{
"constant_score": q.params,
})
}

20
constant_score_test.go Normal file
View File

@ -0,0 +1,20 @@
package esquery
import (
"testing"
)
func TestConstantScore(t *testing.T) {
runTests(t, []queryTest{
{
"constant_score query without boost",
ConstantScore(Term("user", "kimchy")),
"{\"constant_score\":{\"filter\":{\"term\":{\"user\":{\"value\":\"kimchy\"}}}}}\n",
},
{
"constant_score query with boost",
ConstantScore(Term("user", "kimchy")).Boost(2.2),
"{\"constant_score\":{\"filter\":{\"term\":{\"user\":{\"value\":\"kimchy\"}}},\"boost\":2.2}}\n",
},
})
}

31
dis_max.go Normal file
View File

@ -0,0 +1,31 @@
package esquery
import "encoding/json"
type DisMaxQuery struct {
params disMaxParams
}
type disMaxParams struct {
Queries []json.Marshaler `json:"queries"`
TieBreaker float32 `json:"tie_breaker,omitempty"`
}
func DisMax(queries ...json.Marshaler) *DisMaxQuery {
return &DisMaxQuery{
params: disMaxParams{
Queries: queries,
},
}
}
func (q *DisMaxQuery) TieBreaker(b float32) *DisMaxQuery {
q.params.TieBreaker = b
return q
}
func (q DisMaxQuery) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]disMaxParams{
"dis_max": q.params,
})
}

15
dis_max_test.go Normal file
View File

@ -0,0 +1,15 @@
package esquery
import (
"testing"
)
func TestDisMax(t *testing.T) {
runTests(t, []queryTest{
{
"dis_max",
DisMax(Term("title", "Quick pets"), Term("body", "Quick pets")).TieBreaker(0.7),
"{\"dis_max\":{\"queries\":[{\"term\":{\"title\":{\"value\":\"Quick pets\"}}},{\"term\":{\"body\":{\"value\":\"Quick pets\"}}}],\"tie_breaker\":0.7}}\n",
},
})
}