25
25
import org .apache .lucene .search .BooleanClause ;
26
26
import org .apache .lucene .search .DocIdSetIterator ;
27
27
import org .apache .lucene .search .Explanation ;
28
- import org .apache .lucene .search .IndexSearcher ;
28
+ import org .apache .lucene .search .FilterLeafCollector ;
29
+ import org .apache .lucene .search .LeafCollector ;
29
30
import org .apache .lucene .search .Query ;
31
+ import org .apache .lucene .search .Scorable ;
32
+ import org .apache .lucene .search .Weight ;
33
+ import org .apache .lucene .search .IndexSearcher ;
30
34
import org .apache .lucene .search .QueryVisitor ;
31
35
import org .apache .lucene .search .ScoreMode ;
32
36
import org .apache .lucene .search .Scorer ;
33
- import org .apache .lucene .search .Weight ;
37
+ import org .apache .lucene .search .BulkScorer ;
38
+ import org .apache .lucene .util .Bits ;
34
39
import org .elasticsearch .ElasticsearchException ;
35
40
import org .elasticsearch .Version ;
36
41
import org .elasticsearch .script .ScoreScript ;
@@ -83,6 +88,19 @@ public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float bo
83
88
Weight subQueryWeight = subQuery .createWeight (searcher , subQueryScoreMode , boost );
84
89
85
90
return new Weight (this ){
91
+ @ Override
92
+ public BulkScorer bulkScorer (LeafReaderContext context ) throws IOException {
93
+ if (minScore == null ) {
94
+ final BulkScorer subQueryBulkScorer = subQueryWeight .bulkScorer (context );
95
+ if (subQueryBulkScorer == null ) {
96
+ return null ;
97
+ }
98
+ return new ScriptScoreBulkScorer (subQueryBulkScorer , subQueryScoreMode , makeScoreScript (context ));
99
+ } else {
100
+ return super .bulkScorer (context );
101
+ }
102
+ }
103
+
86
104
@ Override
87
105
public void extractTerms (Set <Term > terms ) {
88
106
subQueryWeight .extractTerms (terms );
@@ -94,8 +112,7 @@ public Scorer scorer(LeafReaderContext context) throws IOException {
94
112
if (subQueryScorer == null ) {
95
113
return null ;
96
114
}
97
- Scorer scriptScorer = makeScriptScorer (subQueryScorer , context , null );
98
-
115
+ Scorer scriptScorer = new ScriptScorer (this , makeScoreScript (context ), subQueryScorer , subQueryScoreMode , null );
99
116
if (minScore != null ) {
100
117
scriptScorer = new MinScoreScorer (this , scriptScorer , minScore );
101
118
}
@@ -109,7 +126,8 @@ public Explanation explain(LeafReaderContext context, int doc) throws IOExceptio
109
126
return subQueryExplanation ;
110
127
}
111
128
ExplanationHolder explanationHolder = new ExplanationHolder ();
112
- Scorer scorer = makeScriptScorer (subQueryWeight .scorer (context ), context , explanationHolder );
129
+ Scorer scorer = new ScriptScorer (this , makeScoreScript (context ),
130
+ subQueryWeight .scorer (context ), subQueryScoreMode , explanationHolder );
113
131
int newDoc = scorer .iterator ().advance (doc );
114
132
assert doc == newDoc ; // subquery should have already matched above
115
133
float score = scorer .score ();
@@ -132,42 +150,13 @@ public Explanation explain(LeafReaderContext context, int doc) throws IOExceptio
132
150
}
133
151
return explanation ;
134
152
}
135
-
136
- private Scorer makeScriptScorer (Scorer subQueryScorer , LeafReaderContext context ,
137
- ExplanationHolder explanation ) throws IOException {
153
+
154
+ private ScoreScript makeScoreScript (LeafReaderContext context ) throws IOException {
138
155
final ScoreScript scoreScript = scriptBuilder .newInstance (context );
139
- scoreScript .setScorer (subQueryScorer );
140
156
scoreScript ._setIndexName (indexName );
141
157
scoreScript ._setShard (shardId );
142
158
scoreScript ._setIndexVersion (indexVersion );
143
-
144
- return new Scorer (this ) {
145
- @ Override
146
- public float score () throws IOException {
147
- int docId = docID ();
148
- scoreScript .setDocument (docId );
149
- float score = (float ) scoreScript .execute (explanation );
150
- if (score == Float .NEGATIVE_INFINITY || Float .isNaN (score )) {
151
- throw new ElasticsearchException (
152
- "script score query returned an invalid score: " + score + " for doc: " + docId );
153
- }
154
- return score ;
155
- }
156
- @ Override
157
- public int docID () {
158
- return subQueryScorer .docID ();
159
- }
160
-
161
- @ Override
162
- public DocIdSetIterator iterator () {
163
- return subQueryScorer .iterator ();
164
- }
165
-
166
- @ Override
167
- public float getMaxScore (int upTo ) {
168
- return Float .MAX_VALUE ; // TODO: what would be a good upper bound?
169
- }
170
- };
159
+ return scoreScript ;
171
160
}
172
161
173
162
@ Override
@@ -187,7 +176,7 @@ public void visit(QueryVisitor visitor) {
187
176
@ Override
188
177
public String toString (String field ) {
189
178
StringBuilder sb = new StringBuilder ();
190
- sb .append ("script score (" ).append (subQuery .toString (field )).append (", script: " );
179
+ sb .append ("script_score (" ).append (subQuery .toString (field )).append (", script: " );
191
180
sb .append ("{" + script .toString () + "}" );
192
181
return sb .toString ();
193
182
}
@@ -209,4 +198,118 @@ public boolean equals(Object o) {
209
198
public int hashCode () {
210
199
return Objects .hash (subQuery , script , minScore , indexName , shardId , indexVersion );
211
200
}
201
+
202
+
203
+ private static class ScriptScorer extends Scorer {
204
+ private final ScoreScript scoreScript ;
205
+ private final Scorer subQueryScorer ;
206
+ private final ExplanationHolder explanation ;
207
+
208
+ ScriptScorer (Weight weight , ScoreScript scoreScript , Scorer subQueryScorer ,
209
+ ScoreMode subQueryScoreMode , ExplanationHolder explanation ) {
210
+ super (weight );
211
+ this .scoreScript = scoreScript ;
212
+ if (subQueryScoreMode == ScoreMode .COMPLETE ) {
213
+ scoreScript .setScorer (subQueryScorer );
214
+ }
215
+ this .subQueryScorer = subQueryScorer ;
216
+ this .explanation = explanation ;
217
+ }
218
+
219
+ @ Override
220
+ public float score () throws IOException {
221
+ int docId = docID ();
222
+ scoreScript .setDocument (docId );
223
+ float score = (float ) scoreScript .execute (explanation );
224
+ if (score == Float .NEGATIVE_INFINITY || Float .isNaN (score )) {
225
+ throw new ElasticsearchException (
226
+ "script_score query returned an invalid score [" + score + "] for doc [" + docId + "]." );
227
+ }
228
+ return score ;
229
+ }
230
+ @ Override
231
+ public int docID () {
232
+ return subQueryScorer .docID ();
233
+ }
234
+
235
+ @ Override
236
+ public DocIdSetIterator iterator () {
237
+ return subQueryScorer .iterator ();
238
+ }
239
+
240
+ @ Override
241
+ public float getMaxScore (int upTo ) {
242
+ return Float .MAX_VALUE ; // TODO: what would be a good upper bound?
243
+ }
244
+
245
+ }
246
+
247
+ private static class ScriptScorable extends Scorable {
248
+ private final ScoreScript scoreScript ;
249
+ private final Scorable subQueryScorer ;
250
+ private final ExplanationHolder explanation ;
251
+
252
+ ScriptScorable (ScoreScript scoreScript , Scorable subQueryScorer ,
253
+ ScoreMode subQueryScoreMode , ExplanationHolder explanation ) {
254
+ this .scoreScript = scoreScript ;
255
+ if (subQueryScoreMode == ScoreMode .COMPLETE ) {
256
+ scoreScript .setScorer (subQueryScorer );
257
+ }
258
+ this .subQueryScorer = subQueryScorer ;
259
+ this .explanation = explanation ;
260
+ }
261
+
262
+ @ Override
263
+ public float score () throws IOException {
264
+ int docId = docID ();
265
+ scoreScript .setDocument (docId );
266
+ float score = (float ) scoreScript .execute (explanation );
267
+ if (score == Float .NEGATIVE_INFINITY || Float .isNaN (score )) {
268
+ throw new ElasticsearchException (
269
+ "script_score query returned an invalid score [" + score + "] for doc [" + docId + "]." );
270
+ }
271
+ return score ;
272
+ }
273
+ @ Override
274
+ public int docID () {
275
+ return subQueryScorer .docID ();
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Use the {@link BulkScorer} of the sub-query,
281
+ * as it may be significantly faster (e.g. BooleanScorer) than iterating over the scorer
282
+ */
283
+ private static class ScriptScoreBulkScorer extends BulkScorer {
284
+ private final BulkScorer subQueryBulkScorer ;
285
+ private final ScoreMode subQueryScoreMode ;
286
+ private final ScoreScript scoreScript ;
287
+
288
+ ScriptScoreBulkScorer (BulkScorer subQueryBulkScorer , ScoreMode subQueryScoreMode , ScoreScript scoreScript ) {
289
+ this .subQueryBulkScorer = subQueryBulkScorer ;
290
+ this .subQueryScoreMode = subQueryScoreMode ;
291
+ this .scoreScript = scoreScript ;
292
+ }
293
+
294
+ @ Override
295
+ public int score (LeafCollector collector , Bits acceptDocs , int min , int max ) throws IOException {
296
+ return subQueryBulkScorer .score (wrapCollector (collector ), acceptDocs , min , max );
297
+ }
298
+
299
+ private LeafCollector wrapCollector (LeafCollector collector ) {
300
+ return new FilterLeafCollector (collector ) {
301
+ @ Override
302
+ public void setScorer (Scorable scorer ) throws IOException {
303
+ in .setScorer (new ScriptScorable (scoreScript , scorer , subQueryScoreMode , null ));
304
+ }
305
+ };
306
+ }
307
+
308
+ @ Override
309
+ public long cost () {
310
+ return subQueryBulkScorer .cost ();
311
+ }
312
+
313
+ }
314
+
212
315
}
0 commit comments