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) {
|
||||
if (includes.length == 0 && excludes.length == 0) {
|
||||
into.putAll(map);
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
int mark = sb.length();
|
||||
|
@ -160,12 +164,24 @@ public class XContentMapValues {
|
|||
sb.setLength(mark);
|
||||
continue;
|
||||
}
|
||||
boolean exactIncludeMatch = false;
|
||||
if (includes.length > 0) {
|
||||
boolean atLeastOnOneIncludeMatched = false;
|
||||
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
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
@ -179,14 +195,16 @@ public class XContentMapValues {
|
|||
|
||||
if (entry.getValue() instanceof Map) {
|
||||
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()) {
|
||||
into.put(entry.getKey(), innerInto);
|
||||
}
|
||||
} else if (entry.getValue() instanceof List) {
|
||||
List<Object> list = (List<Object>) entry.getValue();
|
||||
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);
|
||||
} else {
|
||||
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) {
|
||||
if (includes.length == 0 && excludes.length == 0) {
|
||||
to.addAll(from);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Object o : from) {
|
||||
if (o instanceof Map) {
|
||||
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.testng.annotations.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
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;
|
||||
|
||||
/**
|
||||
*/
|
||||
|
@ -73,7 +76,7 @@ public class XContentMapValuesTests {
|
|||
|
||||
source = XContentFactory.xContent(XContentType.JSON).createParser(builder.string()).mapAndClose();
|
||||
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);
|
||||
assertThat(filter.get("path1"), equalTo(source.get("path1")));
|
||||
|
@ -211,4 +214,116 @@ public class XContentMapValuesTests {
|
|||
|
||||
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