Skip to content

Commit a255880

Browse files
authored
Add nested and object fields to field capabilities response (elastic#33803)
This commit adds nested and object fields to the field capabilities response. Closes elastic#33237
1 parent 96b3417 commit a255880

File tree

3 files changed

+115
-12
lines changed

3 files changed

+115
-12
lines changed

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

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ setup:
2424
nested2:
2525
type: float
2626
doc_values: false
27+
level1:
28+
type: nested
29+
properties:
30+
level2:
31+
type: object
32+
properties:
33+
leaf1:
34+
type: text
35+
index: false
36+
2737
- do:
2838
indices.create:
2939
index: test2
@@ -48,6 +58,15 @@ setup:
4858
nested2:
4959
type: float
5060
doc_values: true
61+
level1:
62+
type: nested
63+
properties:
64+
level2:
65+
type: object
66+
properties:
67+
leaf1:
68+
type: text
69+
index: false
5170
- do:
5271
indices.create:
5372
index: test3
@@ -64,14 +83,23 @@ setup:
6483
geo:
6584
type: keyword
6685
object:
67-
type: object
86+
type: nested
6887
properties:
6988
nested1 :
7089
type : long
7190
index: false
7291
nested2:
7392
type: keyword
7493
doc_values: false
94+
level1:
95+
type: object
96+
properties:
97+
level2:
98+
type: object
99+
properties:
100+
leaf1:
101+
type: text
102+
index: false
75103

76104
---
77105
"Get simple field caps":
@@ -112,7 +140,7 @@ setup:
112140
- is_false: fields.geo.keyword.non_searchable_indices
113141
- is_false: fields.geo.keyword.on_aggregatable_indices
114142
---
115-
"Get nested field caps":
143+
"Get leaves field caps":
116144

117145
- do:
118146
field_caps:
@@ -140,6 +168,47 @@ setup:
140168
- is_false: fields.object\.nested2.keyword.non_aggregatable_indices
141169
- is_false: fields.object\.nested2.keyword.non_searchable_indices
142170
---
171+
"Get object and nested field caps":
172+
- skip:
173+
version: " - 6.99.99"
174+
reason: object and nested fields are returned since 7.0
175+
176+
- do:
177+
field_caps:
178+
index: 'test1,test2,test3'
179+
fields: object*,level1*
180+
181+
- match: {fields.object.object.indices: ["test1", "test2"]}
182+
- match: {fields.object.object.searchable: false}
183+
- match: {fields.object.object.aggregatable: false}
184+
- is_false: fields.object.object.non_aggregatable_indices
185+
- is_false: fields.object.object.non_searchable_indices
186+
- match: {fields.object.nested.indices: ["test3"]}
187+
- match: {fields.object.nested.searchable: false}
188+
- match: {fields.object.nested.aggregatable: false}
189+
- is_false: fields.object.nested.non_aggregatable_indices
190+
- is_false: fields.object.nested.non_searchable_indices
191+
- match: {fields.level1.nested.indices: ["test1", "test2"]}
192+
- match: {fields.level1.nested.searchable: false}
193+
- match: {fields.level1.nested.aggregatable: false}
194+
- is_false: fields.level1.nested.non_aggregatable_indices
195+
- is_false: fields.level1.nested.non_searchable_indices
196+
- match: {fields.level1.object.indices: ["test3"]}
197+
- match: {fields.level1.object.searchable: false}
198+
- match: {fields.level1.object.aggregatable: false}
199+
- is_false: fields.level1.object.non_aggregatable_indices
200+
- is_false: fields.level1.object.non_searchable_indices
201+
- match: {fields.level1\.level2.object.searchable: false}
202+
- match: {fields.level1\.level2.object.aggregatable: false}
203+
- is_false: fields.level1\.level2.object.indices
204+
- is_false: fields.level1\.level2.object.non_aggregatable_indices
205+
- is_false: fields.level1\.level2.object.non_searchable_indices
206+
- match: {fields.level1\.level2\.leaf1.text.searchable: false}
207+
- match: {fields.level1\.level2\.leaf1.text.aggregatable: false}
208+
- is_false: fields.level1\.level2\.leaf1.text.indices
209+
- is_false: fields.level1\.level2\.leaf1.text.non_aggregatable_indices
210+
- is_false: fields.level1\.level2\.leaf1.text..non_searchable_indices
211+
---
143212
"Get prefix field caps":
144213

145214
- do:

server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.elasticsearch.common.settings.Settings;
3232
import org.elasticsearch.index.mapper.MappedFieldType;
3333
import org.elasticsearch.index.mapper.MapperService;
34+
import org.elasticsearch.index.mapper.ObjectMapper;
3435
import org.elasticsearch.index.shard.ShardId;
3536
import org.elasticsearch.indices.IndicesService;
3637
import org.elasticsearch.threadpool.ThreadPool;
@@ -86,6 +87,26 @@ protected FieldCapabilitiesIndexResponse shardOperation(final FieldCapabilitiesI
8687
if (indicesService.isMetaDataField(field) || fieldPredicate.test(ft.name())) {
8788
FieldCapabilities fieldCap = new FieldCapabilities(field, ft.typeName(), ft.isSearchable(), ft.isAggregatable());
8889
responseMap.put(field, fieldCap);
90+
} else {
91+
continue;
92+
}
93+
// add nested and object fields
94+
int dotIndex = ft.name().lastIndexOf('.');
95+
while (dotIndex > -1) {
96+
String parentField = ft.name().substring(0, dotIndex);
97+
if (responseMap.containsKey(parentField)) {
98+
// we added this path on another field already
99+
break;
100+
}
101+
// checks if the parent field contains sub-fields
102+
if (mapperService.fullName(parentField) == null) {
103+
// no field type, it must be an object field
104+
ObjectMapper mapper = mapperService.getObjectMapper(parentField);
105+
String type = mapper.nested().isNested() ? "nested" : "object";
106+
FieldCapabilities fieldCap = new FieldCapabilities(parentField, type, false, false);
107+
responseMap.put(parentField, fieldCap);
108+
}
109+
dotIndex = parentField.lastIndexOf('.');
89110
}
90111
}
91112
}

