Skip to content

Commit f14405a

Browse files
authored
Make _doc work as an alias of the actual type of an index. (#39505)
This is similar to the work that has been done on 7.x to make typeless API calls work on indices that have types, except that this commit doesn't introduce typeless calls, eg. the REST API spec or REST handlers haven't been updated. It only affects the get, index, update, delete and bulk APIs. Other APIs that require types such as explain or termvectors are left unchanged. This is necesarry to allow for rolling upgrades from 6.7 to 7.x while internal indices might remain queried during upgrade by nodes that are on either version. Closes #39469
1 parent 1976441 commit f14405a

File tree

22 files changed

+669
-33
lines changed

22 files changed

+669
-33
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
"bulk without types on an index that has types":
3+
4+
- skip:
5+
version: " - 6.6.99"
6+
reason: Typeless APIs were introduced in 6.7.0
7+
8+
- do:
9+
indices.create: # not using include_type_name: false on purpose
10+
include_type_name: true
11+
index: index
12+
body:
13+
mappings:
14+
not_doc:
15+
properties:
16+
foo:
17+
type: "keyword"
18+
- do:
19+
bulk:
20+
refresh: true
21+
body:
22+
- index:
23+
_index: index
24+
_type: _doc
25+
_id: 0
26+
- foo: bar
27+
- index:
28+
_index: index
29+
_type: _doc
30+
_id: 1
31+
- foo: bar
32+
33+
- do:
34+
count:
35+
index: index
36+
37+
- match: {count: 2}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
"DELETE with typeless API on an index that has types":
3+
4+
- skip:
5+
version: " - 6.6.99"
6+
reason: Typeless APIs were introduced in 6.7.0
7+
8+
- do:
9+
indices.create: # not using include_type_name: false on purpose
10+
include_type_name: true
11+
index: index
12+
body:
13+
mappings:
14+
not_doc:
15+
properties:
16+
foo:
17+
type: "keyword"
18+
19+
- do:
20+
index:
21+
index: index
22+
type: not_doc
23+
id: 1
24+
body: { foo: bar }
25+
26+
- do:
27+
catch: bad_request
28+
delete:
29+
index: index
30+
type: some_random_type
31+
id: 1
32+
33+
- match: { error.root_cause.0.reason: "/Rejecting.mapping.update.to.\\[index\\].as.the.final.mapping.would.have.more.than.1.type.*/" }
34+
35+
- do:
36+
delete:
37+
index: index
38+
type: _doc
39+
id: 1
40+
41+
- match: { _index: "index" }
42+
- match: { _type: "_doc" }
43+
- match: { _id: "1"}
44+
- match: { _version: 2}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
"GET with typeless API on an index that has types":
3+
4+
- skip:
5+
version: " - 6.6.99"
6+
reason: Typeless APIs were introduced in 6.7.0
7+
8+
- do:
9+
indices.create: # not using include_type_name: false on purpose
10+
include_type_name: true
11+
index: index
12+
body:
13+
mappings:
14+
not_doc:
15+
properties:
16+
foo:
17+
type: "keyword"
18+
19+
- do:
20+
index:
21+
index: index
22+
type: not_doc
23+
id: 1
24+
body: { foo: bar }
25+
26+
- do:
27+
catch: missing
28+
get:
29+
index: index
30+
type: some_random_type
31+
id: 1
32+
33+
- match: { _index: "index" }
34+
- match: { _type: "some_random_type" }
35+
- match: { _id: "1"}
36+
- match: { found: false}
37+
38+
- do:
39+
get:
40+
index: index
41+
type: _doc
42+
id: 1
43+
44+
- match: { _index: "index" }
45+
- match: { _type: "_doc" }
46+
- match: { _id: "1"}
47+
- match: { _version: 1}
48+
- match: { _source: { foo: bar }}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
"Index with typeless API on an index that has types":
3+
4+
- skip:
5+
version: " - 6.6.99"
6+
reason: Typeless APIs were introduced in 6.7.0
7+
8+
- do:
9+
indices.create: # not using include_type_name: false on purpose
10+
include_type_name: true
11+
index: index
12+
body:
13+
mappings:
14+
not_doc:
15+
properties:
16+
foo:
17+
type: "keyword"
18+
19+
- do:
20+
index:
21+
index: index
22+
type: _doc
23+
id: 1
24+
body: { foo: bar }
25+
26+
- match: { _index: "index" }
27+
- match: { _type: "_doc" }
28+
- match: { _id: "1"}
29+
- match: { _version: 1}
30+
31+
- do:
32+
get: # not using typeless API on purpose
33+
index: index
34+
type: not_doc
35+
id: 1
36+
37+
- match: { _index: "index" }
38+
- match: { _type: "not_doc" } # the important bit to check
39+
- match: { _id: "1"}
40+
- match: { _version: 1}
41+
- match: { _source: { foo: bar }}
42+
43+
44+
- do:
45+
index:
46+
index: index
47+
type: _doc
48+
body: { foo: bar }
49+
50+
- match: { _index: "index" }
51+
- match: { _type: "_doc" }
52+
- match: { _version: 1}
53+
- set: { _id: id }
54+
55+
- do:
56+
get: # using typeful API on purpose
57+
index: index
58+
type: not_doc
59+
id: '$id'
60+
61+
- match: { _index: "index" }
62+
- match: { _type: "not_doc" } # the important bit to check
63+
- match: { _id: $id}
64+
- match: { _version: 1}
65+
- match: { _source: { foo: bar }}
66+
67+
---
68+
"Index call that introduces new field mappings":
69+
70+
- skip:
71+
version: " - 6.6.99"
72+
reason: Typeless APIs were introduced in 6.7.0
73+
74+
- do:
75+
indices.create: # not using include_type_name: false on purpose
76+
include_type_name: true
77+
index: index
78+
body:
79+
mappings:
80+
not_doc:
81+
properties:
82+
foo:
83+
type: "keyword"
84+
- do:
85+
index:
86+
index: index
87+
type: _doc
88+
id: 2
89+
body: { new_field: value }
90+
91+
- match: { _index: "index" }
92+
- match: { _type: "_doc" }
93+
- match: { _id: "2" }
94+
- match: { _version: 1 }
95+
96+
- do:
97+
get: # using typeful API on purpose
98+
index: index
99+
type: not_doc
100+
id: 2
101+
102+
- match: { _index: "index" }
103+
- match: { _type: "not_doc" }
104+
- match: { _id: "2" }
105+
- match: { _version: 1}

rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/20_mix_typeless_typeful.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,22 @@
121121
type: keyword
122122

123123
- do:
124-
catch: /the final mapping would have more than 1 type/
125124
index:
126125
index: test-1
127126
type: _doc
128127
body: { bar: 42 }
129128

129+
# This cluster health call guarantees that changes are visible to the get-mappings API
130+
- do:
131+
cluster.health:
132+
wait_for_events: normal
133+
134+
- do:
135+
indices.get_mapping:
136+
include_type_name: true
137+
index: test-1
138+
139+
- is_true: test-1.mappings.my_type # the template is honored
140+
- is_false: test-1.mappings._doc
141+
- is_true: test-1.mappings.my_type.properties.foo
142+
- is_true: test-1.mappings.my_type.properties.bar
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
"Update with typeless API on an index that has types":
3+
4+
- skip:
5+
version: " - 6.6.99"
6+
reason: Typeless APIs were introduced in 6.7.0
7+
8+
- do:
9+
indices.create: # not using include_type_name: false on purpose
10+
include_type_name: true
11+
index: index
12+
body:
13+
mappings:
14+
not_doc:
15+
properties:
16+
foo:
17+
type: "keyword"
18+
19+
- do:
20+
index:
21+
index: index
22+
type: not_doc
23+
id: 1
24+
body: { foo: bar }
25+
26+
- do:
27+
update:
28+
index: index
29+
type: _doc
30+
id: 1
31+
body:
32+
doc:
33+
foo: baz
34+
35+
- do:
36+
get:
37+
index: index
38+
type: not_doc
39+
id: 1
40+
41+
- match: { _source.foo: baz }
42+
43+
---
44+
"Update call that introduces new field mappings":
45+
46+
- skip:
47+
version: " - 6.7.99"
48+
reason: Typeless APIs were introduced in 6.7.0
49+
50+
- do:
51+
indices.create: # not using include_type_name: false on purpose
52+
include_type_name: true
53+
index: index
54+
body:
55+
mappings:
56+
not_doc:
57+
properties:
58+
foo:
59+
type: "keyword"
60+
61+
- do:
62+
index:
63+
index: index
64+
type: not_doc
65+
id: 1
66+
body: { foo: bar }
67+
68+
- do:
69+
update:
70+
index: index
71+
type: _doc
72+
id: 1
73+
body:
74+
doc:
75+
foo: baz
76+
new_field: value
77+
- do:
78+
get: # using typeful API on purpose
79+
index: index
80+
type: not_doc
81+
id: 1
82+
83+
- match: { _index: "index" }
84+
- match: { _type: "not_doc" }
85+
- match: { _id: "1" }
86+
- match: { _version: 2}
87+
- match: { _source.foo: baz }
88+
- match: { _source.new_field: value }

server/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,8 @@ protected void doRun() throws Exception {
355355
case INDEX:
356356
IndexRequest indexRequest = (IndexRequest) docWriteRequest;
357357
final IndexMetaData indexMetaData = metaData.index(concreteIndex);
358-
MappingMetaData mappingMd = indexMetaData.mappingOrDefault(indexRequest.type());
358+
MappingMetaData mappingMd = indexMetaData.mappingOrDefault(
359+
indexMetaData.resolveDocumentType(indexRequest.type()));
359360
Version indexCreated = indexMetaData.getCreationVersion();
360361
indexRequest.resolveRouting(metaData);
361362
indexRequest.process(indexCreated, mappingMd, concreteIndex.getName());

server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ static void executeBulkItemRequest(BulkPrimaryExecutionContext context, UpdateHe
189189
case UPDATED:
190190
IndexRequest indexRequest = updateResult.action();
191191
IndexMetaData metaData = context.getPrimary().indexSettings().getIndexMetaData();
192-
MappingMetaData mappingMd = metaData.mappingOrDefault(indexRequest.type());
192+
MappingMetaData mappingMd = metaData.mappingOrDefault(metaData.resolveDocumentType(indexRequest.type()));
193193
indexRequest.process(metaData.getCreationVersion(), mappingMd, updateRequest.concreteIndex());
194194
context.setRequestToExecute(indexRequest);
195195
break;

server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,29 @@ public MappingMetaData mappingOrDefault(String mappingType) {
498498
return mappings.get(MapperService.DEFAULT_MAPPING);
499499
}
500500

501+
/**
502+
* Resolves a type from a mapping-related request into the type that should be used when
503+
* merging and updating mappings.
504+
*
505+
* If the special `_doc` type is provided, then we replace it with the actual type that is
506+
* being used in the mappings. This allows typeless APIs such as 'index' or 'put mappings'
507+
* to work against indices with a custom type name.
508+
*/
509+
public String resolveDocumentType(String type) {
510+
if (MapperService.SINGLE_MAPPING_NAME.equals(type) &&
511+
mappings.containsKey(type) == false &&
512+
getCreationVersion().onOrAfter(Version.V_6_0_0)) {
513+
// If the type is _doc and we have a 6.x index, then _doc is an alias
514+
// for the actual type of the index (if any)
515+
for (ObjectCursor<String> cursor : mappings.keys()) {
516+
if (cursor.value.equals(MapperService.DEFAULT_MAPPING) == false) {
517+
return cursor.value;
518+
}
519+
}
520+
}
521+
return type;
522+
}
523+
501524
ImmutableOpenMap<String, DiffableStringMap> getCustomData() {
502525
return this.customData;
503526
}

0 commit comments

Comments
 (0)