Skip to content

Commit a474194

Browse files
authored
Add script_score query support (#3919)
This commit adds script_score query support to the high level client Closes #3843
1 parent 8386d4c commit a474194

File tree

13 files changed

+381
-9
lines changed

13 files changed

+381
-9
lines changed

docs/query-dsl.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ Specialized types of queries that do not fit into other groups
275275

276276
* <<script-query-usage,Script Query Usage>>
277277

278+
* <<script-score-query-usage,Script Score Query Usage>>
279+
278280
See the Elasticsearch documentation on {ref_current}/specialized-queries.html[Specialized queries] for more details.
279281

280282
:includes-from-dirs: query-dsl/specialized
@@ -287,6 +289,8 @@ include::query-dsl/specialized/percolate/percolate-query-usage.asciidoc[]
287289

288290
include::query-dsl/specialized/script/script-query-usage.asciidoc[]
289291

292+
include::query-dsl/specialized/script-score/script-score-query-usage.asciidoc[]
293+
290294
[[span-queries]]
291295
== Span queries
292296

docs/query-dsl/compound/function-score/function-score-query-usage.asciidoc

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,36 @@ q
2929
.MaxBoost(20.0)
3030
.MinScore(1.0)
3131
.Functions(f => f
32-
.Exponential(b => b.Field(p => p.NumberOfCommits).Decay(0.5).Origin(1.0).Scale(0.1).Weight(2.1))
32+
.Exponential(b => b
33+
.Field(p => p.NumberOfCommits)
34+
.Decay(0.5)
35+
.Origin(1.0)
36+
.Scale(0.1)
37+
.Weight(2.1)
38+
.Filter(fi => fi
39+
.Range(r => r
40+
.Field(p => p.NumberOfContributors)
41+
.GreaterThan(10)
42+
)
43+
)
44+
)
3345
.GaussDate(b => b.Field(p => p.LastActivity).Origin(DateMath.Now).Decay(0.5).Scale("1d"))
34-
.LinearGeoLocation(b =>
35-
b.Field(p => p.LocationPoint).Origin(new GeoLocation(70, -70)).Scale(Distance.Miles(1)).MultiValueMode(MultiValueMode.Average))
46+
.LinearGeoLocation(b => b
47+
.Field(p => p.LocationPoint)
48+
.Origin(new GeoLocation(70, -70))
49+
.Scale(Distance.Miles(1))
50+
.MultiValueMode(MultiValueMode.Average)
51+
)
3652
.FieldValueFactor(b => b.Field(p => p.NumberOfContributors).Factor(1.1).Missing(0.1).Modifier(FieldValueFactorModifier.Square))
3753
.RandomScore(r => r.Seed(1337).Field("_seq_no"))
3854
.RandomScore(r => r.Seed("randomstring").Field("_seq_no"))
3955
.Weight(1.0)
40-
.ScriptScore(s => s.Script(ss => ss.Source("Math.log(2 + doc['numberOfCommits'].value)")))
56+
.ScriptScore(s => s
57+
.Script(ss => ss
58+
.Source("Math.log(2 + doc['numberOfCommits'].value)")
59+
)
60+
.Weight(2)
61+
)
4162
)
4263
)
4364
----
@@ -57,7 +78,19 @@ new FunctionScoreQuery()
5778
MinScore = 1.0,
5879
Functions = new List<IScoreFunction>
5980
{
60-
new ExponentialDecayFunction { Origin = 1.0, Decay = 0.5, Field = Field<Project>(p => p.NumberOfCommits), Scale = 0.1, Weight = 2.1 },
81+
new ExponentialDecayFunction
82+
{
83+
Origin = 1.0,
84+
Decay = 0.5,
85+
Field = Field<Project>(p => p.NumberOfCommits),
86+
Scale = 0.1,
87+
Weight = 2.1,
88+
Filter = new NumericRangeQuery
89+
{
90+
Field = Field<Project>(f => f.NumberOfContributors),
91+
GreaterThan = 10
92+
}
93+
},
6194
new GaussDateDecayFunction
6295
{ Origin = DateMath.Now, Field = Field<Project>(p => p.LastActivity), Decay = 0.5, Scale = TimeSpan.FromDays(1) },
6396
new LinearGeoDecayFunction
@@ -72,7 +105,7 @@ new FunctionScoreQuery()
72105
new RandomScoreFunction { Seed = 1337, Field = "_seq_no" },
73106
new RandomScoreFunction { Seed = "randomstring", Field = "_seq_no" },
74107
new WeightFunction { Weight = 1.0 },
75-
new ScriptScoreFunction { Script = new InlineScript("Math.log(2 + doc['numberOfCommits'].value)") }
108+
new ScriptScoreFunction { Script = new InlineScript("Math.log(2 + doc['numberOfCommits'].value)"), Weight = 2.0 }
76109
}
77110
}
78111
----
@@ -94,7 +127,14 @@ new FunctionScoreQuery()
94127
"decay": 0.5
95128
}
96129
},
97-
"weight": 2.1
130+
"weight": 2.1,
131+
"filter": {
132+
"range": {
133+
"numberOfContributors": {
134+
"gt": 10.0
135+
}
136+
}
137+
}
98138
},
99139
{
100140
"gauss": {
@@ -145,7 +185,8 @@ new FunctionScoreQuery()
145185
"script": {
146186
"source": "Math.log(2 + doc['numberOfCommits'].value)"
147187
}
148-
}
188+
},
189+
"weight": 2.0
149190
}
150191
],
151192
"max_boost": 20.0,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
:ref_current: https://www.elastic.co/guide/en/elasticsearch/reference/7.0
2+
3+
:github: https://github.com/elastic/elasticsearch-net
4+
5+
:nuget: https://www.nuget.org/packages
6+
7+
////
8+
IMPORTANT NOTE
9+
==============
10+
This file has been generated from https://github.com/elastic/elasticsearch-net/tree/master/src/Tests/Tests/QueryDsl/Specialized/ScriptScore/ScriptScoreQueryUsageTests.cs.
11+
If you wish to submit a PR for any spelling mistakes, typos or grammatical errors for this file,
12+
please modify the original csharp file found at the link and submit the PR with that change. Thanks!
13+
////
14+
15+
[[script-score-query-usage]]
16+
=== Script Score Query Usage
17+
18+
A query allowing you to modify the score of documents that are retrieved by a query.
19+
This can be useful if, for example, a score function is computationally expensive and
20+
it is sufficient to compute the score on a filtered set of documents.
21+
22+
See the Elasticsearch documentation on {ref_current}/query-dsl-script-score-query.html[script_score query] for more details.
23+
24+
==== Fluent DSL example
25+
26+
[source,csharp]
27+
----
28+
q
29+
.ScriptScore(sn => sn
30+
.Name("named_query")
31+
.Boost(1.1)
32+
.Query(qq => qq
33+
.Range(r => r
34+
.Field(f => f.NumberOfCommits)
35+
.GreaterThan(50)
36+
)
37+
)
38+
.Script(s => s
39+
.Source(_scriptScoreSource)
40+
.Params(p => p
41+
.Add("origin", 100)
42+
.Add("scale", 10)
43+
.Add("decay", 0.5)
44+
.Add("offset", 0)
45+
)
46+
)
47+
)
48+
----
49+
50+
==== Object Initializer syntax example
51+
52+
[source,csharp]
53+
----
54+
new ScriptScoreQuery
55+
{
56+
Name = "named_query",
57+
Boost = 1.1,
58+
Query = new NumericRangeQuery
59+
{
60+
Field = Infer.Field<Project>(f => f.NumberOfCommits),
61+
GreaterThan = 50
62+
},
63+
Script = new InlineScript(_scriptScoreSource)
64+
{
65+
Params = new Dictionary<string, object>
66+
{
67+
{ "origin", 100 },
68+
{ "scale", 10 },
69+
{ "decay", 0.5 },
70+
{ "offset", 0 }
71+
}
72+
},
73+
}
74+
----
75+
76+
[source,javascript]
77+
.Example json output
78+
----
79+
{
80+
"script_score": {
81+
"_name": "named_query",
82+
"boost": 1.1,
83+
"query": {
84+
"range": {
85+
"numberOfCommits": {
86+
"gt": 50.0
87+
}
88+
}
89+
},
90+
"script": {
91+
"source": "decayNumericLinear(params.origin, params.scale, params.offset, params.decay, doc['numberOfCommits'].value)",
92+
"params": {
93+
"origin": 100,
94+
"scale": 10,
95+
"decay": 0.5,
96+
"offset": 0
97+
}
98+
}
99+
}
100+
}
101+
----
102+

