Skip to content

Commit f5f347f

Browse files
authored
Add explanation to runtime field query (backport of #63429) (#63503)
This adds a `detail` to the output of `explain` for runtime fields queries that makes it clear that their score is entirely based on their boost. We don't have any tf/idf/norms/whatever to do any scoring - we just score `boost`.
1 parent c99cf21 commit f5f347f

File tree

8 files changed

+240
-1
lines changed

8 files changed

+240
-1
lines changed

x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/AbstractScriptFieldQuery.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.apache.lucene.search.ConstantScoreScorer;
1111
import org.apache.lucene.search.ConstantScoreWeight;
1212
import org.apache.lucene.search.DocIdSetIterator;
13+
import org.apache.lucene.search.Explanation;
1314
import org.apache.lucene.search.IndexSearcher;
1415
import org.apache.lucene.search.Query;
1516
import org.apache.lucene.search.ScoreMode;
@@ -80,6 +81,15 @@ public float matchCost() {
8081
};
8182
return new ConstantScoreScorer(this, score(), scoreMode, twoPhase);
8283
}
84+
85+
@Override
86+
public Explanation explain(LeafReaderContext context, int doc) throws IOException {
87+
Explanation constantExplanation = super.explain(context, doc);
88+
if (constantExplanation.isMatch()) {
89+
return explainMatch(boost, constantExplanation.getDescription());
90+
}
91+
return constantExplanation;
92+
}
8393
};
8494
}
8595

@@ -98,4 +108,17 @@ public boolean equals(Object obj) {
98108
AbstractScriptFieldQuery<?> other = (AbstractScriptFieldQuery<?>) obj;
99109
return script.equals(other.script) && fieldName.equals(other.fieldName);
100110
}
111+
112+
final Explanation explainMatch(float boost, String description) {
113+
return Explanation.match(
114+
boost,
115+
description,
116+
Explanation.match(
117+
boost,
118+
"boost * runtime_field_score",
119+
Explanation.match(boost, "boost"),
120+
Explanation.match(1.0, "runtime_field_score is always 1")
121+
)
122+
);
123+
}
101124
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.runtimefields.query;
8+
9+
import org.apache.lucene.search.Explanation;
10+
import org.elasticsearch.script.Script;
11+
import org.elasticsearch.test.ESTestCase;
12+
import org.elasticsearch.xpack.runtimefields.mapper.AbstractFieldScript;
13+
14+
import java.io.IOException;
15+
16+
import static org.hamcrest.Matchers.equalTo;
17+
18+
public class AbstractScriptFieldQueryTests extends ESTestCase {
19+
public void testExplainMatched() throws IOException {
20+
AbstractScriptFieldQuery<AbstractFieldScript> query = new AbstractScriptFieldQuery<AbstractFieldScript>(
21+
new Script("test"),
22+
"test",
23+
null
24+
) {
25+
@Override
26+
protected boolean matches(AbstractFieldScript scriptContext, int docId) {
27+
throw new UnsupportedOperationException();
28+
}
29+
30+
@Override
31+
public String toString(String field) {
32+
throw new UnsupportedOperationException();
33+
}
34+
};
35+
float boost = randomBoolean() ? 1.0f : randomFloat();
36+
String dummyDescription = randomAlphaOfLength(10);
37+
assertThat(
38+
query.explainMatch(boost, dummyDescription),
39+
equalTo(
40+
Explanation.match(
41+
boost,
42+
dummyDescription,
43+
Explanation.match(
44+
boost,
45+
"boost * runtime_field_score",
46+
Explanation.match(boost, "boost"),
47+
Explanation.match(1.0, "runtime_field_score is always 1")
48+
)
49+
)
50+
)
51+
);
52+
}
53+
}

x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/10_keyword.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,76 @@ setup:
155155
- match: {hits.total.value: 1}
156156
- match: {hits.hits.0._source.voltage: 5.8}
157157

