diff --git a/docs/changelog/128989.yaml b/docs/changelog/128989.yaml new file mode 100644 index 0000000000000..94ee4c6923d7b --- /dev/null +++ b/docs/changelog/128989.yaml @@ -0,0 +1,6 @@ +pr: 128989 +summary: Fix NPE in semantic highlighter +area: Search +type: bug +issues: + - 128975 diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/highlight/SemanticTextHighlighter.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/highlight/SemanticTextHighlighter.java index 4dff2723115ea..db1cd001119e2 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/highlight/SemanticTextHighlighter.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/highlight/SemanticTextHighlighter.java @@ -209,6 +209,9 @@ private List extractOffsetAndScores( leafQueries.stream().forEach(q -> bq.add(q, BooleanClause.Occur.SHOULD)); Weight weight = new IndexSearcher(reader).createWeight(bq.build(), ScoreMode.COMPLETE, 1); Scorer scorer = weight.scorer(reader.getContext()); + if (scorer == null) { + return List.of(); + } if (previousParent != -1) { if (scorer.iterator().advance(previousParent) == DocIdSetIterator.NO_MORE_DOCS) { return List.of(); diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/highlight/SemanticTextHighlighterTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/highlight/SemanticTextHighlighterTests.java index b808dce4f9750..bd966872a9d13 100644 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/highlight/SemanticTextHighlighterTests.java +++ b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/highlight/SemanticTextHighlighterTests.java @@ -32,6 +32,8 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperServiceTestCase; import org.elasticsearch.index.mapper.SourceToParse; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.NestedQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.shard.ShardId; @@ -170,6 +172,34 @@ public void testSparseVector() throws Exception { ); } + @SuppressWarnings("unchecked") + public void testNoSemanticField() throws Exception { + var mapperService = createDefaultMapperService(useLegacyFormat); + Map queryMap = (Map) queries.get("sparse_vector_1"); + List tokens = readSparseVector(queryMap.get("embeddings")); + var fieldType = (SemanticTextFieldMapper.SemanticTextFieldType) mapperService.mappingLookup().getFieldType(SEMANTIC_FIELD_ELSER); + SparseVectorQueryBuilder sparseQuery = new SparseVectorQueryBuilder( + fieldType.getEmbeddingsField().fullPath(), + tokens, + null, + null, + null, + null + ); + var query = new BoolQueryBuilder().should(sparseQuery).should(new MatchAllQueryBuilder()); + var shardRequest = createShardSearchRequest(query); + var sourceToParse = new SourceToParse("0", new BytesArray("{}"), XContentType.JSON); + assertHighlightOneDoc( + mapperService, + shardRequest, + sourceToParse, + SEMANTIC_FIELD_ELSER, + 10, + HighlightBuilder.Order.SCORE, + new String[0] + ); + } + private MapperService createDefaultMapperService(boolean useLegacyFormat) throws IOException { var mappings = Streams.readFully(SemanticTextHighlighterTests.class.getResourceAsStream("mappings.json")); var settings = Settings.builder() @@ -264,9 +294,13 @@ private void assertHighlightOneDoc( new HashMap<>() ); var result = highlighter.highlight(context); - assertThat(result.fragments().length, equalTo(expectedPassages.length)); - for (int i = 0; i < result.fragments().length; i++) { - assertThat(result.fragments()[i].string(), equalTo(expectedPassages[i])); + if (result == null) { + assertThat(expectedPassages.length, equalTo(0)); + } else { + assertThat(result.fragments().length, equalTo(expectedPassages.length)); + for (int i = 0; i < result.fragments().length; i++) { + assertThat(result.fragments()[i].string(), equalTo(expectedPassages[i])); + } } } } finally {