Skip to content

Commit

Permalink
Collect multiple paths from XContentMapValues (#68540)
Browse files Browse the repository at this point in the history
Currently, when a document source mixed json object and dotted syntax like e.g.
{ "foo" : { "bar" : 0 }, "foo.bar" : 1}, extracting the values from the source
map via XContentMapValues#extractValue returns after the first value for a path
has been found. Instead we should exhaust all possibilities and return a list of
objects of we find more than one value when extending the lookup path.

Closes #68215
  • Loading branch information
Christoph Büscher committed Feb 11, 2021
1 parent ee0d2e2 commit c1aa708
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,27 @@ private static Object extractValue(String[] pathElements,
Map<?, ?> map = (Map<?, ?>) currentValue;
String key = pathElements[index];
int nextIndex = index + 1;
List<Object> extractedValues = new ArrayList<>();
while (true) {
if (map.containsKey(key)) {
Object mapValue = map.get(key);
if (mapValue == null) {
return nullValue;
}
Object val = extractValue(pathElements, nextIndex, mapValue, nullValue);
if (val != null) {
return val;
extractedValues.add(nullValue);
} else {
Object val = extractValue(pathElements, nextIndex, mapValue, nullValue);
if (val != null) {
extractedValues.add(val);
}
}
}
if (nextIndex == pathElements.length) {
return null;
if (extractedValues.size() == 0) {
return null;
} else if (extractedValues.size() == 1) {
return extractedValues.get(0);
} else {
return extractedValues;
}
}
key += "." + pathElements[nextIndex];
nextIndex++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import static org.elasticsearch.common.xcontent.XContentHelper.convertToMap;
import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize;
Expand Down Expand Up @@ -197,6 +198,17 @@ public void testExtractValueMixedObjects() throws IOException {
}
}

public void testExtractValueMixedDottedObjectNotation() throws IOException {
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
.startObject("foo").field("cat", "meow").endObject()
.field("foo.cat", "miau")
.endObject();
try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) {
Map<String, Object> map = parser.map();
assertThat((List<?>) XContentMapValues.extractValue("foo.cat", map), containsInAnyOrder("meow", "miau"));
}
}

public void testExtractRawValue() throws Exception {
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
.field("test", "value")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,49 @@ public void testMixedObjectValues() throws IOException {
DocumentField field = fields.get("foo.bar");
assertThat(field.getValues().size(), equalTo(1));
assertThat(field.getValue(), equalTo("baz"));

source = XContentFactory.jsonBuilder().startObject()
.startObject("foo").field("cat", "meow").endObject()
.field("foo.cat", "miau")
.endObject();

doc = mapperService.documentMapper().parse(source(Strings.toString(source)));

fields = fetchFields(mapperService, source, "foo.cat");
assertThat(fields.size(), equalTo(1));

field = fields.get("foo.cat");
assertThat(field.getValues().size(), equalTo(2));
assertThat(field.getValues(), containsInAnyOrder("meow", "miau"));

source = XContentFactory.jsonBuilder().startObject()
.startObject("foo").field("cat", "meow").endObject()
.array("foo.cat", "miau", "purr")
.endObject();

doc = mapperService.documentMapper().parse(source(Strings.toString(source)));

fields = fetchFields(mapperService, source, "foo.cat");
assertThat(fields.size(), equalTo(1));

field = fields.get("foo.cat");
assertThat(field.getValues().size(), equalTo(3));
assertThat(field.getValues(), containsInAnyOrder("meow", "miau", "purr"));
}

public void testMixedDottedObjectSyntax() throws IOException {
MapperService mapperService = createMapperService();
XContentBuilder source = XContentFactory.jsonBuilder().startObject()
.startObject("object").field("field", "value").endObject()
.field("object.field", "value2")
.endObject();

Map<String, DocumentField> fields = fetchFields(mapperService, source, "*");
assertThat(fields.size(), equalTo(1));

DocumentField field = fields.get("object.field");
assertThat(field.getValues().size(), equalTo(2));
assertThat(field.getValues(), containsInAnyOrder("value", "value2"));
}

public void testNonExistentField() throws IOException {
Expand Down

0 comments on commit c1aa708

Please sign in to comment.