XContentExtraction: Fix state bug

This fixes a nasty bug, when trying to extract arrays, that contain objects
from a searchinput. Reason for this is, that we reset the state to early, while
still being in an array and not having read all of the embedded objects.

With Jackson 2.6 we should reuse jacksons capabilities to not only filter for
xcontent builders, but also for parsers. Not the nicest code ever.

Closes elastic/elasticsearch#852

Original commit: elastic/x-pack-elasticsearch@2682254644
This commit is contained in:
Your Full Name 2015-10-27 14:21:04 +01:00 committed by uboness
parent 6106128272
commit bf4c42a40f
2 changed files with 35 additions and 9 deletions

View File

@ -33,6 +33,10 @@ public final class XContentFilterKeysUtils {
}
private static Map<String, Object> parse(XContentParser parser, State state) throws IOException {
return parse(parser, state, true);
}
private static Map<String, Object> parse(XContentParser parser, State state, boolean isOutsideOfArray) throws IOException {
if (state.includeLeaf) {
return parser.map();
}
@ -46,12 +50,14 @@ public final class XContentFilterKeysUtils {
case START_OBJECT:
if (state.includeKey) {
String fieldName = state.currentFieldName();
Map<String, Object> nestedData = parse(parser, state);
Map<String, Object> nestedData = parse(parser, state, isOutsideOfArray);
data.put(fieldName, nestedData);
} else {
parser.skipChildren();
}
state.previousField();
if (isOutsideOfArray) {
state.previousField();
}
break;
case START_ARRAY:
if (state.includeKey) {
@ -91,7 +97,7 @@ public final class XContentFilterKeysUtils {
for (XContentParser.Token token = parser.nextToken(); token != END_ARRAY; token = parser.nextToken()) {
switch (token) {
case START_OBJECT:
values.add(parse(parser, state));
values.add(parse(parser, state, false));
break;
case VALUE_STRING:
values.add(parser.text());

View File

@ -12,15 +12,12 @@ import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.ESTestCase;
import org.hamcrest.Matchers;
import org.junit.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.*;
/**
*/
@ -114,6 +111,29 @@ public class FilterXContentTests extends ESTestCase {
assertThat(selectMap(filteredData, "leaf3", "key2").get("key2"), Matchers.<Object>equalTo("value2"));
}
// issue #852
public void testArraysAreNotCutOff() throws Exception {
XContentBuilder builder = jsonBuilder().startObject().startArray("buckets")
.startObject().startObject("foo").startObject("values").endObject().endObject().endObject()
.startObject().startObject("foo").startObject("values").endObject().endObject().endObject()
.endArray().endObject();
XContentParser parser = XContentHelper.createParser(builder.bytes());
Set<String> keys = new HashSet<>();
keys.add("buckets.foo.values");
Map<String, Object> filteredData = XContentFilterKeysUtils.filterMapOrdered(keys, parser);
assertThat(filteredData.get("buckets"), instanceOf(List.class));
// both buckets have to include the following keys
List<Map<String, Object>> buckets = (List<Map<String, Object>>) filteredData.get("buckets");
assertThat(buckets, hasSize(2));
assertThat(buckets.get(0).keySet(), containsInAnyOrder("foo"));
assertThat(buckets.get(1).keySet(), containsInAnyOrder("foo"));
}
@SuppressWarnings("unchecked")
private static Map<String, Object> selectMap(Map<String, Object> data, String... path) {
for (String element : path) {