瀏覽代碼

Add nested and object fields to field capabilities response (#33803)

This commit adds nested and object fields to the field capabilities response.

Closes #33237
Jim Ferenczi 7 年之前
父節點
當前提交
a255880497

+ 71 - 2
rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yml

@@ -24,6 +24,16 @@ setup:
                         nested2:
                           type: float
                           doc_values: false
+                    level1:
+                      type: nested
+                      properties:
+                        level2:
+                          type: object
+                          properties:
+                            leaf1:
+                              type: text
+                              index: false
+
   - do:
         indices.create:
           index: test2
@@ -48,6 +58,15 @@ setup:
                         nested2:
                           type: float
                           doc_values: true
+                    level1:
+                      type: nested
+                      properties:
+                        level2:
+                          type: object
+                          properties:
+                            leaf1:
+                              type: text
+                              index: false
   - do:
         indices.create:
           index: test3
@@ -64,7 +83,7 @@ setup:
                     geo:
                       type:     keyword
                     object:
-                      type: object
+                      type: nested
                       properties:
                         nested1 :
                           type : long
@@ -72,6 +91,15 @@ setup:
                         nested2:
                           type: keyword
                           doc_values: false
+                    level1:
+                      type: object
+                      properties:
+                        level2:
+                          type: object
+                          properties:
+                            leaf1:
+                              type: text
+                              index: false
 
 ---
 "Get simple field caps":
@@ -112,7 +140,7 @@ setup:
   - is_false: fields.geo.keyword.non_searchable_indices
   - is_false: fields.geo.keyword.on_aggregatable_indices
 ---
-"Get nested field caps":
+"Get leaves field caps":
 
   - do:
       field_caps:
@@ -140,6 +168,47 @@ setup:
   - is_false: fields.object\.nested2.keyword.non_aggregatable_indices
   - is_false: fields.object\.nested2.keyword.non_searchable_indices
 ---
+"Get object and nested field caps":
+  - skip:
+      version: " - 6.99.99"
+      reason: object and nested fields are returned since 7.0
+
+  - do:
+      field_caps:
+        index: 'test1,test2,test3'
+        fields: object*,level1*
+
+  - match: {fields.object.object.indices:                                 ["test1", "test2"]}
+  - match: {fields.object.object.searchable:                              false}
+  - match: {fields.object.object.aggregatable:                            false}
+  - is_false: fields.object.object.non_aggregatable_indices
+  - is_false: fields.object.object.non_searchable_indices
+  - match: {fields.object.nested.indices:                                 ["test3"]}
+  - match: {fields.object.nested.searchable:                              false}
+  - match: {fields.object.nested.aggregatable:                            false}
+  - is_false: fields.object.nested.non_aggregatable_indices
+  - is_false: fields.object.nested.non_searchable_indices
+  - match: {fields.level1.nested.indices:                                 ["test1", "test2"]}
+  - match: {fields.level1.nested.searchable:                              false}
+  - match: {fields.level1.nested.aggregatable:                            false}
+  - is_false: fields.level1.nested.non_aggregatable_indices
+  - is_false: fields.level1.nested.non_searchable_indices
+  - match: {fields.level1.object.indices:                                 ["test3"]}
+  - match: {fields.level1.object.searchable:                              false}
+  - match: {fields.level1.object.aggregatable:                            false}
+  - is_false: fields.level1.object.non_aggregatable_indices
+  - is_false: fields.level1.object.non_searchable_indices
+  - match: {fields.level1\.level2.object.searchable:                      false}
+  - match: {fields.level1\.level2.object.aggregatable:                    false}
+  - is_false: fields.level1\.level2.object.indices
+  - is_false: fields.level1\.level2.object.non_aggregatable_indices
+  - is_false: fields.level1\.level2.object.non_searchable_indices
+  - match: {fields.level1\.level2\.leaf1.text.searchable:                 false}
+  - match: {fields.level1\.level2\.leaf1.text.aggregatable:               false}
+  - is_false: fields.level1\.level2\.leaf1.text.indices
+  - is_false: fields.level1\.level2\.leaf1.text.non_aggregatable_indices
+  - is_false: fields.level1\.level2\.leaf1.text..non_searchable_indices
+---
 "Get prefix field caps":
 
   - do:

+ 21 - 0
server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java

@@ -31,6 +31,7 @@ import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.mapper.MapperService;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.shard.ShardId;
 import org.elasticsearch.indices.IndicesService;
 import org.elasticsearch.threadpool.ThreadPool;
@@ -86,6 +87,26 @@ public class TransportFieldCapabilitiesIndexAction extends TransportSingleShardA
                 if (indicesService.isMetaDataField(field) || fieldPredicate.test(ft.name())) {
                     FieldCapabilities fieldCap = new FieldCapabilities(field, ft.typeName(), ft.isSearchable(), ft.isAggregatable());
                     responseMap.put(field, fieldCap);
+                } else {
+                    continue;
+                }
+                // add nested and object fields
+                int dotIndex = ft.name().lastIndexOf('.');
+                while (dotIndex > -1) {
+                    String parentField = ft.name().substring(0, dotIndex);
+                    if (responseMap.containsKey(parentField)) {
+                        // we added this path on another field already
+                        break;
+                    }
+                    // checks if the parent field contains sub-fields
+                    if (mapperService.fullName(parentField) == null) {
+                        // no field type, it must be an object field
+                        ObjectMapper mapper = mapperService.getObjectMapper(parentField);
+                        String type = mapper.nested().isNested() ? "nested" : "object";
+                        FieldCapabilities fieldCap = new FieldCapabilities(parentField, type, false, false);
+                        responseMap.put(parentField, fieldCap);
+                    }
+                    dotIndex = parentField.lastIndexOf('.');
                 }
             }
         }

