diff --git a/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java b/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java index 27b2c1ba67e4a..ab316e9f31a0d 100644 --- a/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java +++ b/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java @@ -154,24 +154,18 @@ private static void filter(Map map, Map into, St } sb.append(key); String path = sb.toString(); - boolean excluded = false; - for (String exclude : excludes) { - if (Regex.simpleMatch(exclude, path)) { - excluded = true; - break; - } - } - if (excluded) { + + if (Regex.simpleMatch(excludes, path)) { sb.setLength(mark); continue; } - boolean exactIncludeMatch; + + boolean exactIncludeMatch = false; // true if the current position was specifically mentioned + boolean pathIsPrefixOfAnInclude = false; // true if potentially a sub scope can be included if (includes.length == 0) { // implied match anything exactIncludeMatch = true; } else { - exactIncludeMatch = false; - boolean pathIsPrefixOfAnInclude = false; for (String include : includes) { // check for prefix matches as well to see if we need to zero in, something like: obj1.arr1.* or *.field // note, this does not work well with middle matches, like obj1.*.obj3 @@ -198,11 +192,12 @@ private static void filter(Map map, Map into, St break; } } - if (!pathIsPrefixOfAnInclude && !exactIncludeMatch) { - // skip subkeys, not interesting. - sb.setLength(mark); - continue; - } + } + + if (!(pathIsPrefixOfAnInclude || exactIncludeMatch)) { + // skip subkeys, not interesting. + sb.setLength(mark); + continue; } @@ -210,7 +205,7 @@ private static void filter(Map map, Map into, St Map innerInto = Maps.newHashMap(); // if we had an exact match, we want give deeper excludes their chance filter((Map) entry.getValue(), innerInto, exactIncludeMatch ? Strings.EMPTY_ARRAY : includes, excludes, sb); - if (!innerInto.isEmpty()) { + if (exactIncludeMatch || !innerInto.isEmpty()) { into.put(entry.getKey(), innerInto); } } else if (entry.getValue() instanceof List) { diff --git a/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java b/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java index eff2bdd450678..e2fba5ac17062 100644 --- a/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java +++ b/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.test.ElasticsearchTestCase; +import org.hamcrest.Matchers; import org.junit.Test; import java.util.Arrays; @@ -33,7 +34,6 @@ import java.util.List; import java.util.Map; -import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.IsEqual.equalTo; @@ -46,6 +46,7 @@ public void testFilter() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder().startObject() .field("test1", "value1") .field("test2", "value2") + .field("something_else", "value3") .endObject(); Map source = XContentFactory.xContent(XContentType.JSON).createParser(builder.string()).mapAndClose(); @@ -59,8 +60,9 @@ public void testFilter() throws Exception { assertThat(filter.get("test2").toString(), equalTo("value2")); filter = XContentMapValues.filter(source, Strings.EMPTY_ARRAY, new String[]{"test1"}); - assertThat(filter.size(), equalTo(1)); + assertThat(filter.size(), equalTo(2)); assertThat(filter.get("test2").toString(), equalTo("value2")); + assertThat(filter.get("something_else").toString(), equalTo("value3")); // more complex object... builder = XContentFactory.jsonBuilder().startObject() @@ -200,20 +202,6 @@ public void testExtractRawValue() throws Exception { assertThat(XContentMapValues.extractRawValues("path1.xxx.path2.yyy.test", map).get(0).toString(), equalTo("value")); } - @Test - public void testThatFilteringWithNestedArrayAndExclusionWorks() throws Exception { - XContentBuilder builder = XContentFactory.jsonBuilder().startObject() - .startArray("coordinates") - .startArray().value("foo").endArray() - .endArray() - .endObject(); - - Tuple> mapTuple = XContentHelper.convertToMap(builder.bytes(), true); - Map filteredSource = XContentMapValues.filter(mapTuple.v2(), Strings.EMPTY_ARRAY, new String[]{"nonExistingField"}); - - assertThat(mapTuple.v2(), equalTo(filteredSource)); - } - @Test public void prefixedNamesFilteringTest() { Map map = new HashMap(); @@ -368,4 +356,101 @@ public void filterWithEmptyIncludesExcludes() { assertThat(filteredMap.get("field").toString(), equalTo("value")); } + + @SuppressWarnings({"unchecked"}) + @Test + public void testThatFilterIncludesEmptyObjectWhenUsingIncludes() throws Exception { + XContentBuilder builder = XContentFactory.jsonBuilder().startObject() + .startObject("obj") + .endObject() + .endObject(); + + Tuple> mapTuple = XContentHelper.convertToMap(builder.bytes(), true); + Map filteredSource = XContentMapValues.filter(mapTuple.v2(), new String[]{"obj"}, Strings.EMPTY_ARRAY); + + assertThat(mapTuple.v2(), equalTo(filteredSource)); + } + + @Test + public void testThatFilterIncludesEmptyObjectWhenUsingExcludes() throws Exception { + XContentBuilder builder = XContentFactory.jsonBuilder().startObject() + .startObject("obj") + .endObject() + .endObject(); + + Tuple> mapTuple = XContentHelper.convertToMap(builder.bytes(), true); + Map filteredSource = XContentMapValues.filter(mapTuple.v2(), Strings.EMPTY_ARRAY, new String[]{"nonExistingField"}); + + assertThat(mapTuple.v2(), equalTo(filteredSource)); + } + + @Test + public void testNotOmittingObjectsWithExcludedProperties() throws Exception { + XContentBuilder builder = XContentFactory.jsonBuilder().startObject() + .startObject("obj") + .field("f1", "v1") + .endObject() + .endObject(); + + Tuple> mapTuple = XContentHelper.convertToMap(builder.bytes(), true); + Map filteredSource = XContentMapValues.filter(mapTuple.v2(), Strings.EMPTY_ARRAY, new String[]{"obj.f1"}); + + assertThat(filteredSource.size(), equalTo(1)); + assertThat(filteredSource, hasKey("obj")); + assertThat(((Map) filteredSource.get("obj")).size(), equalTo(0)); + } + + @SuppressWarnings({"unchecked"}) + @Test + public void testNotOmittingObjectWithNestedExcludedObject() throws Exception { + XContentBuilder builder = XContentFactory.jsonBuilder().startObject() + .startObject("obj1") + .startObject("obj2") + .startObject("obj3") + .endObject() + .endObject() + .endObject() + .endObject(); + + // implicit include + Tuple> mapTuple = XContentHelper.convertToMap(builder.bytes(), true); + Map filteredSource = XContentMapValues.filter(mapTuple.v2(), Strings.EMPTY_ARRAY, new String[]{"*.obj2"}); + + assertThat(filteredSource.size(), equalTo(1)); + assertThat(filteredSource, hasKey("obj1")); + assertThat(((Map) filteredSource.get("obj1")).size(), Matchers.equalTo(0)); + + // explicit include + filteredSource = XContentMapValues.filter(mapTuple.v2(), new String[]{"obj1"}, new String[]{"*.obj2"}); + assertThat(filteredSource.size(), equalTo(1)); + assertThat(filteredSource, hasKey("obj1")); + assertThat(((Map) filteredSource.get("obj1")).size(), Matchers.equalTo(0)); + + // wild card include + filteredSource = XContentMapValues.filter(mapTuple.v2(), new String[]{"*.obj2"}, new String[]{"*.obj3"}); + assertThat(filteredSource.size(), equalTo(1)); + assertThat(filteredSource, hasKey("obj1")); + assertThat(((Map) filteredSource.get("obj1")), hasKey("obj2")); + assertThat(((Map) ((Map) filteredSource.get("obj1")).get("obj2")).size(), Matchers.equalTo(0)); + } + + @SuppressWarnings({"unchecked"}) + @Test + public void testIncludingObjectWithNestedIncludedObject() throws Exception { + XContentBuilder builder = XContentFactory.jsonBuilder().startObject() + .startObject("obj1") + .startObject("obj2") + .endObject() + .endObject() + .endObject(); + + Tuple> mapTuple = XContentHelper.convertToMap(builder.bytes(), true); + Map filteredSource = XContentMapValues.filter(mapTuple.v2(), new String[]{"*.obj2"}, Strings.EMPTY_ARRAY); + + assertThat(filteredSource.size(), equalTo(1)); + assertThat(filteredSource, hasKey("obj1")); + assertThat(((Map) filteredSource.get("obj1")).size(), equalTo(1)); + assertThat(((Map) filteredSource.get("obj1")), hasKey("obj2")); + assertThat(((Map) ((Map) filteredSource.get("obj1")).get("obj2")).size(), equalTo(0)); + } } diff --git a/src/test/java/org/elasticsearch/search/fields/SearchFieldsTests.java b/src/test/java/org/elasticsearch/search/fields/SearchFieldsTests.java index 4abed6dbb7289..314c235cf38ed 100644 --- a/src/test/java/org/elasticsearch/search/fields/SearchFieldsTests.java +++ b/src/test/java/org/elasticsearch/search/fields/SearchFieldsTests.java @@ -253,7 +253,7 @@ public void testPartialFields() throws Exception { SearchResponse response = client().prepareSearch("test") .addPartialField("partial1", "obj1.arr1.*", null) - .addPartialField("partial2", null, "obj1.*") + .addPartialField("partial2", null, "obj1") .execute().actionGet(); assertThat("Failures " + Arrays.toString(response.getShardFailures()), response.getShardFailures().length, equalTo(0));