158+
---
159+
"explain term query":
160+
- do:
161+
search:
162+
index: sensor
163+
explain: true
164+
body:
165+
query:
166+
term:
167+
day_of_week: Monday
168+
- match: {hits.hits.0._explanation.value: 1.0}
169+
- match: {hits.hits.0._explanation.description: day_of_week:Monday}
170+
- match: {hits.hits.0._explanation.details.0.value: 1.0}
171+
- match: {hits.hits.0._explanation.details.0.description: 'boost * runtime_field_score'}
172+
- match: {hits.hits.0._explanation.details.0.details.0.value: 1.0}
173+
- match: {hits.hits.0._explanation.details.0.details.0.description: 'boost'}
174+
- match: {hits.hits.0._explanation.details.0.details.1.value: 1.0}
175+
- match: {hits.hits.0._explanation.details.0.details.1.description: 'runtime_field_score is always 1'}
176+
177+
---
178+
"explain term query with boost":
179+
- do:
180+
search:
181+
index: sensor
182+
explain: true
183+
body:
184+
query:
185+
term:
186+
day_of_week:
187+
value: Monday
188+
boost: 7
189+
- match: {hits.hits.0._explanation.value: 7.0}
190+
- match: {hits.hits.0._explanation.description: day_of_week:Monday^7.0}
191+
- match: {hits.hits.0._explanation.details.0.value: 7.0}
192+
- match: {hits.hits.0._explanation.details.0.description: 'boost * runtime_field_score'}
193+
- match: {hits.hits.0._explanation.details.0.details.0.value: 7.0}
194+
- match: {hits.hits.0._explanation.details.0.details.0.description: 'boost'}
195+
- match: {hits.hits.0._explanation.details.0.details.1.value: 1.0}
196+
- match: {hits.hits.0._explanation.details.0.details.1.description: 'runtime_field_score is always 1'}
197+
198+
---
199+
"explain term query wrapped in script score":
200+
- do:
201+
search:
202+
index: sensor
203+
explain: true
204+
body:
205+
query:
206+
script_score:
207+
script:
208+
source: _score * 1000
209+
query:
210+
term:
211+
day_of_week:
212+
value: Monday
213+
boost: 7
214+
- match: {hits.hits.0._explanation.value: 7000.0}
215+
- match: {hits.hits.0._explanation.description: "script score function, computed with script:\"Script{type=inline, lang='painless', idOrCode='_score * 1000', options={}, params={}}\""}
216+
- match: {hits.hits.0._explanation.details.0.value: 7.0}
217+
- match: {hits.hits.0._explanation.details.0.description: '_score: '}
218+
- match: {hits.hits.0._explanation.details.0.details.0.value: 7.0}
219+
- match: {hits.hits.0._explanation.details.0.details.0.description: day_of_week:Monday^7.0}
220+
- match: {hits.hits.0._explanation.details.0.details.0.details.0.value: 7.0}
221+
- match: {hits.hits.0._explanation.details.0.details.0.details.0.description: 'boost * runtime_field_score'}
222+
- match: {hits.hits.0._explanation.details.0.details.0.details.0.details.0.value: 7.0}
223+
- match: {hits.hits.0._explanation.details.0.details.0.details.0.details.0.description: 'boost'}
224+
- match: {hits.hits.0._explanation.details.0.details.0.details.0.details.1.value: 1.0}
225+
- match: {hits.hits.0._explanation.details.0.details.0.details.0.details.1.description: 'runtime_field_score is always 1'}
226+
227+
158228
---
159229
"match query":
160230
- do:

x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/20_long.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,25 @@ setup:
172172
- match: {hits.total.value: 1}
173173
- match: {hits.hits.0._source.voltage: 5.8}
174174

175+
---
176+
"explain term query":
177+
- do:
178+
search:
179+
index: sensor
180+
explain: true
181+
body:
182+
query:
183+
term:
184+
voltage_times_ten: 58
185+
- match: {hits.hits.0._explanation.value: 1.0}
186+
- match: {hits.hits.0._explanation.description: voltage_times_ten:58}
187+
- match: {hits.hits.0._explanation.details.0.value: 1.0}
188+
- match: {hits.hits.0._explanation.details.0.description: 'boost * runtime_field_score'}
189+
- match: {hits.hits.0._explanation.details.0.details.0.value: 1.0}
190+
- match: {hits.hits.0._explanation.details.0.details.0.description: 'boost'}
191+
- match: {hits.hits.0._explanation.details.0.details.1.value: 1.0}
192+
- match: {hits.hits.0._explanation.details.0.details.1.description: 'runtime_field_score is always 1'}
193+
175194
---
176195
"nested":
177196
- do:

