Filtering maps had false hit is a field was a prefix (but not a match) of an include. Also, exact matching a key whose value is an object resulted in an empty value.
Closes #3288
This commit is contained in:
parent
4c8f3de34b
commit
018ca58cdb
|
@ -141,6 +141,10 @@ public class XContentMapValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void filter(Map<String, Object> map, Map<String, Object> into, String[] includes, String[] excludes, StringBuilder sb) {
|
private static void filter(Map<String, Object> map, Map<String, Object> into, String[] includes, String[] excludes, StringBuilder sb) {
|
||||||
|
if (includes.length == 0 && excludes.length == 0) {
|
||||||
|
into.putAll(map);
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
int mark = sb.length();
|
int mark = sb.length();
|
||||||
|
@ -160,12 +164,24 @@ public class XContentMapValues {
|
||||||
sb.setLength(mark);
|
sb.setLength(mark);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
boolean exactIncludeMatch = false;
|
||||||
if (includes.length > 0) {
|
if (includes.length > 0) {
|
||||||
boolean atLeastOnOneIncludeMatched = false;
|
boolean atLeastOnOneIncludeMatched = false;
|
||||||
for (String include : includes) {
|
for (String include : includes) {
|
||||||
// check for prefix as well, something like: obj1.arr1.*
|
// check for prefix as well to see if we need to zero in, something like: obj1.arr1.*
|
||||||
// note, this does not work well with middle matches, like obj1.*.obj3
|
// note, this does not work well with middle matches, like obj1.*.obj3
|
||||||
if (include.startsWith(path) || Regex.simpleMatch(include, path)) {
|
if (include.startsWith(path)) {
|
||||||
|
if (include.length() == path.length()) {
|
||||||
|
atLeastOnOneIncludeMatched = true;
|
||||||
|
exactIncludeMatch = true;
|
||||||
|
break;
|
||||||
|
} else if (include.length() > path.length() && include.charAt(path.length()) == '.') {
|
||||||
|
// include might may match deeper paths. Dive deeper.
|
||||||
|
atLeastOnOneIncludeMatched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Regex.simpleMatch(include, path)) {
|
||||||
atLeastOnOneIncludeMatched = true;
|
atLeastOnOneIncludeMatched = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -179,14 +195,16 @@ public class XContentMapValues {
|
||||||
|
|
||||||
if (entry.getValue() instanceof Map) {
|
if (entry.getValue() instanceof Map) {
|
||||||
Map<String, Object> innerInto = Maps.newHashMap();
|
Map<String, Object> innerInto = Maps.newHashMap();
|
||||||
filter((Map<String, Object>) entry.getValue(), innerInto, includes, excludes, sb);
|
// if we had an exact match, we want give deeper excludes their chance
|
||||||
|
filter((Map<String, Object>) entry.getValue(), innerInto, exactIncludeMatch ? Strings.EMPTY_ARRAY : includes, excludes, sb);
|
||||||
if (!innerInto.isEmpty()) {
|
if (!innerInto.isEmpty()) {
|
||||||
into.put(entry.getKey(), innerInto);
|
into.put(entry.getKey(), innerInto);
|
||||||
}
|
}
|
||||||
} else if (entry.getValue() instanceof List) {
|
} else if (entry.getValue() instanceof List) {
|
||||||
List<Object> list = (List<Object>) entry.getValue();
|
List<Object> list = (List<Object>) entry.getValue();
|
||||||
List<Object> innerInto = new ArrayList<Object>(list.size());
|
List<Object> innerInto = new ArrayList<Object>(list.size());
|
||||||
filter(list, innerInto, includes, excludes, sb);
|
// if we had an exact match, we want give deeper excludes their chance
|
||||||
|
filter(list, innerInto, exactIncludeMatch ? Strings.EMPTY_ARRAY : includes, excludes, sb);
|
||||||
into.put(entry.getKey(), innerInto);
|
into.put(entry.getKey(), innerInto);
|
||||||
} else {
|
} else {
|
||||||
into.put(entry.getKey(), entry.getValue());
|
into.put(entry.getKey(), entry.getValue());
|
||||||
|
@ -196,6 +214,11 @@ public class XContentMapValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void filter(List<Object> from, List<Object> to, String[] includes, String[] excludes, StringBuilder sb) {
|
private static void filter(List<Object> from, List<Object> to, String[] includes, String[] excludes, StringBuilder sb) {
|
||||||
|
if (includes.length == 0 && excludes.length == 0) {
|
||||||
|
to.addAll(from);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (Object o : from) {
|
for (Object o : from) {
|
||||||
if (o instanceof Map) {
|
if (o instanceof Map) {
|
||||||
Map<String, Object> innerInto = Maps.newHashMap();
|
Map<String, Object> innerInto = Maps.newHashMap();
|
||||||
|
|
|
@ -28,11 +28,14 @@ import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.hamcrest.core.IsEqual.equalTo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@ -73,7 +76,7 @@ public class XContentMapValuesTests {
|
||||||
|
|
||||||
source = XContentFactory.xContent(XContentType.JSON).createParser(builder.string()).mapAndClose();
|
source = XContentFactory.xContent(XContentType.JSON).createParser(builder.string()).mapAndClose();
|
||||||
filter = XContentMapValues.filter(source, new String[]{"path1"}, Strings.EMPTY_ARRAY);
|
filter = XContentMapValues.filter(source, new String[]{"path1"}, Strings.EMPTY_ARRAY);
|
||||||
assertThat(filter.size(), equalTo(0));
|
assertThat(filter.size(), equalTo(1));
|
||||||
|
|
||||||
filter = XContentMapValues.filter(source, new String[]{"path1*"}, Strings.EMPTY_ARRAY);
|
filter = XContentMapValues.filter(source, new String[]{"path1*"}, Strings.EMPTY_ARRAY);
|
||||||
assertThat(filter.get("path1"), equalTo(source.get("path1")));
|
assertThat(filter.get("path1"), equalTo(source.get("path1")));
|
||||||
|
@ -202,13 +205,125 @@ public class XContentMapValuesTests {
|
||||||
public void testThatFilteringWithNestedArrayAndExclusionWorks() throws Exception {
|
public void testThatFilteringWithNestedArrayAndExclusionWorks() throws Exception {
|
||||||
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
|
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
|
||||||
.startArray("coordinates")
|
.startArray("coordinates")
|
||||||
.startArray().value("foo").endArray()
|
.startArray().value("foo").endArray()
|
||||||
.endArray()
|
.endArray()
|
||||||
.endObject();
|
.endObject();
|
||||||
|
|
||||||
Tuple<XContentType, Map<String, Object>> mapTuple = XContentHelper.convertToMap(builder.bytes(), true);
|
Tuple<XContentType, Map<String, Object>> mapTuple = XContentHelper.convertToMap(builder.bytes(), true);
|
||||||
Map<String, Object> filteredSource = XContentMapValues.filter(mapTuple.v2(), Strings.EMPTY_ARRAY, new String[]{"nonExistingField"});
|
Map<String, Object> filteredSource = XContentMapValues.filter(mapTuple.v2(), Strings.EMPTY_ARRAY, new String[]{"nonExistingField"});
|
||||||
|
|
||||||
assertThat(mapTuple.v2(), equalTo(filteredSource));
|
assertThat(mapTuple.v2(), equalTo(filteredSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prefixedNamesFilteringTest() {
|
||||||
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
|
map.put("obj", "value");
|
||||||
|
map.put("obj_name", "value_name");
|
||||||
|
Map<String, Object> filterdMap = XContentMapValues.filter(map, new String[]{"obj_name"}, Strings.EMPTY_ARRAY);
|
||||||
|
assertThat(filterdMap.size(), equalTo(1));
|
||||||
|
assertThat((String) filterdMap.get("obj_name"), equalTo("value_name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void nestedFilteringTest() {
|
||||||
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
|
map.put("field", "value");
|
||||||
|
map.put("array",
|
||||||
|
Arrays.asList(
|
||||||
|
1,
|
||||||
|
new HashMap<String, Object>() {{
|
||||||
|
put("nested", 2);
|
||||||
|
put("nested_2", 3);
|
||||||
|
}}));
|
||||||
|
Map<String, Object> falteredMap = XContentMapValues.filter(map, new String[]{"array.nested"}, Strings.EMPTY_ARRAY);
|
||||||
|
assertThat(falteredMap.size(), equalTo(1));
|
||||||
|
|
||||||
|
// Selecting members of objects within arrays (ex. [ 1, { nested: "value"} ]) always returns all values in the array (1 in the ex)
|
||||||
|
// this is expected behavior as this types of objects are not supported in ES
|
||||||
|
assertThat((Integer) ((List) falteredMap.get("array")).get(0), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) ((List) falteredMap.get("array")).get(1)).size(), equalTo(1));
|
||||||
|
assertThat((Integer) ((Map<String, Object>) ((List) falteredMap.get("array")).get(1)).get("nested"), equalTo(2));
|
||||||
|
|
||||||
|
falteredMap = XContentMapValues.filter(map, new String[]{"array.*"}, Strings.EMPTY_ARRAY);
|
||||||
|
assertThat(falteredMap.size(), equalTo(1));
|
||||||
|
assertThat((Integer) ((List) falteredMap.get("array")).get(0), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) ((List) falteredMap.get("array")).get(1)).size(), equalTo(2));
|
||||||
|
|
||||||
|
map.clear();
|
||||||
|
map.put("field", "value");
|
||||||
|
map.put("obj",
|
||||||
|
new HashMap<String, Object>() {{
|
||||||
|
put("field", "value");
|
||||||
|
put("field2", "value2");
|
||||||
|
}});
|
||||||
|
falteredMap = XContentMapValues.filter(map, new String[]{"obj.field"}, Strings.EMPTY_ARRAY);
|
||||||
|
assertThat(falteredMap.size(), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) falteredMap.get("obj")).size(), equalTo(1));
|
||||||
|
assertThat((String) ((Map<String, Object>) falteredMap.get("obj")).get("field"), equalTo("value"));
|
||||||
|
|
||||||
|
falteredMap = XContentMapValues.filter(map, new String[]{"obj.*"}, Strings.EMPTY_ARRAY);
|
||||||
|
assertThat(falteredMap.size(), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) falteredMap.get("obj")).size(), equalTo(2));
|
||||||
|
assertThat((String) ((Map<String, Object>) falteredMap.get("obj")).get("field"), equalTo("value"));
|
||||||
|
assertThat((String) ((Map<String, Object>) falteredMap.get("obj")).get("field2"), equalTo("value2"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Test
|
||||||
|
public void completeObjectFilteringTest() {
|
||||||
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
|
map.put("field", "value");
|
||||||
|
map.put("obj",
|
||||||
|
new HashMap<String, Object>() {{
|
||||||
|
put("field", "value");
|
||||||
|
put("field2", "value2");
|
||||||
|
}});
|
||||||
|
map.put("array",
|
||||||
|
Arrays.asList(
|
||||||
|
1,
|
||||||
|
new HashMap<String, Object>() {{
|
||||||
|
put("field", "value");
|
||||||
|
put("field2", "value2");
|
||||||
|
}}));
|
||||||
|
|
||||||
|
Map<String, Object> filteredMap = XContentMapValues.filter(map, new String[]{"obj"}, Strings.EMPTY_ARRAY);
|
||||||
|
assertThat(filteredMap.size(), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) filteredMap.get("obj")).size(), equalTo(2));
|
||||||
|
assertThat(((Map<String, Object>) filteredMap.get("obj")).get("field").toString(), equalTo("value"));
|
||||||
|
assertThat(((Map<String, Object>) filteredMap.get("obj")).get("field2").toString(), equalTo("value2"));
|
||||||
|
|
||||||
|
|
||||||
|
filteredMap = XContentMapValues.filter(map, new String[]{"obj"}, new String[]{"*.field2"});
|
||||||
|
assertThat(filteredMap.size(), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) filteredMap.get("obj")).size(), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) filteredMap.get("obj")).get("field").toString(), equalTo("value"));
|
||||||
|
|
||||||
|
|
||||||
|
filteredMap = XContentMapValues.filter(map, new String[]{"array"}, new String[]{});
|
||||||
|
assertThat(filteredMap.size(), equalTo(1));
|
||||||
|
assertThat(((List) filteredMap.get("array")).size(), equalTo(2));
|
||||||
|
assertThat((Integer) ((List) filteredMap.get("array")).get(0), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) ((List) filteredMap.get("array")).get(1)).size(), equalTo(2));
|
||||||
|
|
||||||
|
filteredMap = XContentMapValues.filter(map, new String[]{"array"}, new String[]{"*.field2"});
|
||||||
|
assertThat(filteredMap.size(), equalTo(1));
|
||||||
|
assertThat(((List) filteredMap.get("array")).size(), equalTo(2));
|
||||||
|
assertThat((Integer) ((List) filteredMap.get("array")).get(0), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) ((List) filteredMap.get("array")).get(1)).size(), equalTo(1));
|
||||||
|
assertThat(((Map<String, Object>) ((List) filteredMap.get("array")).get(1)).get("field").toString(), equalTo("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterWithEmptyIncludesExcludes() {
|
||||||
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
|
map.put("field", "value");
|
||||||
|
Map<String, Object> filteredMap = XContentMapValues.filter(map, Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY);
|
||||||
|
assertThat(filteredMap.size(), equalTo(1));
|
||||||
|
assertThat(filteredMap.get("field").toString(), equalTo("value"));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue