From 2aba7a57fb0a7d4768ca6ed58d51aee7ed6cad8e Mon Sep 17 00:00:00 2001 From: Sascha Woo Date: Tue, 23 Nov 2021 20:08:50 +0100 Subject: [PATCH] Fix IndexOutOfBoundsException when try to map inner hits with no results returned. Original Pull Request #1998 Closes #1997 (cherry picked from commit 49324a3) --- .../elasticsearch/core/SearchHitMapping.java | 10 ++--- .../core/ElasticsearchTemplateTests.java | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/elasticsearch/core/SearchHitMapping.java b/src/main/java/org/springframework/data/elasticsearch/core/SearchHitMapping.java index b89d6ee0e..ed76060be 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/SearchHitMapping.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/SearchHitMapping.java @@ -23,8 +23,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.elasticsearch.search.aggregations.Aggregations; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.data.elasticsearch.UncategorizedElasticsearchException; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.NestedMetaData; @@ -44,12 +43,11 @@ import org.springframework.util.Assert; * @author Mark Paluch * @author Roman Puchkovskiy * @author Matt Gilene + * @author Sascha Woo * @since 4.0 */ class SearchHitMapping { - private static final Logger LOGGER = LoggerFactory.getLogger(SearchHitMapping.class); - private final Class type; private final ElasticsearchConverter converter; private final MappingContext, ElasticsearchPersistentProperty> mappingContext; @@ -173,7 +171,7 @@ class SearchHitMapping { */ private SearchHits mapInnerDocuments(SearchHits searchHits, Class type) { - if (searchHits.getTotalHits() == 0) { + if (searchHits.isEmpty()) { return searchHits; } @@ -217,7 +215,7 @@ class SearchHitMapping { searchHits.getAggregations()); } } catch (Exception e) { - LOGGER.warn("Could not map inner_hits", e); + throw new UncategorizedElasticsearchException("Unable to convert inner hits.", e); } return searchHits; diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java index 2c99e2fdd..097a5bfc3 100755 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java @@ -50,10 +50,13 @@ import org.elasticsearch.cluster.metadata.AliasMetadata; import org.elasticsearch.common.lucene.search.function.CombineFunction; import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder; import org.elasticsearch.index.query.functionscore.GaussDecayFunctionBuilder; +import org.elasticsearch.join.query.HasChildQueryBuilder; +import org.elasticsearch.join.query.JoinQueryBuilders; import org.elasticsearch.join.query.ParentIdQueryBuilder; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; @@ -3029,6 +3032,46 @@ public abstract class ElasticsearchTemplateTests { indexOperations.removeAlias(aliasQuery); } + @Test // #1997 + @DisplayName("should return document with inner hits size zero") + void shouldReturnDocumentWithInnerHitsSizeZero() { + + // given + SampleJoinEntity sampleQuestionEntity1 = new SampleJoinEntity(); + sampleQuestionEntity1.setUuid("q1"); + sampleQuestionEntity1.setText("This is a question"); + sampleQuestionEntity1.setMyJoinField(new JoinField<>("question")); + + SampleJoinEntity sampleAnswerEntity1 = new SampleJoinEntity(); + sampleAnswerEntity1.setUuid("a1"); + sampleAnswerEntity1.setText("This is an answer"); + sampleAnswerEntity1.setMyJoinField(new JoinField<>("answer", sampleQuestionEntity1.getUuid())); + + SampleJoinEntity sampleAnswerEntity2 = new SampleJoinEntity(); + sampleAnswerEntity1.setUuid("a2"); + sampleAnswerEntity1.setText("This is an answer"); + sampleAnswerEntity1.setMyJoinField(new JoinField<>("answer", sampleQuestionEntity1.getUuid())); + + IndexOperations indexOps = operations.indexOps(SampleJoinEntity.class); + operations.save(Arrays.asList(sampleQuestionEntity1, sampleAnswerEntity1, sampleAnswerEntity2)); + indexOps.refresh(); + + // when + Query query = new NativeSearchQueryBuilder().withQuery( + JoinQueryBuilders.hasChildQuery("answer", matchAllQuery(), org.apache.lucene.search.join.ScoreMode.Avg) + .innerHit(new InnerHitBuilder("innerHits").setSize(0))) + .build(); + + SearchHits searchHits = operations.search(query, SampleJoinEntity.class); + + // then + assertThat(searchHits).isNotNull(); + assertThat(searchHits.getTotalHits()).isEqualTo(1); + assertThat(searchHits.getSearchHits()).hasSize(1); + assertThat(searchHits.getSearchHit(0).getInnerHits().size()).isEqualTo(1); + assertThat(searchHits.getSearchHit(0).getInnerHits("innerHits").getTotalHits()).isEqualTo(1); + } + @Test // DATAES-541 public void shouldRemoveAlias() {