Fix IndexOutOfBoundsException when try to map inner hits with no results returned.

Original Pull Request #1998
Closes #1997

(cherry picked from commit 49324a3)
This commit is contained in:
Sascha Woo 2021-11-23 20:08:50 +01:00
parent 15ca49a92e
commit 2aba7a57fb
2 changed files with 47 additions and 6 deletions

View File

@ -23,8 +23,7 @@ import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.Aggregations;
import org.slf4j.Logger; import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.NestedMetaData; import org.springframework.data.elasticsearch.core.document.NestedMetaData;
@ -44,12 +43,11 @@ import org.springframework.util.Assert;
* @author Mark Paluch * @author Mark Paluch
* @author Roman Puchkovskiy * @author Roman Puchkovskiy
* @author Matt Gilene * @author Matt Gilene
* @author Sascha Woo
* @since 4.0 * @since 4.0
*/ */
class SearchHitMapping<T> { class SearchHitMapping<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchHitMapping.class);
private final Class<T> type; private final Class<T> type;
private final ElasticsearchConverter converter; private final ElasticsearchConverter converter;
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext; private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
@ -173,7 +171,7 @@ class SearchHitMapping<T> {
*/ */
private SearchHits<?> mapInnerDocuments(SearchHits<SearchDocument> searchHits, Class<T> type) { private SearchHits<?> mapInnerDocuments(SearchHits<SearchDocument> searchHits, Class<T> type) {
if (searchHits.getTotalHits() == 0) { if (searchHits.isEmpty()) {
return searchHits; return searchHits;
} }
@ -217,7 +215,7 @@ class SearchHitMapping<T> {
searchHits.getAggregations()); searchHits.getAggregations());
} }
} catch (Exception e) { } catch (Exception e) {
LOGGER.warn("Could not map inner_hits", e); throw new UncategorizedElasticsearchException("Unable to convert inner hits.", e);
} }
return searchHits; return searchHits;

View File

@ -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.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.VersionType; import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.query.InnerHitBuilder;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
import org.elasticsearch.index.query.functionscore.GaussDecayFunctionBuilder; 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.join.query.ParentIdQueryBuilder;
import org.elasticsearch.script.Script; import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType; import org.elasticsearch.script.ScriptType;
@ -3029,6 +3032,46 @@ public abstract class ElasticsearchTemplateTests {
indexOperations.removeAlias(aliasQuery); 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<SampleJoinEntity> 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 @Test // DATAES-541
public void shouldRemoveAlias() { public void shouldRemoveAlias() {