Skip to content

Commit 5dcc805

Browse files
authored
Restore a noop _all metadata field for 6x indices (#37808)
This commit restores a noop version of the AllFieldMapper that is instanciated only for indices created in 6x. We need this metadata field mapper to be present in this version in order to allow the upgrade of indices that explicitly disable _all (enabled: false). The mapping of these indices contains a reference to the _all field that we cannot remove in 7 so we'll need to keep this metadata mapper in 7x. Since indices created in 6x will not be compatible with 8, we'll remove this noop mapper in the next major version. Closes #37429
1 parent f51bc00 commit 5dcc805

File tree

14 files changed

+354
-28
lines changed

14 files changed

+354
-28
lines changed

qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,9 @@
6767
field3: value
6868
- match: { hits.total: 1 }
6969
- match: { hits.hits.0._id: q3 }
70+
71+
---
72+
"Index with _all is available":
73+
- do:
74+
indices.get:
75+
index: all-index

qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,21 @@
203203
tasks.get:
204204
wait_for_completion: true
205205
task_id: $task
206+
207+
---
208+
"Create an index with _all explicitly disabled":
209+
- skip:
210+
features: warnings
211+
- do:
212+
warnings:
213+
- "[_all] is deprecated in 6.0+ and will be removed in 7.0. As a replacement, you can use [copy_to] on mapping fields to create your own catch all field."
214+
indices.create:
215+
index: all-index
216+
body:
217+
mappings:
218+
type:
219+
_all:
220+
enabled: false
221+
properties:
222+
field:
223+
type: text

qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,17 @@
125125
wait_for_completion: true
126126
task_id: $task_id
127127
- match: { task.headers.X-Opaque-Id: "Reindexing Again" }
128+
129+
---
130+
"Index with _all is available":
131+
- do:
132+
indices.get:
133+
index: all-index
134+
135+
- do:
136+
indices.get_mapping:
137+
index: all-index
138+
139+
- is_true: all-index.mappings._all
140+
- match: { all-index.mappings._all.enabled: false}
141+

server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsIndexAction.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.action.admin.indices.mapping.get;
2121

2222
import org.elasticsearch.ElasticsearchException;
23+
import org.elasticsearch.Version;
2324
import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse.FieldMappingMetaData;
2425
import org.elasticsearch.action.support.ActionFilters;
2526
import org.elasticsearch.action.support.single.shard.TransportSingleShardAction;
@@ -91,7 +92,8 @@ protected ShardsIterator shards(ClusterState state, InternalRequest request) {
9192
protected GetFieldMappingsResponse shardOperation(final GetFieldMappingsIndexRequest request, ShardId shardId) {
9293
assert shardId != null;
9394
IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
94-
Predicate<String> metadataFieldPredicate = indicesService::isMetaDataField;
95+
Version indexCreatedVersion = indexService.mapperService().getIndexSettings().getIndexVersionCreated();
96+
Predicate<String> metadataFieldPredicate = (f) -> indicesService.isMetaDataField(indexCreatedVersion, f);
9597
Predicate<String> fieldPredicate = metadataFieldPredicate.or(indicesService.getFieldFilter().apply(shardId.getIndexName()));
9698

9799
DocumentMapper mapper = indexService.mapperService().documentMapper();

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ protected FieldCapabilitiesIndexResponse shardOperation(final FieldCapabilitiesI
8383
for (String field : fieldNames) {
8484
MappedFieldType ft = mapperService.fullName(field);
8585
if (ft != null) {
86-
if (indicesService.isMetaDataField(field) || fieldPredicate.test(ft.name())) {
86+
if (indicesService.isMetaDataField(mapperService.getIndexSettings().getIndexVersionCreated(), field)
87+
|| fieldPredicate.test(ft.name())) {
8788
FieldCapabilities fieldCap = new FieldCapabilities(field, ft.typeName(), ft.isSearchable(), ft.isAggregatable());
8889
responseMap.put(field, fieldCap);
8990
} else {
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.index.mapper;
21+
22+
import org.apache.lucene.index.IndexOptions;
23+
import org.apache.lucene.index.IndexableField;
24+
import org.apache.lucene.search.MatchNoDocsQuery;
25+
import org.apache.lucene.search.Query;
26+
import org.elasticsearch.common.settings.Settings;
27+
import org.elasticsearch.common.xcontent.XContentBuilder;
28+
import org.elasticsearch.common.xcontent.support.XContentMapValues;
29+
import org.elasticsearch.index.query.QueryShardContext;
30+
31+
import java.io.IOException;
32+
import java.util.Iterator;
33+
import java.util.List;
34+
import java.util.Map;
35+
36+
/**
37+
* Noop mapper that ensures that mappings created in 6x that explicitly disable the _all field
38+
* can be restored in this version.
39+
*
40+
* TODO: Remove in 8
41+
*/
42+
public class AllFieldMapper extends MetadataFieldMapper {
43+
public static final String NAME = "_all";
44+
public static final String CONTENT_TYPE = "_all";
45+
46+
public static class Defaults {
47+
public static final MappedFieldType FIELD_TYPE = new AllFieldType();
48+
49+
static {
50+
FIELD_TYPE.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
51+
FIELD_TYPE.setTokenized(true);
52+
FIELD_TYPE.setName(NAME);
53+
FIELD_TYPE.freeze();
54+
}
55+
}
56+
57+
public static class Builder extends MetadataFieldMapper.Builder<Builder, AllFieldMapper> {
58+
private boolean disableExplicit = false;
59+
60+
public Builder(MappedFieldType existing) {
61+
super(NAME, existing == null ? Defaults.FIELD_TYPE : existing, Defaults.FIELD_TYPE);
62+
builder = this;
63+
}
64+
65+
private Builder setDisableExplicit() {
66+
this.disableExplicit = true;
67+
return this;
68+
}
69+
70+
@Override
71+
public AllFieldMapper build(BuilderContext context) {
72+
return new AllFieldMapper(fieldType, context.indexSettings(), disableExplicit);
73+
}
74+
}
75+
76+
public static class TypeParser implements MetadataFieldMapper.TypeParser {
77+
@Override
78+
public MetadataFieldMapper.Builder<?,?> parse(String name, Map<String, Object> node,
79+
ParserContext parserContext) throws MapperParsingException {
80+
Builder builder = new Builder(parserContext.mapperService().fullName(NAME));
81+
for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
82+
Map.Entry<String, Object> entry = iterator.next();
83+
String fieldName = entry.getKey();
84+
if (fieldName.equals("enabled")) {
85+
boolean enabled = XContentMapValues.nodeBooleanValue(entry.getValue(), "enabled");
86+
if (enabled) {
87+
throw new IllegalArgumentException("[_all] is disabled in this version.");
88+
}
89+
builder.setDisableExplicit();
90+
iterator.remove();
91+
}
92+
}
93+
return builder;
94+
}
95+
96+
@Override
97+
public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) {
98+
final Settings indexSettings = context.mapperService().getIndexSettings().getSettings();
99+
return new AllFieldMapper(indexSettings, Defaults.FIELD_TYPE, false);
100+
}
101+
}
102+
103+
static final class AllFieldType extends StringFieldType {
104+
AllFieldType() {
105+
}
106+
107+
protected AllFieldType(AllFieldType ref) {
108+
super(ref);
109+
}
110+
111+
@Override
112+
public MappedFieldType clone() {
113+
return new AllFieldType(this);
114+
}
115+
116+
@Override
117+
public String typeName() {
118+
return CONTENT_TYPE;
119+
}
120+
121+
@Override
122+
public Query existsQuery(QueryShardContext context) {
123+
return new MatchNoDocsQuery();
124+
}
125+
}
126+
127+
private final boolean disableExplicit;
128+
129+
private AllFieldMapper(Settings indexSettings, MappedFieldType existing, boolean disableExplicit) {
130+
this(existing.clone(), indexSettings, disableExplicit);
131+
}
132+
133+
private AllFieldMapper(MappedFieldType fieldType, Settings indexSettings, boolean disableExplicit) {
134+
super(NAME, fieldType, Defaults.FIELD_TYPE, indexSettings);
135+
this.disableExplicit = disableExplicit;
136+
}
137+
138+
@Override
139+
public void preParse(ParseContext context) throws IOException {
140+
}
141+
142+
@Override
143+
public void postParse(ParseContext context) throws IOException {
144+
super.parse(context);
145+
}
146+
147+
@Override
148+
public void parse(ParseContext context) throws IOException {
149+
// we parse in post parse
150+
}
151+
152+
@Override
153+
protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {
154+
// noop mapper
155+
return;
156+
}
157+
158+
@Override
159+
protected String contentType() {
160+
return CONTENT_TYPE;
161+
}
162+
163+
@Override
164+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
165+
boolean includeDefaults = params.paramAsBoolean("include_defaults", false);
166+
if (includeDefaults || disableExplicit) {
167+
builder.startObject(CONTENT_TYPE);
168+
if (disableExplicit) {
169+
builder.field("enabled", false);
170+
}
171+
builder.endObject();
172+
}
173+
return builder;
174+
}
175+
}

server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.lucene.search.Weight;
2828
import org.apache.lucene.util.BytesRef;
2929
import org.elasticsearch.ElasticsearchGenerationException;
30+
import org.elasticsearch.Version;
3031
import org.elasticsearch.common.bytes.BytesArray;
3132
import org.elasticsearch.common.compress.CompressedXContent;
3233
import org.elasticsearch.common.settings.Settings;
@@ -73,7 +74,9 @@ public Builder(RootObjectMapper.Builder builder, MapperService mapperService) {
7374

7475
final String type = rootObjectMapper.name();
7576
final DocumentMapper existingMapper = mapperService.documentMapper(type);
76-
final Map<String, TypeParser> metadataMapperParsers = mapperService.mapperRegistry.getMetadataMapperParsers();
77+
final Version indexCreatedVersion = mapperService.getIndexSettings().getIndexVersionCreated();
78+
final Map<String, TypeParser> metadataMapperParsers =
79+
mapperService.mapperRegistry.getMetadataMapperParsers(indexCreatedVersion);
7780
for (Map.Entry<String, MetadataFieldMapper.TypeParser> entry : metadataMapperParsers.entrySet()) {
7881
final String name = entry.getKey();
7982
final MetadataFieldMapper existingMetadataMapper = existingMapper == null

server/src/main/java/org/elasticsearch/index/mapper/DocumentMapperParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ public DocumentMapperParser(IndexSettings indexSettings, MapperService mapperSer
6565
this.similarityService = similarityService;
6666
this.queryShardContextSupplier = queryShardContextSupplier;
6767
this.typeParsers = mapperRegistry.getMapperParsers();
68-
this.rootTypeParsers = mapperRegistry.getMetadataMapperParsers();
69-
indexVersionCreated = indexSettings.getIndexVersionCreated();
68+
this.indexVersionCreated = indexSettings.getIndexVersionCreated();
69+
this.rootTypeParsers = mapperRegistry.getMetadataMapperParsers(indexVersionCreated);
7070
}
7171

7272
public Mapper.TypeParser.ParserContext parserContext(String type) {

server/src/main/java/org/elasticsearch/indices/IndicesService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.lucene.util.RamUsageEstimator;
3030
import org.elasticsearch.ElasticsearchException;
3131
import org.elasticsearch.ResourceAlreadyExistsException;
32+
import org.elasticsearch.Version;
3233
import org.elasticsearch.action.admin.indices.stats.CommonStats;
3334
import org.elasticsearch.action.admin.indices.stats.CommonStatsFlags;
3435
import org.elasticsearch.action.admin.indices.stats.CommonStatsFlags.Flag;
@@ -1382,8 +1383,8 @@ public Function<String, Predicate<String>> getFieldFilter() {
13821383
/**
13831384
* Returns true if the provided field is a registered metadata field (including ones registered via plugins), false otherwise.
13841385
*/
1385-
public boolean isMetaDataField(String field) {
1386-
return mapperRegistry.isMetaDataField(field);
1386+
public boolean isMetaDataField(Version indexCreatedVersion, String field) {
1387+
return mapperRegistry.isMetaDataField(indexCreatedVersion, field);
13871388
}
13881389

13891390
/**

server/src/main/java/org/elasticsearch/indices/mapper/MapperRegistry.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
package org.elasticsearch.indices.mapper;
2121

22+
import org.elasticsearch.Version;
23+
import org.elasticsearch.index.mapper.AllFieldMapper;
2224
import org.elasticsearch.index.mapper.Mapper;
2325
import org.elasticsearch.index.mapper.MetadataFieldMapper;
2426
import org.elasticsearch.plugins.MapperPlugin;
@@ -36,13 +38,19 @@ public final class MapperRegistry {
3638

3739
private final Map<String, Mapper.TypeParser> mapperParsers;
3840
private final Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers;
41+
private final Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers6x;
3942
private final Function<String, Predicate<String>> fieldFilter;
4043

4144

4245
public MapperRegistry(Map<String, Mapper.TypeParser> mapperParsers,
4346
Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers, Function<String, Predicate<String>> fieldFilter) {
4447
this.mapperParsers = Collections.unmodifiableMap(new LinkedHashMap<>(mapperParsers));
4548
this.metadataMapperParsers = Collections.unmodifiableMap(new LinkedHashMap<>(metadataMapperParsers));
49+
// add the _all field mapper for indices created in 6x
50+
Map<String, MetadataFieldMapper.TypeParser> metadata6x = new LinkedHashMap<>();
51+
metadata6x.put(AllFieldMapper.NAME, new AllFieldMapper.TypeParser());
52+
metadata6x.putAll(metadataMapperParsers);
53+
this.metadataMapperParsers6x = Collections.unmodifiableMap(metadata6x);
4654
this.fieldFilter = fieldFilter;
4755
}
4856

@@ -58,15 +66,15 @@ public Map<String, Mapper.TypeParser> getMapperParsers() {
5866
* Return a map of the meta mappers that have been registered. The
5967
* returned map uses the name of the field as a key.
6068
*/
61-
public Map<String, MetadataFieldMapper.TypeParser> getMetadataMapperParsers() {
62-
return metadataMapperParsers;
69+
public Map<String, MetadataFieldMapper.TypeParser> getMetadataMapperParsers(Version indexCreatedVersion) {
70+
return indexCreatedVersion.onOrAfter(Version.V_7_0_0) ? metadataMapperParsers : metadataMapperParsers6x;
6371
}
6472

6573
/**
66-
* Returns true if the provide field is a registered metadata field, false otherwise
74+
* Returns true if the provided field is a registered metadata field, false otherwise
6775
*/
68-
public boolean isMetaDataField(String field) {
69-
return getMetadataMapperParsers().containsKey(field);
76+
public boolean isMetaDataField(Version indexCreatedVersion, String field) {
77+
return getMetadataMapperParsers(indexCreatedVersion).containsKey(field);
7078
}
7179

7280
/**

0 commit comments

Comments
 (0)