src/CodeGeneration/DocGenerator/StringExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ public static class StringExtensions
114114
new []{ new [] {8.2, 18.2}, new [] {8.2, -18.8}, new [] {-8.8, -10.8}, new [] {8.8, 18.2}}
115115
}"
116116
},
117-
{ "ProjectFilterExpectedJson", "new {term = new {type = new {value = \"project\"}}}" }
117+
{ "ProjectFilterExpectedJson", "new {term = new {type = new {value = \"project\"}}}" },
118+
{ "_scriptScoreSource", "\"decayNumericLinear(params.origin, params.scale, params.offset, params.decay, doc['numberOfCommits'].value)\""}
118119
};
119120

120121
private static readonly Regex LeadingSpacesAndAsterisk = new Regex(@"^(?<value>[ \t]*\*\s?).*", RegexOptions.Compiled);

src/Nest/QueryDsl/Abstractions/Container/IQueryContainer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ public interface IQueryContainer
115115
[DataMember(Name ="script")]
116116
IScriptQuery Script { get; set; }
117117

118+
/// <inheritdoc cref="IScriptScoreQuery"/>
119+
[DataMember(Name ="script_score")]
120+
IScriptScoreQuery ScriptScore { get; set; }
121+
118122
[DataMember(Name ="simple_query_string")]
119123
ISimpleQueryStringQuery SimpleQueryString { get; set; }
120124

