Skip to content

Commit 0fe8493

Browse files
committed
Enforce that field aliases can only be specified on indexes with a single type.
1 parent 457d204 commit 0fe8493

File tree

12 files changed

+131
-42
lines changed

12 files changed

+131
-42
lines changed

docs/reference/mapping/types/alias.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
[[alias]]
22
=== Alias datatype
33

4+
NOTE: Field aliases can only be specified on indexes with a single mapping type. To add a field
5+
alias, the index must therefore have been created in 6.0 or later, or be an older index with
6+
the setting `index.mapping.single_type: true`. Please see <<removal-of-types>> for more information.
7+
48
An `alias` mapping defines an alternate name for a field in the index.
59
The alias can be used in place of the target field in <<search, search>> requests,
610
and selected other APIs like <<search-field-caps, field capabilities>>.

modules/percolator/src/test/java/org/elasticsearch/percolator/PercolateQueryBuilderTests.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,20 @@ protected Collection<Class<? extends Plugin>> getPlugins() {
9797
@Override
9898
protected void initializeAdditionalMappings(MapperService mapperService) throws IOException {
9999
queryField = randomAlphaOfLength(4);
100-
aliasField = randomAlphaOfLength(4);
101100

102101
String docType = "_doc";
103102
mapperService.merge(docType, new CompressedXContent(Strings.toString(PutMappingRequest.buildFromSimplifiedDef(docType,
104-
queryField, "type=percolator", aliasField, "type=alias,path=" + queryField
103+
queryField, "type=percolator"
105104
))), MapperService.MergeReason.MAPPING_UPDATE, false);
105+
106+
// Field aliases are only supported on indexes with a single type.
107+
if (mapperService.getIndexSettings().isSingleType()) {
108+
aliasField = randomAlphaOfLength(4);
109+
mapperService.merge(docType, new CompressedXContent(Strings.toString(PutMappingRequest.buildFromSimplifiedDef(docType,
110+
aliasField, "type=alias,path=" + queryField
111+
))), MapperService.MergeReason.MAPPING_UPDATE, false);
112+
}
113+
106114
mapperService.merge(docType, new CompressedXContent(Strings.toString(PutMappingRequest.buildFromSimplifiedDef(docType,
107115
STRING_FIELD_NAME, "type=text"
108116
))), MapperService.MergeReason.MAPPING_UPDATE, false);
@@ -369,6 +377,8 @@ public void testSerializationFailsUnlessFetched() throws IOException {
369377
}
370378

371379
public void testFieldAlias() throws IOException {
380+
assumeTrue("Test only runs when there is a single mapping type.", isSingleType());
381+
372382
QueryShardContext shardContext = createShardContext();
373383

374384
PercolateQueryBuilder builder = doCreateTestQueryBuilder(false);

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.elasticsearch.common.xcontent.XContentBuilder;
2323
import org.elasticsearch.common.xcontent.support.XContentMapValues;
24+
import org.elasticsearch.index.IndexSettings;
2425

2526
import java.io.IOException;
2627
import java.util.Collections;
@@ -74,6 +75,10 @@ public Mapper merge(Mapper mergeWith, boolean updateAllTypes) {
7475
return mergeWith;
7576
}
7677

78+
/**
79+
* Note: this method is a no-op because field aliases cannot be specified on indexes
80+
* with more than one type.
81+
*/
7782
@Override
7883
public Mapper updateFieldType(Map<String, MappedFieldType> fullNameToFieldType) {
7984
return this;
@@ -96,6 +101,8 @@ public static class TypeParser implements Mapper.TypeParser {
96101
@Override
97102
public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
98103
throws MapperParsingException {
104+
checkIndexCompatibility(parserContext.mapperService().getIndexSettings(), name);
105+
99106
FieldAliasMapper.Builder builder = new FieldAliasMapper.Builder(name);
100107
Object pathField = node.remove(Names.PATH);
101108
String path = XContentMapValues.nodeStringValue(pathField, null);
@@ -104,6 +111,13 @@ public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext
104111
}
105112
return builder.path(path);
106113
}
114+
115+
private static void checkIndexCompatibility(IndexSettings settings, String name) {
116+
if (!settings.isSingleType()) {
117+
throw new IllegalStateException("Cannot create a field alias [" + name + "] " +
118+
"on index [" + settings.getIndex().getName() + "], as it has multiple types.");
119+
}
120+
}
107121
}
108122

109123
public static class Builder extends Mapper.Builder<FieldAliasMapper.Builder, FieldAliasMapper> {

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,32 @@
1919

2020
package org.elasticsearch.index.mapper;
2121

22+
import org.elasticsearch.Version;
23+
import org.elasticsearch.cluster.metadata.IndexMetaData;
2224
import org.elasticsearch.common.Strings;
2325
import org.elasticsearch.common.compress.CompressedXContent;
26+
import org.elasticsearch.common.settings.Settings;
2427
import org.elasticsearch.common.xcontent.XContentFactory;
2528
import org.elasticsearch.index.IndexService;
2629
import org.elasticsearch.index.mapper.MapperService.MergeReason;
30+
import org.elasticsearch.plugins.Plugin;
2731
import org.elasticsearch.test.ESSingleNodeTestCase;
32+
import org.elasticsearch.test.InternalSettingsPlugin;
33+
import org.elasticsearch.test.VersionUtils;
2834
import org.junit.Before;
2935

3036
import java.io.IOException;
37+
import java.util.Collection;
3138

3239
public class FieldAliasMapperTests extends ESSingleNodeTestCase {
3340
private MapperService mapperService;
3441
private DocumentMapperParser parser;
3542

43+
@Override
44+
protected Collection<Class<? extends Plugin>> getPlugins() {
45+
return pluginList(InternalSettingsPlugin.class);
46+
}
47+
3648
@Before
3749
public void setup() {
3850
IndexService indexService = createIndex("test");
@@ -164,4 +176,31 @@ public void testMergeFailure() throws IOException {
164176
assertEquals("Cannot merge a field alias mapping [alias-field] with a mapping that is not for a field alias.",
165177
exception.getMessage());
166178
}
179+
180+
public void testFieldAliasDisallowedWithMultipleTypes() throws IOException {
181+
Version version = VersionUtils.randomVersionBetween(random(), null, Version.V_5_6_0);
182+
Settings settings = Settings.builder()
183+
.put(IndexMetaData.SETTING_VERSION_CREATED, version)
184+
.build();
185+
IndexService indexService = createIndex("alias-test", settings);
186+
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
187+
188+
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject()
189+
.startObject("type")
190+
.startObject("properties")
191+
.startObject("alias-field")
192+
.field("type", "alias")
193+
.field("path", "concrete-field")
194+
.endObject()
195+
.startObject("concrete-field")
196+
.field("type", "keyword")
197+
.endObject()
198+
.endObject()
199+
.endObject()
200+
.endObject());
201+
IllegalStateException e = expectThrows(IllegalStateException.class,
202+
() -> parser.parse("type", new CompressedXContent(mapping)));
203+
assertEquals("Cannot create a field alias [alias-field] on index [alias-test],"
204+
+ " as it has multiple types.", e.getMessage());
205+
}
167206
}

server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,12 @@ public void testToQueryFieldsWildcard() throws Exception {
235235
DisjunctionMaxQuery dQuery = (DisjunctionMaxQuery) query;
236236
assertThat(dQuery.getTieBreakerMultiplier(), equalTo(1.0f));
237237
assertThat(dQuery.getDisjuncts().size(), equalTo(3));
238-
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 0).getTerm(), equalTo(new Term(STRING_FIELD_NAME, "test")));
239-
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 1).getTerm(), equalTo(new Term(STRING_FIELD_NAME_2, "test")));
240-
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 2).getTerm(), equalTo(new Term(STRING_FIELD_NAME, "test")));
238+
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 0).getTerm(),
239+
equalTo(new Term(expectedFieldName(STRING_ALIAS_FIELD_NAME), "test")));
240+
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 1).getTerm(),
241+
equalTo(new Term(STRING_FIELD_NAME_2, "test")));
242+
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 2).getTerm(),
243+
equalTo(new Term(STRING_FIELD_NAME, "test")));
241244
}
242245