server/src/test/java/org/elasticsearch/index/mapper/FieldFilterMapperPluginTests.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@
3535
import org.elasticsearch.test.ESSingleNodeTestCase;
3636
import org.junit.Before;
3737

38+
import java.util.ArrayList;
39+
import java.util.Arrays;
3840
import java.util.Collection;
3941
import java.util.Collections;
4042
import java.util.HashMap;
43+
import java.util.List;
4144
import java.util.Map;
4245
import java.util.Set;
4346
import java.util.function.Function;
@@ -90,20 +93,26 @@ public void testGetFieldMappings() {
9093
}
9194

9295
public void testFieldCapabilities() {
96+
List<String> allFields = new ArrayList<>(ALL_FLAT_FIELDS);
97+
allFields.addAll(ALL_OBJECT_FIELDS);
9398
FieldCapabilitiesResponse index1 = client().fieldCaps(new FieldCapabilitiesRequest().fields("*").indices("index1")).actionGet();
94-
assertFieldCaps(index1, ALL_FLAT_FIELDS);
99+
assertFieldCaps(index1, allFields);
95100
FieldCapabilitiesResponse filtered = client().fieldCaps(new FieldCapabilitiesRequest().fields("*").indices("filtered")).actionGet();
96-
assertFieldCaps(filtered, FILTERED_FLAT_FIELDS);
101+
List<String> filteredFields = new ArrayList<>(FILTERED_FLAT_FIELDS);
102+
filteredFields.addAll(ALL_OBJECT_FIELDS);
103+
assertFieldCaps(filtered, filteredFields);
97104
//double check that submitting the filtered mappings to an unfiltered index leads to the same field_caps output
98105
//as the one coming from a filtered index with same mappings
99106
GetMappingsResponse getMappingsResponse = client().admin().indices().prepareGetMappings("filtered").get();
100107
ImmutableOpenMap<String, MappingMetaData> filteredMapping = getMappingsResponse.getMappings().get("filtered");
101108
assertAcked(client().admin().indices().prepareCreate("test").addMapping("_doc", filteredMapping.get("_doc").getSourceAsMap()));
102109
FieldCapabilitiesResponse test = client().fieldCaps(new FieldCapabilitiesRequest().fields("*").indices("test")).actionGet();
103-
assertFieldCaps(test, FILTERED_FLAT_FIELDS);
110+
// properties.value is an object field in the new mapping
111+
filteredFields.add("properties.value");
112+
assertFieldCaps(test, filteredFields);
104113
}
105114

106-
private static void assertFieldCaps(FieldCapabilitiesResponse fieldCapabilitiesResponse, String[] expectedFields) {
115+
private static void assertFieldCaps(FieldCapabilitiesResponse fieldCapabilitiesResponse, Collection<String> expectedFields) {
107116
Map<String, Map<String, FieldCapabilities>> responseMap = fieldCapabilitiesResponse.get();
108117
Set<String> builtInMetaDataFields = IndicesModule.getBuiltInMetaDataFields();
109118
for (String field : builtInMetaDataFields) {
@@ -118,7 +127,7 @@ private static void assertFieldCaps(FieldCapabilitiesResponse fieldCapabilitiesR
118127
}
119128

120129
private static void assertFieldMappings(Map<String, Map<String, GetFieldMappingsResponse.FieldMappingMetaData>> mappings,
121-
String[] expectedFields) {
130+
Collection<String> expectedFields) {
122131
assertEquals(1, mappings.size());
123132
Map<String, GetFieldMappingsResponse.FieldMappingMetaData> fields = new HashMap<>(mappings.get("_doc"));
124133
Set<String> builtInMetaDataFields = IndicesModule.getBuiltInMetaDataFields();
@@ -245,14 +254,18 @@ public Function<String, Predicate<String>> getFieldFilter() {
245254
}
246255
}
247256

248-
private static final String[] ALL_FLAT_FIELDS = new String[]{
257+
private static final Collection<String> ALL_FLAT_FIELDS = Arrays.asList(
249258
"name.first", "name.last_visible", "birth", "age_visible", "address.street", "address.location", "address.area_visible",
250259
"properties.key_visible", "properties.key_visible.keyword", "properties.value", "properties.value.keyword_visible"
251-
};
260+
);
252261

253-
private static final String[] FILTERED_FLAT_FIELDS = new String[]{
254-
"name.last_visible", "age_visible", "address.area_visible", "properties.key_visible", "properties.value.keyword_visible"
255-
};
262+
private static final Collection<String> ALL_OBJECT_FIELDS = Arrays.asList(
263+
"name", "address", "properties"
264+
);
265+
266+
private static final Collection<String> FILTERED_FLAT_FIELDS = Arrays.asList(
267+
"name.last_visible", "age_visible", "address.area_visible", "properties.key_visible", "properties.value.keyword_visible"
268+
);
256269

257270
private static final String TEST_ITEM = "{\n" +
258271
" \"_doc\": {\n" +

0 commit comments

Comments
 (0)