src/Nest/QueryDsl/Abstractions/Container/QueryContainer-Assignments.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public partial class QueryContainer : IQueryContainer, IDescriptor
3838
private IRawQuery _raw;
3939
private IRegexpQuery _regexp;
4040
private IScriptQuery _script;
41+
private IScriptScoreQuery _scriptScore;
4142
private ISimpleQueryStringQuery _simpleQueryString;
4243
private ISpanContainingQuery _spanContaining;
4344
private ISpanFieldMaskingQuery _spanFieldMasking;
@@ -251,6 +252,12 @@ IScriptQuery IQueryContainer.Script
251252
set => _script = Set(value);
252253
}
253254

255+
IScriptScoreQuery IQueryContainer.ScriptScore
256+
{
257+
get => _scriptScore;
258+
set => _scriptScore = Set(value);
259+
}
260+
254261
ISimpleQueryStringQuery IQueryContainer.SimpleQueryString
255262
{
256263
get => _simpleQueryString;

src/Nest/QueryDsl/Abstractions/Container/QueryContainerDescriptor.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,9 @@ public QueryContainer FunctionScore(Func<FunctionScoreQueryDescriptor<T>, IFunct
437437
public QueryContainer Script(Func<ScriptQueryDescriptor<T>, IScriptQuery> selector) =>
438438
WrapInContainer(selector, (query, container) => container.Script = query);
439439

440+
public QueryContainer ScriptScore(Func<ScriptScoreQueryDescriptor<T>, IScriptScoreQuery> selector) =>
441+
WrapInContainer(selector, (query, container) => container.ScriptScore = query);
442+
440443
public QueryContainer Exists(Func<ExistsQueryDescriptor<T>, IExistsQuery> selector) =>
441444
WrapInContainer(selector, (query, container) => container.Exists = query);
442445

src/Nest/QueryDsl/Query.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ public static QueryContainer Regexp(Func<RegexpQueryDescriptor<T>, IRegexpQuery>
117117
public static QueryContainer Script(Func<ScriptQueryDescriptor<T>, IScriptQuery> selector) =>
118118
new QueryContainerDescriptor<T>().Script(selector);
119119

120+
/// <inheritdoc cref="IScriptScoreQuery"/>
121+
public static QueryContainer ScriptScore(Func<ScriptScoreQueryDescriptor<T>, IScriptScoreQuery> selector) =>
122+
new QueryContainerDescriptor<T>().ScriptScore(selector);
123+
120124
public static QueryContainer SimpleQueryString(Func<SimpleQueryStringQueryDescriptor<T>, ISimpleQueryStringQuery> selector) =>
121125
new QueryContainerDescriptor<T>().SimpleQueryString(selector);
122126

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
using Elasticsearch.Net.Utf8Json;
4+
5+
namespace Nest
6+
{
7+
/// <summary>
8+
/// A query allowing you to modify the score of documents that are retrieved by a query.
9+
/// This can be useful if, for example, a score function is computationally expensive and it is sufficient to
10+
/// compute the score on a filtered set of documents.
11+
/// </summary>
12+
[ReadAs(typeof(ScriptScoreQuery))]
13+
[InterfaceDataContract]
14+
public interface IScriptScoreQuery : IQuery
15+
{
16+
/// <summary>
17+
/// The query to execute
18+
/// </summary>
19+
[DataMember(Name = "query")]
20+
QueryContainer Query { get; set; }
21+
22+
/// <summary>
23+
/// The script to execute
24+
/// </summary>
25+
[DataMember(Name = "script")]
26+
IScript Script { get; set; }
27+
}
28+
29+
/// <inheritdoc cref="IScriptScoreQuery" />
30+
public class ScriptScoreQuery : QueryBase, IScriptScoreQuery
31+
{
32+
/// <inheritdoc />
33+
public QueryContainer Query { get; set; }
34+
35+
/// <inheritdoc />
36+
public IScript Script { get; set; }
37+
38+
protected override bool Conditionless => IsConditionless(this);
39+
40+
internal override void InternalWrapInContainer(IQueryContainer c) => c.ScriptScore = this;
41+
42+
internal static bool IsConditionless(IScriptScoreQuery q)
43+
{
44+
if (q.Script == null || q.Query.IsConditionless())
45+
return true;
46+
47+
switch (q.Script)
48+
{
49+
case IInlineScript inlineScript:
50+
return inlineScript.Source.IsNullOrEmpty();
51+
case IIndexedScript indexedScript:
52+
return indexedScript.Id.IsNullOrEmpty();
53+
}
54+
55+
return false;
56+
}
57+
}
58+
59+
/// <inheritdoc cref="IScriptScoreQuery" />
60+
public class ScriptScoreQueryDescriptor<T>
61+
: QueryDescriptorBase<ScriptScoreQueryDescriptor<T>, IScriptScoreQuery>
62+
, IScriptScoreQuery where T : class
63+
{
64+
protected override bool Conditionless => ScriptScoreQuery.IsConditionless(this);
65+
QueryContainer IScriptScoreQuery.Query { get; set; }
66+
67+
IScript IScriptScoreQuery.Script { get; set; }
68+
69+
/// <inheritdoc cref="IScriptScoreQuery.Query" />
70+
public ScriptScoreQueryDescriptor<T> Query(Func<QueryContainerDescriptor<T>, QueryContainer> selector) =>
71+
Assign(selector, (a, v) => a.Query = v?.Invoke(new QueryContainerDescriptor<T>()));
72+
73+
/// <inheritdoc cref="IScriptScoreQuery.Script" />
74+
public ScriptScoreQueryDescriptor<T> Script(Func<ScriptDescriptor, IScript> selector) =>
75+
Assign(selector, (a, v) => a.Script = v?.Invoke(new ScriptDescriptor()));
76+
}
77+
}

src/Nest/QueryDsl/Visitor/DslPrettyPrintVisitor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ public virtual void Visit(IGeoShapeQuery query)
187187

188188
public virtual void Visit(IScriptQuery query) => Write("script");
189189

190+
public virtual void Visit(IScriptScoreQuery query) => Write("script_score");
191+
190192
public virtual void Visit(IRawQuery query) => Write("raw");
191193

192194
public virtual void Visit(IPercolateQuery query) => Write("percolate");

src/Nest/QueryDsl/Visitor/QueryVisitor.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ public interface IQueryVisitor
8686

8787
void Visit(IScriptQuery query);
8888

89+
void Visit(IScriptScoreQuery query);
90+
8991
void Visit(IGeoPolygonQuery query);
9092

9193
void Visit(IGeoDistanceQuery query);
@@ -237,6 +239,8 @@ public virtual void Visit(ITermsQuery query) { }
237239

238240
public virtual void Visit(IScriptQuery query) { }
239241

242+
public virtual void Visit(IScriptScoreQuery query) { }
243+
240244
public virtual void Visit(IGeoPolygonQuery query) { }
241245

242246
public virtual void Visit(IGeoDistanceQuery query) { }

src/Nest/QueryDsl/Visitor/QueryWalker.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public void Walk(IQueryContainer qd, IQueryVisitor visitor)
4444
VisitQuery(qd.MatchPhrase, visitor, (v, d) => v.Visit(d));
4545
VisitQuery(qd.MatchPhrasePrefix, visitor, (v, d) => v.Visit(d));
4646
VisitQuery(qd.Script, visitor, (v, d) => v.Visit(d));
47+
VisitQuery(qd.ScriptScore, visitor, (v, d) => v.Visit(d));
4748
VisitQuery(qd.Exists, visitor, (v, d) => v.Visit(d));
4849
VisitQuery(qd.GeoPolygon, visitor, (v, d) => v.Visit(d));
4950
VisitQuery(qd.GeoDistance, visitor, (v, d) => v.Visit(d));

0 commit comments

Comments
 (0)