47
47
import java .util .List ;
48
48
import java .util .Map ;
49
49
import java .util .Objects ;
50
+ import java .util .Optional ;
50
51
import java .util .Set ;
51
52
import java .util .TreeSet ;
52
53
import java .util .stream .Collectors ;
@@ -65,6 +66,7 @@ public class ExtractedFieldsDetector {
65
66
private final int docValueFieldsLimit ;
66
67
private final FieldCapabilitiesResponse fieldCapabilitiesResponse ;
67
68
private final Map <String , Long > cardinalitiesForFieldsWithConstraints ;
69
+ private final List <String > topNestedFieldPrefixes ;
68
70
69
71
ExtractedFieldsDetector (DataFrameAnalyticsConfig config ,
70
72
int docValueFieldsLimit ,
@@ -74,6 +76,26 @@ public class ExtractedFieldsDetector {
74
76
this .docValueFieldsLimit = docValueFieldsLimit ;
75
77
this .fieldCapabilitiesResponse = Objects .requireNonNull (fieldCapabilitiesResponse );
76
78
this .cardinalitiesForFieldsWithConstraints = Objects .requireNonNull (cardinalitiesForFieldsWithConstraints );
79
+ this .topNestedFieldPrefixes = findTopNestedFieldPrefixes (fieldCapabilitiesResponse );
80
+ }
81
+
82
+ private List <String > findTopNestedFieldPrefixes (FieldCapabilitiesResponse fieldCapabilitiesResponse ) {
83
+ List <String > sortedNestedFieldPrefixes = fieldCapabilitiesResponse .get ().keySet ().stream ()
84
+ .filter (field -> isNested (getMappingTypes (field )))
85
+ .map (field -> field + "." )
86
+ .sorted ()
87
+ .collect (Collectors .toList ());
88
+ Iterator <String > iterator = sortedNestedFieldPrefixes .iterator ();
89
+ String previousNestedFieldPrefix = null ;
90
+ while (iterator .hasNext ()) {
91
+ String nestedFieldPrefix = iterator .next ();
92
+ if (previousNestedFieldPrefix != null && nestedFieldPrefix .startsWith (previousNestedFieldPrefix )) {
93
+ iterator .remove ();
94
+ } else {
95
+ previousNestedFieldPrefix = nestedFieldPrefix ;
96
+ }
97
+ }
98
+ return Collections .unmodifiableList (sortedNestedFieldPrefixes );
77
99
}
78
100
79
101
public Tuple <ExtractedFields , List <FieldSelection >> detect () {
@@ -139,7 +161,14 @@ private void validateFieldsRequireForProcessors(Set<String> processorFields) {
139
161
}
140
162
removeObjects (fieldsForProcessor );
141
163
if (fieldsForProcessor .size () < processorFields .size ()) {
142
- throw ExceptionsHelper .badRequestException ("fields for feature_processors must not be objects" );
164
+ throw ExceptionsHelper .badRequestException ("fields for feature_processors must not be objects or nested" );
165
+ }
166
+ for (String field : fieldsForProcessor ) {
167
+ Optional <String > matchingNestedFieldPattern = findMatchingNestedFieldPattern (field );
168
+ if (matchingNestedFieldPattern .isPresent ()) {
169
+ throw ExceptionsHelper .badRequestException ("nested fields [{}] cannot be used in a feature_processor" ,
170
+ matchingNestedFieldPattern .get ());
171
+ }
143
172
}
144
173
Collection <String > errorFields = new ArrayList <>();
145
174
for (String fieldName : fieldsForProcessor ) {
@@ -190,7 +219,7 @@ private void removeObjects(Set<String> fields) {
190
219
while (fieldsIterator .hasNext ()) {
191
220
String field = fieldsIterator .next ();
192
221
Set <String > types = getMappingTypes (field );
193
- if (isObject (types )) {
222
+ if (isObject (types ) || isNested ( types ) ) {
194
223
fieldsIterator .remove ();
195
224
}
196
225
}
@@ -210,6 +239,11 @@ private void addExcludedField(String field, String reason, Set<FieldSelection> f
210
239
fieldSelection .add (FieldSelection .excluded (field , getMappingTypes (field ), reason ));
211
240
}
212
241
242
+ private void addExcludedNestedPattern (String pattern , Set <FieldSelection > fieldSelection ) {
243
+ fieldSelection .add (FieldSelection .excluded (
244
+ pattern , Collections .singleton (ObjectMapper .NESTED_CONTENT_TYPE ), "nested fields are not supported" ));
245
+ }
246
+
213
247
private Set <String > getMappingTypes (String field ) {
214
248
Map <String , FieldCapabilities > fieldCaps = fieldCapabilitiesResponse .getField (field );
215
249
return fieldCaps == null ? Collections .emptySet () : fieldCaps .keySet ();
@@ -223,6 +257,11 @@ private void removeFieldsWithIncompatibleTypes(Set<String> fields, Set<FieldSele
223
257
addExcludedField (field , "unsupported type; supported types are " + getSupportedTypes (), fieldSelection );
224
258
fieldsIterator .remove ();
225
259
}
260
+ Optional <String > matchingNestedFieldPattern = findMatchingNestedFieldPattern (field );
261
+ if (matchingNestedFieldPattern .isPresent ()) {
262
+ addExcludedNestedPattern (matchingNestedFieldPattern .get (), fieldSelection );
263
+ fieldsIterator .remove ();
264
+ }
226
265
}
227
266
}
228
267
@@ -257,6 +296,10 @@ private Set<String> getSupportedTypes() {
257
296
return supportedTypes ;
258
297
}
259
298
299
+ private Optional <String > findMatchingNestedFieldPattern (String field ) {
300
+ return topNestedFieldPrefixes .stream ().filter (prefix -> field .startsWith (prefix )).map (prefix -> prefix + "*" ).findFirst ();
301
+ }
302
+
260
303
private void includeAndExcludeFields (Set <String > fields , Set <FieldSelection > fieldSelection ) {
261
304
FetchSourceContext analyzedFields = config .getAnalyzedFields ();
262
305
if (analyzedFields == null ) {
@@ -294,10 +337,10 @@ private void includeAndExcludeFields(Set<String> fields, Set<FieldSelection> fie
294
337
295
338
private void checkIncludesExcludesAreNotObjects (FetchSourceContext analyzedFields ) {
296
339
List <String > objectFields = Stream .concat (Arrays .stream (analyzedFields .includes ()), Arrays .stream (analyzedFields .excludes ()))
297
- .filter (field -> isObject (getMappingTypes (field )))
340
+ .filter (field -> isObject (getMappingTypes (field )) || isNested ( getMappingTypes ( field )) )
298
341
.collect (Collectors .toList ());
299
342
if (objectFields .isEmpty () == false ) {
300
- throw ExceptionsHelper .badRequestException ("{} must not include or exclude object fields: {}" ,
343
+ throw ExceptionsHelper .badRequestException ("{} must not include or exclude object or nested fields: {}" ,
301
344
DataFrameAnalyticsConfig .ANALYZED_FIELDS .getPreferredName (), objectFields );
302
345
}
303
346
}
@@ -317,10 +360,15 @@ private void applyIncludesExcludes(Set<String> fields, Set<String> includes, Set
317
360
}
318
361
} else {
319
362
fieldsIterator .remove ();
320
- if (hasCompatibleType (field )) {
321
- addExcludedField (field , "field not in includes list" , fieldSelection );
322
- } else {
363
+ if (hasCompatibleType (field ) == false ) {
323
364
addExcludedField (field , "unsupported type; supported types are " + getSupportedTypes (), fieldSelection );
365
+ } else {
366
+ Optional <String > matchingNestedFieldPattern = findMatchingNestedFieldPattern (field );
367
+ if (matchingNestedFieldPattern .isPresent ()) {
368
+ addExcludedNestedPattern (matchingNestedFieldPattern .get (), fieldSelection );
369
+ } else {
370
+ addExcludedField (field , "field not in includes list" , fieldSelection );
371
+ }
324
372
}
325
373
}
326
374
}
@@ -337,6 +385,10 @@ private void checkFieldsHaveCompatibleTypes(Set<String> fields) {
337
385
throw ExceptionsHelper .badRequestException ("field [{}] has unsupported type {}. Supported types are {}." , field ,
338
386
fieldCaps .keySet (), getSupportedTypes ());
339
387
}
388
+ Optional <String > matchingNestedFieldPattern = findMatchingNestedFieldPattern (field );
389
+ if (matchingNestedFieldPattern .isPresent ()) {
390
+ throw ExceptionsHelper .badRequestException ("nested fields [{}] are not supported" , matchingNestedFieldPattern .get ());
391
+ }
340
392
}
341
393
}
342
394
@@ -601,7 +653,11 @@ private static boolean isBoolean(Set<String> types) {
601
653
return types .size () == 1 && types .contains (BooleanFieldMapper .CONTENT_TYPE );
602
654
}
603
655
604
- private boolean isObject (Set <String > types ) {
656
+ private static boolean isObject (Set <String > types ) {
605
657
return types .size () == 1 && types .contains (ObjectMapper .CONTENT_TYPE );
606
658
}
659
+
660
+ private static boolean isNested (Set <String > types ) {
661
+ return types .size () == 1 && types .contains (ObjectMapper .NESTED_CONTENT_TYPE );
662
+ }
607
663
}
0 commit comments