Skip to content

Commit f5e54b4

Browse files
authored
Deprecate support for chained multi-fields. (#41926)
We now issue a deprecation warning if a multi-field definition contains a `[fields]` entry. This PR also simplifies the definition of `MultiFieldParserContext`. Addresses #41267.
1 parent 07ab45a commit f5e54b4

File tree

4 files changed

+65
-6
lines changed

4 files changed

+65
-6
lines changed

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,17 @@ public Supplier<QueryShardContext> queryShardContextSupplier() {
136136
protected Function<String, SimilarityProvider> similarityLookupService() { return similarityLookupService; }
137137

138138
public ParserContext createMultiFieldContext(ParserContext in) {
139-
return new MultiFieldParserContext(in) {
140-
@Override
141-
public boolean isWithinMultiField() { return true; }
142-
};
139+
return new MultiFieldParserContext(in);
143140
}
144141

145142
static class MultiFieldParserContext extends ParserContext {
146143
MultiFieldParserContext(ParserContext in) {
147144
super(in.type(), in.similarityLookupService(), in.mapperService(), in.typeParsers(),
148145
in.indexVersionCreated(), in.queryShardContextSupplier());
149146
}
147+
148+
@Override
149+
public boolean isWithinMultiField() { return true; }
150150
}
151151

152152
}

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919

2020
package org.elasticsearch.index.mapper;
2121

22+
import org.apache.logging.log4j.LogManager;
2223
import org.apache.lucene.index.IndexOptions;
2324
import org.elasticsearch.ElasticsearchParseException;
25+
import org.elasticsearch.common.logging.DeprecationLogger;
2426
import org.elasticsearch.common.time.DateFormatter;
2527
import org.elasticsearch.common.xcontent.support.XContentMapValues;
2628
import org.elasticsearch.index.analysis.AnalysisMode;
@@ -37,6 +39,7 @@
3739
import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringValue;
3840

3941
public class TypeParsers {
42+
private static final DeprecationLogger deprecationLogger = new DeprecationLogger(LogManager.getLogger(TypeParsers.class));
4043

4144
public static final String DOC_VALUES = "doc_values";
4245
public static final String INDEX_OPTIONS_DOCS = "docs";
@@ -214,11 +217,18 @@ public static void parseField(FieldMapper.Builder builder, String name, Map<Stri
214217

215218
public static boolean parseMultiField(FieldMapper.Builder builder, String name, Mapper.TypeParser.ParserContext parserContext,
216219
String propName, Object propNode) {
217-
parserContext = parserContext.createMultiFieldContext(parserContext);
218220
if (propName.equals("fields")) {
221+
if (parserContext.isWithinMultiField()) {
222+
deprecationLogger.deprecatedAndMaybeLog("multifield_within_multifield", "At least one multi-field, [" + name + "], was " +
223+
"encountered that itself contains a multi-field. Defining multi-fields within a multi-field is deprecated and will " +
224+
"no longer be supported in 8.0. To resolve the issue, all instances of [fields] that occur within a [fields] block " +
225+
"should be removed from the mappings, either by flattening the chained [fields] blocks into a single level, or " +
226+
"switching to [copy_to] if appropriate.");
227+
}
219228

220-
final Map<String, Object> multiFieldsPropNodes;
229+
parserContext = parserContext.createMultiFieldContext(parserContext);
221230

231+
final Map<String, Object> multiFieldsPropNodes;
222232
if (propNode instanceof List && ((List<?>) propNode).isEmpty()) {
223233
multiFieldsPropNodes = Collections.emptyMap();
224234
} else if (propNode instanceof Map) {

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

+12
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ public void testExternalValuesWithMultifield() throws Exception {
169169

170170
assertThat(raw, notNullValue());
171171
assertThat(raw.binaryValue(), is(new BytesRef("foo")));
172+
173+
assertWarnings("At least one multi-field, [field], was " +
174+
"encountered that itself contains a multi-field. Defining multi-fields within a multi-field is deprecated and will " +
175+
"no longer be supported in 8.0. To resolve the issue, all instances of [fields] that occur within a [fields] block " +
176+
"should be removed from the mappings, either by flattening the chained [fields] blocks into a single level, or " +
177+
"switching to [copy_to] if appropriate.");
172178
}
173179

174180
public void testExternalValuesWithMultifieldTwoLevels() throws Exception {
@@ -234,5 +240,11 @@ public void testExternalValuesWithMultifieldTwoLevels() throws Exception {
234240

235241
assertThat(doc.rootDoc().getField("field.raw"), notNullValue());
236242
assertThat(doc.rootDoc().getField("field.raw").stringValue(), is("foo"));
243+
244+
assertWarnings("At least one multi-field, [field], was " +
245+
"encountered that itself contains a multi-field. Defining multi-fields within a multi-field is deprecated and will " +
246+
"no longer be supported in 8.0. To resolve the issue, all instances of [fields] that occur within a [fields] block " +
247+
"should be removed from the mappings, either by flattening the chained [fields] blocks into a single level, or " +
248+
"switching to [copy_to] if appropriate.");
237249
}
238250
}

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

+37
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
import org.apache.lucene.analysis.standard.StandardAnalyzer;
2525
import org.elasticsearch.Version;
2626
import org.elasticsearch.cluster.metadata.IndexMetaData;
27+
import org.elasticsearch.common.bytes.BytesReference;
2728
import org.elasticsearch.common.settings.Settings;
29+
import org.elasticsearch.common.xcontent.XContentBuilder;
30+
import org.elasticsearch.common.xcontent.XContentFactory;
31+
import org.elasticsearch.common.xcontent.XContentHelper;
2832
import org.elasticsearch.index.IndexSettings;
2933
import org.elasticsearch.index.analysis.AbstractTokenFilterFactory;
3034
import org.elasticsearch.index.analysis.AnalysisMode;
@@ -36,6 +40,7 @@
3640
import org.elasticsearch.index.analysis.TokenFilterFactory;
3741
import org.elasticsearch.test.ESTestCase;
3842

43+
import java.io.IOException;
3944
import java.util.Collections;
4045
import java.util.HashMap;
4146
import java.util.Map;
@@ -157,6 +162,38 @@ public void testParseTextFieldCheckAnalyzerWithSearchAnalyzerAnalysisMode() {
157162
TypeParsers.parseTextField(builder, "name", new HashMap<>(fieldNode), parserContext);
158163
}
159164

165+
public void testMultiFieldWithinMultiField() throws IOException {
166+
TextFieldMapper.Builder builder = new TextFieldMapper.Builder("textField");
167+
168+
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
169+
.field("type", "keyword")
170+
.startObject("fields")
171+
.startObject("sub-field")
172+
.field("type", "keyword")
173+
.startObject("fields")
174+
.startObject("sub-sub-field")
175+
.field("type", "keyword")
176+
.endObject()
177+
.endObject()
178+
.endObject()
179+
.endObject()
180+
.endObject();
181+
182+
Map<String, Object> fieldNode = XContentHelper.convertToMap(
183+
BytesReference.bytes(mapping), true, mapping.contentType()).v2();
184+
185+
Mapper.TypeParser typeParser = new KeywordFieldMapper.TypeParser();
186+
Mapper.TypeParser.ParserContext parserContext = new Mapper.TypeParser.ParserContext("type",
187+
null, null, type -> typeParser, Version.CURRENT, null);
188+
189+
TypeParsers.parseField(builder, "some-field", fieldNode, parserContext);
190+
assertWarnings("At least one multi-field, [sub-field], was " +
191+
"encountered that itself contains a multi-field. Defining multi-fields within a multi-field is deprecated and will " +
192+
"no longer be supported in 8.0. To resolve the issue, all instances of [fields] that occur within a [fields] block " +
193+
"should be removed from the mappings, either by flattening the chained [fields] blocks into a single level, or " +
194+
"switching to [copy_to] if appropriate.");
195+
}
196+
160197
private Analyzer createAnalyzerWithMode(String name, AnalysisMode mode) {
161198
TokenFilterFactory tokenFilter = new AbstractTokenFilterFactory(indexSettings, name, Settings.EMPTY) {
162199
@Override

0 commit comments

Comments
 (0)