|
16 | 16 |
|
17 | 17 | import java.io.IOException; |
18 | 18 | import java.time.ZoneId; |
| 19 | +import java.util.ArrayList; |
| 20 | +import java.util.Iterator; |
19 | 21 | import java.util.List; |
20 | 22 | import java.util.Map; |
21 | 23 | import java.util.Objects; |
@@ -81,13 +83,66 @@ public Object extract(SearchHit hit) { |
81 | 83 | Object value = null; |
82 | 84 | DocumentField field = null; |
83 | 85 | if (hitName != null) { |
84 | | - // a nested field value is grouped under the nested parent name (ie dep.dep_name lives under "dep":[{dep_name:value}]) |
85 | | - field = hit.field(hitName); |
| 86 | + value = unwrapFieldsMultiValue(extractNestedField(hit)); |
86 | 87 | } else { |
87 | 88 | field = hit.field(fieldName); |
| 89 | + if (field != null) { |
| 90 | + value = unwrapFieldsMultiValue(field.getValues()); |
| 91 | + } |
| 92 | + } |
| 93 | + return value; |
| 94 | + } |
| 95 | + |
| 96 | + /* |
| 97 | + * For a path of fields like root.nested1.nested2.leaf where nested1 and nested2 are nested field types, |
| 98 | + * fieldName is root.nested1.nested2.leaf, while hitName is root.nested1.nested2 |
| 99 | + * We first look for root.nested1.nested2 or root.nested1 or root in the SearchHit until we find something. |
| 100 | + * If the DocumentField lives under "root.nested1" the remaining path to search for (in the DocumentField itself) is nested2. |
| 101 | + * After this step is done, what remains to be done is just getting the leaf values. |
| 102 | + */ |
| 103 | + @SuppressWarnings("unchecked") |
| 104 | + private Object extractNestedField(SearchHit hit) { |
| 105 | + Object value; |
| 106 | + DocumentField field; |
| 107 | + String tempHitname = hitName; |
| 108 | + List<String> remainingPath = new ArrayList<>(); |
| 109 | + // first, search for the "root" DocumentField under which the remaining path of nested document values is |
| 110 | + while ((field = hit.field(tempHitname)) == null) { |
| 111 | + int indexOfDot = tempHitname.lastIndexOf("."); |
| 112 | + if (indexOfDot < 0) {// there is no such field in the hit |
| 113 | + return null; |
| 114 | + } |
| 115 | + remainingPath.add(0, tempHitname.substring(indexOfDot + 1)); |
| 116 | + tempHitname = tempHitname.substring(0, indexOfDot); |
88 | 117 | } |
89 | | - if (field != null) { |
90 | | - value = unwrapFieldsMultiValue(field.getValues()); |
| 118 | + // then dig into DocumentField's structure until we reach the field we are interested into |
| 119 | + if (remainingPath.size() > 0) { |
| 120 | + List<Object> values = field.getValues(); |
| 121 | + Iterator<String> pathIterator = remainingPath.iterator(); |
| 122 | + while (pathIterator.hasNext()) { |
| 123 | + String pathElement = pathIterator.next(); |
| 124 | + Map<String, List<Object>> elements = (Map<String, List<Object>>) values.get(0); |
| 125 | + values = elements.get(pathElement); |
| 126 | + /* |
| 127 | + * if this path is not found it means we hit another nested document (inner_root_1.inner_root_2.nested_field_2) |
| 128 | + * something like this |
| 129 | + * "root_field_1.root_field_2.nested_field_1" : [ |
| 130 | + * { |
| 131 | + * "inner_root_1.inner_root_2.nested_field_2" : [ |
| 132 | + * { |
| 133 | + * "leaf_field" : [ |
| 134 | + * "abc2" |
| 135 | + * ] |
| 136 | + * So, start re-building the path until the right one is found, ie inner_root_1.inner_root_2...... |
| 137 | + */ |
| 138 | + while (values == null) { |
| 139 | + pathElement += "." + pathIterator.next(); |
| 140 | + values = elements.get(pathElement); |
| 141 | + } |
| 142 | + } |
| 143 | + value = ((Map<String, Object>) values.get(0)).get(fieldName.substring(hitName.length() + 1)); |
| 144 | + } else { |
| 145 | + value = field.getValues(); |
91 | 146 | } |
92 | 147 | return value; |
93 | 148 | } |
|
0 commit comments