243246
public void testToQueryFieldMissing() throws Exception {

server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ public void testToQueryFieldsWildcard() throws Exception {
507507
DisjunctionMaxQuery dQuery = (DisjunctionMaxQuery) query;
508508
assertThat(dQuery.getDisjuncts().size(), equalTo(3));
509509
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 0).getTerm(),
510-
equalTo(new Term(STRING_FIELD_NAME, "test")));
510+
equalTo(new Term(expectedFieldName(STRING_ALIAS_FIELD_NAME), "test")));
511511
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 1).getTerm(),
512512
equalTo(new Term(STRING_FIELD_NAME_2, "test")));
513513
assertThat(assertDisjunctionSubQuery(query, TermQuery.class, 2).getTerm(),

server/src/test/java/org/elasticsearch/index/query/RangeQueryBuilderTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ protected void doAssertLuceneQuery(RangeQueryBuilder queryBuilder, Query query,
150150

151151
} else if (getCurrentTypes().length == 0 ||
152152
(expectedFieldName.equals(DATE_FIELD_NAME) == false
153+
&& expectedFieldName.equals(DATE_ALIAS_FIELD_NAME) == false
153154
&& expectedFieldName.equals(INT_FIELD_NAME) == false
155+
&& expectedFieldName.equals(INT_ALIAS_FIELD_NAME) == false
154156
&& expectedFieldName.equals(DATE_RANGE_FIELD_NAME) == false
155157
&& expectedFieldName.equals(INT_RANGE_FIELD_NAME) == false)) {
156158
assertThat(query, instanceOf(TermRangeQuery.class));

server/src/test/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilderTests.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,16 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws
6262
.startObject("prefix_field")
6363
.field("type", "text")
6464
.startObject("index_prefixes").endObject()
65-
.endObject()
66-
.startObject("prefix_field_alias")
67-
.field("type", "alias")
68-
.field("path", "prefix_field")
69-
.endObject()
70-
.endObject().endObject().endObject();
71-
65+
.endObject();
66+
67+
// Field aliases are only supported on indexes with a single type.
68+
if (mapperService.getIndexSettings().isSingleType()) {
69+
mapping.startObject("prefix_field_alias")
70+
.field("type", "alias")
71+
.field("path", "prefix_field")
72+
.endObject();
73+
}
74+
mapping.endObject().endObject().endObject();
7275
mapperService.merge("_doc",
7376
new CompressedXContent(Strings.toString(mapping)), MapperService.MergeReason.MAPPING_UPDATE, true);
7477
}
@@ -178,7 +181,9 @@ public void testToQueryInnerSpanMultiTerm() throws IOException {
178181
}
179182

180183
public void testToQueryInnerTermQuery() throws IOException {
181-
String fieldName = randomFrom("prefix_field", "prefix_field_alias");
184+
String fieldName = isSingleType()
185+
? randomFrom("prefix_field", "prefix_field_alias")
186+
: "prefix_field";
182187
final QueryShardContext context = createShardContext();
183188
if (context.getIndexSettings().getIndexVersionCreated().onOrAfter(Version.V_6_4_0)) {
184189
Query query = new SpanMultiTermQueryBuilder(new PrefixQueryBuilder(fieldName, "foo"))

server/src/test/java/org/elasticsearch/index/query/TermsSetQueryBuilderTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ public void testDoToQuery_msmScriptField() throws Exception {
263263
}
264264

265265
public void testFieldAlias() {
266+
assumeTrue("Test runs only when there is a single mapping type.", isSingleType());
267+
266268
List<String> randomTerms = Arrays.asList(generateRandomStringArray(5, 10, false, false));
267269
TermsSetQueryBuilder queryBuilder = new TermsSetQueryBuilder(STRING_ALIAS_FIELD_NAME, randomTerms)
268270
.setMinimumShouldMatchField("m_s_m");

server/src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsIT.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,6 @@ private XContentBuilder getMappingForType(String type) throws IOException {
7777
.startObject("field1")
7878
.field("type", "text")
7979
.endObject()
80-
.startObject("alias")
81-
.field("type", "alias")
82-
.field("path", "field1")
83-
.endObject()
8480
.startObject("obj")
8581
.startObject("properties")
8682
.startObject("subfield")
@@ -232,7 +228,18 @@ public void testSimpleGetFieldMappingsWithDefaults() throws Exception {
232228

233229
@SuppressWarnings("unchecked")
234230
public void testGetFieldMappingsWithFieldAlias() throws Exception {
235-
assertAcked(prepareCreate("test").addMapping("type", getMappingForType("type")));
231+
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
232+
.startObject("properties")
233+
.startObject("field1")
234+
.field("type", "text")
235+
.endObject()
236+
.startObject("alias")
237+
.field("type", "alias")
238+
.field("path", "field1")
239+
.endObject()
240+
.endObject()
241+
.endObject();
242+
assertAcked(prepareCreate("test").addMapping("type", mapping));
236243

237244
GetFieldMappingsResponse response = client().admin().indices().prepareGetFieldMappings()
238245
.setFields("alias", "field1").get();

0 commit comments

Comments
 (0)