x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/30_double.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,22 @@ setup:
194194
voltage_percent: 1.0
195195
- match: {hits.total.value: 1}
196196
- match: {hits.hits.0._source.voltage: 5.8}
197+
198+
---
199+
"explain term query":
200+
- do:
201+
search:
202+
index: sensor
203+
explain: true
204+
body:
205+
query:
206+
term:
207+
voltage_percent: 1.0
208+
- match: {hits.hits.0._explanation.value: 1.0}
209+
- match: {hits.hits.0._explanation.description: voltage_percent:1.0}
210+
- match: {hits.hits.0._explanation.details.0.value: 1.0}
211+
- match: {hits.hits.0._explanation.details.0.description: 'boost * runtime_field_score'}
212+
- match: {hits.hits.0._explanation.details.0.details.0.value: 1.0}
213+
- match: {hits.hits.0._explanation.details.0.details.0.description: 'boost'}
214+
- match: {hits.hits.0._explanation.details.0.details.1.value: 1.0}
215+
- match: {hits.hits.0._explanation.details.0.details.1.description: 'runtime_field_score is always 1'}

x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/40_date.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,22 @@ setup:
183183
tomorrow: 2018-01-19T17:41:34Z
184184
- match: {hits.total.value: 1}
185185
- match: {hits.hits.0._source.voltage: 4.0}
186+
187+
---
188+
"explain term query":
189+
- do:
190+
search:
191+
index: sensor
192+
explain: true
193+
body:
194+
query:
195+
term:
196+
tomorrow: 2018-01-19T17:41:34Z
197+
- match: {hits.hits.0._explanation.value: 1.0}
198+
- match: {hits.hits.0._explanation.description: tomorrow:1516383694000}
199+
- match: {hits.hits.0._explanation.details.0.value: 1.0}
200+
- match: {hits.hits.0._explanation.details.0.description: 'boost * runtime_field_score'}
201+
- match: {hits.hits.0._explanation.details.0.details.0.value: 1.0}
202+
- match: {hits.hits.0._explanation.details.0.details.0.description: 'boost'}
203+
- match: {hits.hits.0._explanation.details.0.details.1.value: 1.0}
204+
- match: {hits.hits.0._explanation.details.0.details.1.description: 'runtime_field_score is always 1'}

x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/50_ip.yml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,21 @@ setup:
160160
- match: {hits.total.value: 1}
161161
- match: {hits.hits.0._source.timestamp: "1998-04-30T14:31:27-05:00"}
162162

163-
# TODO tests for using the ip in a script. there is almost certainly whitelist "fun" here.
163+
---
164+
"explain term query":
165+
- do:
166+
search:
167+
index: http_logs
168+
explain: true
169+
body:
170+
query:
171+
term:
172+
ip: 252.0.0.0
173+
- match: {hits.hits.0._explanation.value: 1.0}
174+
- match: {hits.hits.0._explanation.description: ip:252.0.0.0}
175+
- match: {hits.hits.0._explanation.details.0.value: 1.0}
176+
- match: {hits.hits.0._explanation.details.0.description: 'boost * runtime_field_score'}
177+
- match: {hits.hits.0._explanation.details.0.details.0.value: 1.0}
178+
- match: {hits.hits.0._explanation.details.0.details.0.description: 'boost'}
179+
- match: {hits.hits.0._explanation.details.0.details.1.value: 1.0}
180+
- match: {hits.hits.0._explanation.details.0.details.1.description: 'runtime_field_score is always 1'}

x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/60_boolean.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,22 @@ setup:
135135
timestamp: asc
136136
- match: {hits.total.value: 4}
137137
- match: {hits.hits.0._source.voltage: 5.6}
138+
139+
---
140+
"explain term query":
141+
- do:
142+
search:
143+
index: sensor
144+
explain: true
145+
body:
146+
query:
147+
term:
148+
over_v: true
149+
- match: {hits.hits.0._explanation.value: 1.0}
150+
- match: {hits.hits.0._explanation.description: over_v:true}
151+
- match: {hits.hits.0._explanation.details.0.value: 1.0}
152+
- match: {hits.hits.0._explanation.details.0.description: 'boost * runtime_field_score'}
153+
- match: {hits.hits.0._explanation.details.0.details.0.value: 1.0}
154+
- match: {hits.hits.0._explanation.details.0.details.0.description: 'boost'}
155+
- match: {hits.hits.0._explanation.details.0.details.1.value: 1.0}
156+
- match: {hits.hits.0._explanation.details.0.details.1.description: 'runtime_field_score is always 1'}

0 commit comments

Comments
 (0)