Skip to content

Commit 3f98b85

Browse files
committed
inner_hits: Return an empty _source for nested inner hit when filtering on a field that doesn't exist.
Before this change the search request would fail with an error indicating that it couldn't detect xcontent type based on the string: `null`
1 parent 4ab638b commit 3f98b85

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

core/src/main/java/org/elasticsearch/search/fetch/subphase/FetchSourceSubPhase.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,16 @@
2020
package org.elasticsearch.search.fetch.subphase;
2121

2222
import org.elasticsearch.ElasticsearchException;
23-
import org.elasticsearch.common.bytes.BytesReference;
2423
import org.elasticsearch.common.io.stream.BytesStreamOutput;
2524
import org.elasticsearch.common.xcontent.XContentBuilder;
26-
import org.elasticsearch.common.xcontent.XContentType;
2725
import org.elasticsearch.search.SearchHit;
2826
import org.elasticsearch.search.fetch.FetchSubPhase;
2927
import org.elasticsearch.search.internal.SearchContext;
3028
import org.elasticsearch.search.lookup.SourceLookup;
3129

3230
import java.io.IOException;
33-
import java.io.UncheckedIOException;
3431
import java.util.Map;
3532

36-
import static org.elasticsearch.common.xcontent.XContentFactory.contentBuilder;
37-
3833
public final class FetchSourceSubPhase implements FetchSubPhase {
3934

4035
@Override
@@ -65,7 +60,17 @@ public void hitExecute(SearchContext context, HitContext hitContext) {
6560
final int initialCapacity = nestedHit ? 1024 : Math.min(1024, source.internalSourceRef().length());
6661
BytesStreamOutput streamOutput = new BytesStreamOutput(initialCapacity);
6762
XContentBuilder builder = new XContentBuilder(source.sourceContentType().xContent(), streamOutput);
68-
builder.value(value);
63+
if (value != null) {
64+
builder.value(value);
65+
} else {
66+
// This happens if the source filtering could not find the specified in the _source.
67+
// Just doing `builder.value(null)` is valid, but the xcontent validation can't detect what format
68+
// it is. In certain cases, for example response serialization we fail if no xcontent type can't be
69+
// detected. So instead we just return an empty top level object. Also this is in inline with what was
70+
// being return in this situation in 5.x and earlier.
71+
builder.startObject();
72+
builder.endObject();
73+
}
6974
hitContext.hit().sourceRef(builder.bytes());
7075
} catch (IOException e) {
7176
throw new ElasticsearchException("Error filtering source", e);

core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@
5656

5757
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
5858
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
59-
import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue;
6059
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
6160
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
6261
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
@@ -635,6 +634,18 @@ public void testNestedSource() throws Exception {
635634
assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap().size(), equalTo(2));
636635
assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(1).getSourceAsMap().get("message"),
637636
equalTo("fox ate rabbit x y z"));
637+
638+
// Source filter on a field that does not exist inside the nested document and just check that we do not fail and
639+
// return an empty _source:
640+
response = client().prepareSearch()
641+
.setQuery(nestedQuery("comments", matchQuery("comments.message", "away"), ScoreMode.None)
642+
.innerHit(new InnerHitBuilder().setFetchSourceContext(new FetchSourceContext(true,
643+
new String[]{"comments.missing_field"}, null))))
644+
.get();
645+
assertNoFailures(response);
646+
assertHitCount(response, 1);
647+
assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getTotalHits(), equalTo(1L));
648+
assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap().size(), equalTo(0));
638649
}
639650

640651
public void testInnerHitsWithIgnoreUnmapped() throws Exception {

0 commit comments

Comments
 (0)