Skip to content

Commit 149fbf2

Browse files
authored
LTR sometines throw NullPointerException: Cannot read field "approximation" because "top" is null (elastic#120809) (elastic#120827)
* Add check on the DisiPriorityQueue size. * Update docs/changelog/120809.yaml * Add a unit test.
1 parent 8adafb0 commit 149fbf2

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

docs/changelog/120809.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 120809
2+
summary: LTR sometines throw `NullPointerException:` Cannot read field "approximation"
3+
because "top" is null
4+
area: Ranking
5+
type: bug
6+
issues: []

x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractor.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,16 @@ public void setNextReader(LeafReaderContext segmentContext) throws IOException {
5555
}
5656
scorers.add(scorer);
5757
}
58-
rankerIterator = new DisjunctionDISIApproximation(disiPriorityQueue);
58+
59+
rankerIterator = disiPriorityQueue.size() > 0 ? new DisjunctionDISIApproximation(disiPriorityQueue) : null;
5960
}
6061

6162
@Override
6263
public void addFeatures(Map<String, Object> featureMap, int docId) throws IOException {
64+
if (rankerIterator == null) {
65+
return;
66+
}
67+
6368
rankerIterator.advance(docId);
6469
for (int i = 0; i < featureNames.size(); i++) {
6570
Scorer scorer = scorers.get(i);

x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractorTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elasticsearch.index.query.QueryRewriteContext;
2424
import org.elasticsearch.index.query.SearchExecutionContext;
2525
import org.elasticsearch.test.AbstractBuilderTestCase;
26+
import org.elasticsearch.test.ESTestCase;
2627
import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ltr.QueryExtractorBuilder;
2728
import org.elasticsearch.xpack.core.ml.utils.QueryProvider;
2829

@@ -31,12 +32,14 @@
3132
import java.util.HashMap;
3233
import java.util.List;
3334
import java.util.Map;
35+
import java.util.stream.Stream;
3436

3537
import static org.hamcrest.Matchers.anEmptyMap;
3638
import static org.hamcrest.Matchers.hasEntry;
3739
import static org.hamcrest.Matchers.hasKey;
3840
import static org.hamcrest.Matchers.hasSize;
3941
import static org.hamcrest.Matchers.not;
42+
import static org.mockito.Mockito.mock;
4043

4144
public class QueryFeatureExtractorTests extends AbstractBuilderTestCase {
4245

@@ -125,4 +128,29 @@ public void testQueryExtractor() throws IOException {
125128
dir.close();
126129
}
127130

131+
public void testEmptyDisiPriorityQueue() throws IOException {
132+
addDocs(
133+
new String[] { "the quick brown fox", "the slow brown fox", "the grey dog", "yet another string" },
134+
new int[] { 5, 10, 12, 11 }
135+
);
136+
137+
// Scorers returned by weights are null
138+
List<String> featureNames = randomList(1, 10, ESTestCase::randomIdentifier);
139+
List<Weight> weights = Stream.generate(() -> mock(Weight.class)).limit(featureNames.size()).toList();
140+
141+
QueryFeatureExtractor featureExtractor = new QueryFeatureExtractor(featureNames, weights);
142+
143+
for (LeafReaderContext leafReaderContext : searcher.getLeafContexts()) {
144+
int maxDoc = leafReaderContext.reader().maxDoc();
145+
featureExtractor.setNextReader(leafReaderContext);
146+
for (int i = 0; i < maxDoc; i++) {
147+
Map<String, Object> featureMap = new HashMap<>();
148+
featureExtractor.addFeatures(featureMap, i);
149+
assertThat(featureMap, anEmptyMap());
150+
}
151+
}
152+
153+
reader.close();
154+
dir.close();
155+
}
128156
}

0 commit comments

Comments
 (0)