Skip to content

Commit 987f2d7

Browse files
committed
Add RankFeatureQuery
The `rank_feature` query boosts the relevance score of documents based on the numeric value of a `rank_feature` or `rank_features` field. See here for details: https://www.elastic.co/guide/en/elasticsearch/reference/7.14/query-dsl-rank-feature-query.html Close olivere#1428
1 parent 593cd32 commit 987f2d7

File tree

3 files changed

+284
-0
lines changed

3 files changed

+284
-0
lines changed

search_queries_pinned_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Copyright 2012-present Oliver Eilhard. All rights reserved.
2+
// Use of this source code is governed by a MIT-license.
3+
// See http://olivere.mit-license.org/license.txt for details.
4+
15
package elastic
26

37
import (

search_queries_rank_feature.go

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// Copyright 2012-present Oliver Eilhard. All rights reserved.
2+
// Use of this source code is governed by a MIT-license.
3+
// See http://olivere.mit-license.org/license.txt for details.
4+
5+
package elastic
6+
7+
// RankFeatureQuery boosts the relevance score of documents based on the
8+
// numeric value of a rank_feature or rank_features field.
9+
//
10+
// The RankFeatureQuery is typically used in the should clause of a BoolQuery
11+
// so its relevance scores are added to other scores from the BoolQuery.
12+
//
13+
// For more details, see:
14+
// https://www.elastic.co/guide/en/elasticsearch/reference/7.14/query-dsl-rank-feature-query.html
15+
type RankFeatureQuery struct {
16+
field string
17+
scoreFunc RankFeatureScoreFunction
18+
boost *float64
19+
queryName string
20+
}
21+
22+
// NewRankFeatureQuery creates and initializes a new RankFeatureQuery.
23+
func NewRankFeatureQuery(field string) *RankFeatureQuery {
24+
return &RankFeatureQuery{
25+
field: field,
26+
}
27+
}
28+
29+
// Field name.
30+
func (q *RankFeatureQuery) Field(field string) *RankFeatureQuery {
31+
q.field = field
32+
return q
33+
}
34+
35+
// ScoreFunction specifies the score function for the RankFeatureQuery.
36+
func (q *RankFeatureQuery) ScoreFunction(f RankFeatureScoreFunction) *RankFeatureQuery {
37+
q.scoreFunc = f
38+
return q
39+
}
40+
41+
// Boost sets the boost for this query.
42+
func (q *RankFeatureQuery) Boost(boost float64) *RankFeatureQuery {
43+
q.boost = &boost
44+
return q
45+
}
46+
47+
// QueryName sets the query name for the filter that can be used when
48+
// searching for matched_filters per hit.
49+
func (q *RankFeatureQuery) QueryName(queryName string) *RankFeatureQuery {
50+
q.queryName = queryName
51+
return q
52+
}
53+
54+
// Source returns the JSON serializable content for this query.
55+
func (q *RankFeatureQuery) Source() (interface{}, error) {
56+
// {
57+
// "rank_feature": {
58+
// "field": "pagerank",
59+
// "saturation": {
60+
// "pivot": 8
61+
// }
62+
// }
63+
// }
64+
65+
query := make(map[string]interface{})
66+
params := make(map[string]interface{})
67+
query["rank_feature"] = params
68+
params["field"] = q.field
69+
if q.scoreFunc != nil {
70+
src, err := q.scoreFunc.Source()
71+
if err != nil {
72+
return nil, err
73+
}
74+
params[q.scoreFunc.Name()] = src
75+
}
76+
if q.boost != nil {
77+
params["boost"] = *q.boost
78+
}
79+
if q.queryName != "" {
80+
params["_name"] = q.queryName
81+
}
82+
83+
return query, nil
84+
}
85+
86+
// -- Score functions --
87+
88+
// RankFeatureScoreFunction specifies the interface for score functions
89+
// in the context of a RankFeatureQuery.
90+
type RankFeatureScoreFunction interface {
91+
Name() string
92+
Source() (interface{}, error)
93+
}
94+
95+
// -- Log score function --
96+
97+
// RankFeatureLogScoreFunction represents a Logarithmic score function for a
98+
// RankFeatureQuery.
99+
//
100+
// See here for details:
101+
// https://www.elastic.co/guide/en/elasticsearch/reference/7.14/query-dsl-rank-feature-query.html#rank-feature-query-logarithm
102+
type RankFeatureLogScoreFunction struct {
103+
scalingFactor float64
104+
}
105+
106+
// NewRankFeatureLogScoreFunction returns a new RankFeatureLogScoreFunction
107+
// with the given scaling factor.
108+
func NewRankFeatureLogScoreFunction(scalingFactor float64) *RankFeatureLogScoreFunction {
109+
return &RankFeatureLogScoreFunction{
110+
scalingFactor: scalingFactor,
111+
}
112+
}
113+
114+
// Name of the score function.
115+
func (f *RankFeatureLogScoreFunction) Name() string { return "log" }
116+
117+
// Source returns a serializable JSON object for building the query.
118+
func (f *RankFeatureLogScoreFunction) Source() (interface{}, error) {
119+
return map[string]interface{}{
120+
"scaling_factor": f.scalingFactor,
121+
}, nil
122+
}
123+
124+
// -- Saturation score function --
125+
126+
// RankFeatureSaturationScoreFunction represents a Log score function for a
127+
// RankFeatureQuery.
128+
//
129+
// See here for details:
130+
// https://www.elastic.co/guide/en/elasticsearch/reference/7.14/query-dsl-rank-feature-query.html#rank-feature-query-saturation
131+
type RankFeatureSaturationScoreFunction struct {
132+
pivot *float64
133+
}
134+
135+
// NewRankFeatureSaturationScoreFunction initializes a new
136+
// RankFeatureSaturationScoreFunction.
137+
func NewRankFeatureSaturationScoreFunction() *RankFeatureSaturationScoreFunction {
138+
return &RankFeatureSaturationScoreFunction{}
139+
}
140+
141+
// Pivot specifies the pivot to use.
142+
func (f *RankFeatureSaturationScoreFunction) Pivot(pivot float64) *RankFeatureSaturationScoreFunction {
143+
f.pivot = &pivot
144+
return f
145+
}
146+
147+
// Name of the score function.
148+
func (f *RankFeatureSaturationScoreFunction) Name() string { return "saturation" }
149+
150+
// Source returns a serializable JSON object for building the query.
151+
func (f *RankFeatureSaturationScoreFunction) Source() (interface{}, error) {
152+
m := make(map[string]interface{})
153+
if f.pivot != nil {
154+
m["pivot"] = *f.pivot
155+
}
156+
return m, nil
157+
}
158+
159+
// -- Sigmoid score function --
160+
161+
// RankFeatureSigmoidScoreFunction represents a Sigmoid score function for a
162+
// RankFeatureQuery.
163+
//
164+
// See here for details:
165+
// https://www.elastic.co/guide/en/elasticsearch/reference/7.14/query-dsl-rank-feature-query.html#rank-feature-query-sigmoid
166+
type RankFeatureSigmoidScoreFunction struct {
167+
pivot float64
168+
exponent float64
169+
}
170+
171+
// NewRankFeatureSigmoidScoreFunction returns a new RankFeatureSigmoidScoreFunction
172+
// with the given scaling factor.
173+
func NewRankFeatureSigmoidScoreFunction(pivot, exponent float64) *RankFeatureSigmoidScoreFunction {
174+
return &RankFeatureSigmoidScoreFunction{
175+
pivot: pivot,
176+
exponent: exponent,
177+
}
178+
}
179+
180+
// Name of the score function.
181+
func (f *RankFeatureSigmoidScoreFunction) Name() string { return "sigmoid" }
182+
183+
// Source returns a serializable JSON object for building the query.
184+
func (f *RankFeatureSigmoidScoreFunction) Source() (interface{}, error) {
185+
return map[string]interface{}{
186+
"pivot": f.pivot,
187+
"exponent": f.exponent,
188+
}, nil
189+
}
190+
191+
// -- Linear score function --
192+
193+
// RankFeatureLinearScoreFunction represents a Linear score function for a
194+
// RankFeatureQuery.
195+
//
196+
// See here for details:
197+
// https://www.elastic.co/guide/en/elasticsearch/reference/7.14/query-dsl-rank-feature-query.html#rank-feature-query-linear
198+
type RankFeatureLinearScoreFunction struct {
199+
}
200+
201+
// NewRankFeatureLinearScoreFunction initializes a new
202+
// RankFeatureLinearScoreFunction.
203+
func NewRankFeatureLinearScoreFunction() *RankFeatureLinearScoreFunction {
204+
return &RankFeatureLinearScoreFunction{}
205+
}
206+
207+
// Name of the score function.
208+
func (f *RankFeatureLinearScoreFunction) Name() string { return "linear" }
209+
210+
// Source returns a serializable JSON object for building the query.
211+
func (f *RankFeatureLinearScoreFunction) Source() (interface{}, error) {
212+
return map[string]interface{}{}, nil
213+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2012-present Oliver Eilhard. All rights reserved.
2+
// Use of this source code is governed by a MIT-license.
3+
// See http://olivere.mit-license.org/license.txt for details.
4+
5+
package elastic
6+
7+
import (
8+
"encoding/json"
9+
"testing"
10+
)
11+
12+
func TestRankFeatureQueryTest(t *testing.T) {
13+
tests := []struct {
14+
Query Query
15+
Expected string
16+
}{
17+
// #0
18+
{
19+
Query: NewRankFeatureQuery("pagerank"),
20+
Expected: `{"rank_feature":{"field":"pagerank"}}`,
21+
},
22+
// #1
23+
{
24+
Query: NewRankFeatureQuery("url_length").Boost(0.1),
25+
Expected: `{"rank_feature":{"boost":0.1,"field":"url_length"}}`,
26+
},
27+
// #2
28+
{
29+
Query: NewRankFeatureQuery("pagerank").ScoreFunction(NewRankFeatureSaturationScoreFunction().Pivot(8)),
30+
Expected: `{"rank_feature":{"field":"pagerank","saturation":{"pivot":8}}}`,
31+
},
32+
// #3
33+
{
34+
Query: NewRankFeatureQuery("pagerank").ScoreFunction(NewRankFeatureSaturationScoreFunction()),
35+
Expected: `{"rank_feature":{"field":"pagerank","saturation":{}}}`,
36+
},
37+
// #4
38+
{
39+
Query: NewRankFeatureQuery("pagerank").ScoreFunction(NewRankFeatureLogScoreFunction(4)),
40+
Expected: `{"rank_feature":{"field":"pagerank","log":{"scaling_factor":4}}}`,
41+
},
42+
// #5
43+
{
44+
Query: NewRankFeatureQuery("pagerank").ScoreFunction(NewRankFeatureSigmoidScoreFunction(7, 0.6)),
45+
Expected: `{"rank_feature":{"field":"pagerank","sigmoid":{"exponent":0.6,"pivot":7}}}`,
46+
},
47+
// #6
48+
{
49+
Query: NewRankFeatureQuery("pagerank").ScoreFunction(NewRankFeatureLinearScoreFunction()),
50+
Expected: `{"rank_feature":{"field":"pagerank","linear":{}}}`,
51+
},
52+
}
53+
54+
for i, tt := range tests {
55+
src, err := tt.Query.Source()
56+
if err != nil {
57+
t.Fatalf("#%d: encoding Source failed: %v", i, err)
58+
}
59+
data, err := json.Marshal(src)
60+
if err != nil {
61+
t.Fatalf("#%d: marshaling to JSON failed: %v", i, err)
62+
}
63+
if want, got := tt.Expected, string(data); want != got {
64+
t.Fatalf("#%d: expected\n%s\ngot:\n%s", i, want, got)
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)