mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 10:25:15 +00:00
Fix nested _source retrieval with includes/excludes (#33180)
If an exclude or an include clause removes an entry to a nested field in the original source at query time, the creation of nested hits fails with an NPE. This change fixes this exception and replaces the nested document source with an empty map. Closes #33163 Closes #33170
This commit is contained in:
parent
557eabf7b5
commit
1404dd2a42
@ -57,6 +57,7 @@ public final class FetchSourceSubPhase implements FetchSubPhase {
|
||||
if (nestedHit) {
|
||||
value = getNestedSource((Map<String, Object>) value, hitContext);
|
||||
}
|
||||
|
||||
try {
|
||||
final int initialCapacity = nestedHit ? 1024 : Math.min(1024, source.internalSourceRef().length());
|
||||
BytesStreamOutput streamOutput = new BytesStreamOutput(initialCapacity);
|
||||
@ -81,6 +82,9 @@ public final class FetchSourceSubPhase implements FetchSubPhase {
|
||||
private Map<String, Object> getNestedSource(Map<String, Object> sourceAsMap, HitContext hitContext) {
|
||||
for (SearchHit.NestedIdentity o = hitContext.hit().getNestedIdentity(); o != null; o = o.getChild()) {
|
||||
sourceAsMap = (Map<String, Object>) sourceAsMap.get(o.getField().string());
|
||||
if (sourceAsMap == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return sourceAsMap;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import org.elasticsearch.test.TestSearchContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@ -78,6 +79,29 @@ public class FetchSourceSubPhaseTests extends ESTestCase {
|
||||
assertEquals(Collections.singletonMap("field","value"), hitContext.hit().getSourceAsMap());
|
||||
}
|
||||
|
||||
public void testNestedSource() throws IOException {
|
||||
Map<String, Object> expectedNested = Collections.singletonMap("nested2", Collections.singletonMap("field", "value0"));
|
||||
XContentBuilder source = XContentFactory.jsonBuilder().startObject()
|
||||
.field("field", "value")
|
||||
.field("field2", "value2")
|
||||
.field("nested1", expectedNested)
|
||||
.endObject();
|
||||
FetchSubPhase.HitContext hitContext = hitExecuteMultiple(source, true, null, null,
|
||||
new SearchHit.NestedIdentity("nested1", 0,null));
|
||||
assertEquals(expectedNested, hitContext.hit().getSourceAsMap());
|
||||
hitContext = hitExecuteMultiple(source, true, new String[]{"invalid"}, null,
|
||||
new SearchHit.NestedIdentity("nested1", 0,null));
|
||||
assertEquals(Collections.emptyMap(), hitContext.hit().getSourceAsMap());
|
||||
|
||||
hitContext = hitExecuteMultiple(source, true, null, null,
|
||||
new SearchHit.NestedIdentity("nested1", 0, new SearchHit.NestedIdentity("nested2", 0, null)));
|
||||
assertEquals(Collections.singletonMap("field", "value0"), hitContext.hit().getSourceAsMap());
|
||||
|
||||
hitContext = hitExecuteMultiple(source, true, new String[]{"invalid"}, null,
|
||||
new SearchHit.NestedIdentity("nested1", 0, new SearchHit.NestedIdentity("nested2", 0, null)));
|
||||
assertEquals(Collections.emptyMap(), hitContext.hit().getSourceAsMap());
|
||||
}
|
||||
|
||||
public void testSourceDisabled() throws IOException {
|
||||
FetchSubPhase.HitContext hitContext = hitExecute(null, true, null, null);
|
||||
assertNull(hitContext.hit().getSourceAsMap());
|
||||
@ -96,17 +120,29 @@ public class FetchSourceSubPhaseTests extends ESTestCase {
|
||||
}
|
||||
|
||||
private FetchSubPhase.HitContext hitExecute(XContentBuilder source, boolean fetchSource, String include, String exclude) {
|
||||
return hitExecute(source, fetchSource, include, exclude, null);
|
||||
}
|
||||
|
||||
|
||||
private FetchSubPhase.HitContext hitExecute(XContentBuilder source, boolean fetchSource, String include, String exclude,
|
||||
SearchHit.NestedIdentity nestedIdentity) {
|
||||
return hitExecuteMultiple(source, fetchSource,
|
||||
include == null ? Strings.EMPTY_ARRAY : new String[]{include},
|
||||
exclude == null ? Strings.EMPTY_ARRAY : new String[]{exclude});
|
||||
exclude == null ? Strings.EMPTY_ARRAY : new String[]{exclude}, nestedIdentity);
|
||||
}
|
||||
|
||||
private FetchSubPhase.HitContext hitExecuteMultiple(XContentBuilder source, boolean fetchSource, String[] includes, String[] excludes) {
|
||||
return hitExecuteMultiple(source, fetchSource, includes, excludes, null);
|
||||
}
|
||||
|
||||
private FetchSubPhase.HitContext hitExecuteMultiple(XContentBuilder source, boolean fetchSource, String[] includes, String[] excludes,
|
||||
SearchHit.NestedIdentity nestedIdentity) {
|
||||
FetchSourceContext fetchSourceContext = new FetchSourceContext(fetchSource, includes, excludes);
|
||||
SearchContext searchContext = new FetchSourceSubPhaseTestSearchContext(fetchSourceContext,
|
||||
source == null ? null : BytesReference.bytes(source));
|
||||
FetchSubPhase.HitContext hitContext = new FetchSubPhase.HitContext();
|
||||
hitContext.reset(new SearchHit(1, null, null, null), null, 1, null);
|
||||
final SearchHit searchHit = new SearchHit(1, null, null, nestedIdentity, null);
|
||||
hitContext.reset(searchHit, null, 1, null);
|
||||
FetchSourceSubPhase phase = new FetchSourceSubPhase();
|
||||
phase.hitExecute(searchContext, hitContext);
|
||||
return hitContext;
|
||||
|
Loading…
x
Reference in New Issue
Block a user