Skip to content

Commit acb7f59

Browse files
committed
Fix an NPE when requesting inner hits and _source is disabled. (#44836)
This PR makes two changes to FetchSourceSubPhase when _source is disabled and we're in a nested context: * If no source filters are provided, return early to avoid an NPE. * If there are source filters, make sure to throw an exception. The behavior was chosen to match what currently happens in a non-nested context.
1 parent 6a60fd6 commit acb7f59

File tree

2 files changed

+28
-7
lines changed

2 files changed

+28
-7
lines changed

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,23 @@ public void hitExecute(SearchContext context, HitContext hitContext) {
4242
SourceLookup source = context.lookup().source();
4343
FetchSourceContext fetchSourceContext = context.fetchSourceContext();
4444
assert fetchSourceContext.fetchSource();
45-
if (nestedHit == false) {
46-
if (fetchSourceContext.includes().length == 0 && fetchSourceContext.excludes().length == 0) {
47-
hitContext.hit().sourceRef(source.internalSourceRef());
48-
return;
49-
}
50-
if (source.internalSourceRef() == null) {
45+
46+
// If source is disabled in the mapping, then attempt to return early.
47+
if (source.source() == null && source.internalSourceRef() == null) {
48+
if (containsFilters(fetchSourceContext)) {
5149
throw new IllegalArgumentException("unable to fetch fields from _source field: _source is disabled in the mappings " +
52-
"for index [" + context.indexShard().shardId().getIndexName() + "]");
50+
"for index [" + context.indexShard().shardId().getIndexName() + "]");
5351
}
52+
return;
53+
}
54+
55+
// If this is a parent document and there are no source filters, then add the source as-is.
56+
if (nestedHit == false && containsFilters(fetchSourceContext) == false) {
57+
hitContext.hit().sourceRef(source.internalSourceRef());
58+
return;
5459
}
5560

61+
// Otherwise, filter the source and add it to the hit.
5662
Object value = source.filter(fetchSourceContext);
5763
if (nestedHit) {
5864
value = getNestedSource((Map<String, Object>) value, hitContext);
@@ -79,6 +85,10 @@ public void hitExecute(SearchContext context, HitContext hitContext) {
7985
}
8086
}
8187

88+
private static boolean containsFilters(FetchSourceContext context) {
89+
return context.includes().length != 0 || context.excludes().length != 0;
90+
}
91+
8292
private Map<String, Object> getNestedSource(Map<String, Object> sourceAsMap, HitContext hitContext) {
8393
for (SearchHit.NestedIdentity o = hitContext.hit().getNestedIdentity(); o != null; o = o.getChild()) {
8494
sourceAsMap = (Map<String, Object>) sourceAsMap.get(o.getField().string());

server/src/test/java/org/elasticsearch/search/fetch/subphase/FetchSourceSubPhaseTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,17 @@ public void testSourceDisabled() throws IOException {
119119
"for index [index]", exception.getMessage());
120120
}
121121

122+
public void testNestedSourceWithSourceDisabled() {
123+
FetchSubPhase.HitContext hitContext = hitExecute(null, true, null, null,
124+
new SearchHit.NestedIdentity("nested1", 0, null));
125+
assertNull(hitContext.hit().getSourceAsMap());
126+
127+
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
128+
() -> hitExecute(null, true, "field1", null, new SearchHit.NestedIdentity("nested1", 0, null)));
129+
assertEquals("unable to fetch fields from _source field: _source is disabled in the mappings " +
130+
"for index [index]", e.getMessage());
131+
}
132+
122133
private FetchSubPhase.HitContext hitExecute(XContentBuilder source, boolean fetchSource, String include, String exclude) {
123134
return hitExecute(source, fetchSource, include, exclude, null);
124135
}

0 commit comments

Comments
 (0)