+ 23 - 10
server/src/test/java/org/elasticsearch/index/mapper/FieldFilterMapperPluginTests.java

@@ -35,9 +35,12 @@ import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.test.ESSingleNodeTestCase;
 import org.junit.Before;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
@@ -90,20 +93,26 @@ public class FieldFilterMapperPluginTests extends ESSingleNodeTestCase {
     }
 
     public void testFieldCapabilities() {
+        List<String> allFields = new ArrayList<>(ALL_FLAT_FIELDS);
+        allFields.addAll(ALL_OBJECT_FIELDS);
         FieldCapabilitiesResponse index1 = client().fieldCaps(new FieldCapabilitiesRequest().fields("*").indices("index1")).actionGet();
-        assertFieldCaps(index1, ALL_FLAT_FIELDS);
+        assertFieldCaps(index1, allFields);
         FieldCapabilitiesResponse filtered = client().fieldCaps(new FieldCapabilitiesRequest().fields("*").indices("filtered")).actionGet();
-        assertFieldCaps(filtered, FILTERED_FLAT_FIELDS);
+        List<String> filteredFields = new ArrayList<>(FILTERED_FLAT_FIELDS);
+        filteredFields.addAll(ALL_OBJECT_FIELDS);
+        assertFieldCaps(filtered, filteredFields);
         //double check that submitting the filtered mappings to an unfiltered index leads to the same field_caps output
         //as the one coming from a filtered index with same mappings
         GetMappingsResponse getMappingsResponse = client().admin().indices().prepareGetMappings("filtered").get();
         ImmutableOpenMap<String, MappingMetaData> filteredMapping = getMappingsResponse.getMappings().get("filtered");
         assertAcked(client().admin().indices().prepareCreate("test").addMapping("_doc", filteredMapping.get("_doc").getSourceAsMap()));
         FieldCapabilitiesResponse test = client().fieldCaps(new FieldCapabilitiesRequest().fields("*").indices("test")).actionGet();
-        assertFieldCaps(test, FILTERED_FLAT_FIELDS);
+        // properties.value is an object field in the new mapping
+        filteredFields.add("properties.value");
+        assertFieldCaps(test, filteredFields);
     }
 
-    private static void assertFieldCaps(FieldCapabilitiesResponse fieldCapabilitiesResponse, String[] expectedFields) {
+    private static void assertFieldCaps(FieldCapabilitiesResponse fieldCapabilitiesResponse, Collection<String> expectedFields) {
         Map<String, Map<String, FieldCapabilities>> responseMap = fieldCapabilitiesResponse.get();
         Set<String> builtInMetaDataFields = IndicesModule.getBuiltInMetaDataFields();
         for (String field : builtInMetaDataFields) {
@@ -118,7 +127,7 @@ public class FieldFilterMapperPluginTests extends ESSingleNodeTestCase {
     }
 
     private static void assertFieldMappings(Map<String, Map<String, GetFieldMappingsResponse.FieldMappingMetaData>> mappings,
-                                            String[] expectedFields) {
+                                            Collection<String> expectedFields) {
         assertEquals(1, mappings.size());
         Map<String, GetFieldMappingsResponse.FieldMappingMetaData> fields = new HashMap<>(mappings.get("_doc"));
         Set<String> builtInMetaDataFields = IndicesModule.getBuiltInMetaDataFields();
@@ -245,14 +254,18 @@ public class FieldFilterMapperPluginTests extends ESSingleNodeTestCase {
         }
     }
 
-    private static final String[] ALL_FLAT_FIELDS = new String[]{
+    private static final Collection<String> ALL_FLAT_FIELDS = Arrays.asList(
         "name.first", "name.last_visible", "birth", "age_visible", "address.street", "address.location", "address.area_visible",
         "properties.key_visible", "properties.key_visible.keyword", "properties.value", "properties.value.keyword_visible"
-    };
+    );
 
-    private static final String[] FILTERED_FLAT_FIELDS = new String[]{
-            "name.last_visible", "age_visible", "address.area_visible", "properties.key_visible", "properties.value.keyword_visible"
-    };
+    private static final Collection<String> ALL_OBJECT_FIELDS = Arrays.asList(
+        "name", "address", "properties"
+    );
+
+    private static final Collection<String> FILTERED_FLAT_FIELDS = Arrays.asList(
+        "name.last_visible", "age_visible", "address.area_visible", "properties.key_visible", "properties.value.keyword_visible"
+    );
 
     private static final String TEST_ITEM = "{\n" +
             "  \"_doc\": {\n" +