diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json index 2ff9d3f68d9d9..d4a16e576e1b9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json @@ -14,7 +14,7 @@ }, "params": { "include_type_name": { - "type" : "string", + "type" : "boolean", "description" : "Whether a type should be expected in the body of the mappings." }, "wait_for_active_shards": { diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_field_mapping.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_field_mapping.json index 3d5a629eff08e..3ce610153b5e6 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_field_mapping.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_field_mapping.json @@ -21,6 +21,10 @@ } }, "params": { + "include_type_name": { + "type" : "boolean", + "description" : "Whether a type should be returned in the body of the mappings." + }, "include_defaults": { "type" : "boolean", "description" : "Whether the default mapping values should be returned as well" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json index 9bfb9c76abf82..ccec2ddffdd0c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json @@ -17,7 +17,7 @@ }, "params": { "include_type_name": { - "type" : "string", + "type" : "boolean", "description" : "Whether to add the type name to the response" }, "ignore_unavailable": { diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json index e3a97ee5c012a..e2aae3b7444aa 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json @@ -16,6 +16,10 @@ } }, "params": { + "include_type_name": { + "type" : "boolean", + "description" : "Whether a type should be returned in the body of the mappings." + }, "flat_settings": { "type": "boolean", "description": "Return settings in flat format (default: false)" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json index 4efb615329639..cc55ffccdd1ef 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json @@ -17,7 +17,7 @@ }, "params": { "include_type_name": { - "type" : "string", + "type" : "boolean", "description" : "Whether a type should be expected in the body of the mappings." }, "timeout": { diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json index 5bcb2f8a24346..65aa9506ff9f1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json @@ -13,6 +13,10 @@ } }, "params": { + "include_type_name": { + "type" : "boolean", + "description" : "Whether a type should be returned in the body of the mappings." + }, "order": { "type" : "number", "description" : "The order for this template when merging multiple matching ones (higher numbers are merged later, overriding the lower numbers)" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml index 6cf0a0b7cf26c..b77c56d34160c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml @@ -1,54 +1,48 @@ --- setup: + - skip: + version: " - 6.99.99" + reason: include_type_name is not supported before 7.0.0 - do: indices.create: + include_type_name: false index: test_index body: mappings: - test_type: - properties: - text: - type: text + properties: + text: + type: text --- -"Get field mapping with no index and type": +"Get field mapping with no index": - do: indices.get_field_mapping: + include_type_name: false fields: text - - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - match: {test_index.mappings.text.mapping.text.type: text} --- "Get field mapping by index only": - do: indices.get_field_mapping: + include_type_name: false index: test_index fields: text - - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - match: {test_index.mappings.text.mapping.text.type: text} --- -"Get field mapping by type & field": +"Get field mapping by field, with another field that doesn't exist": - do: indices.get_field_mapping: + include_type_name: false index: test_index - type: test_type - fields: text - - - match: {test_index.mappings.test_type.text.mapping.text.type: text} - ---- -"Get field mapping by type & field, with another field that doesn't exist": - - - do: - indices.get_field_mapping: - index: test_index - type: test_type fields: [ text , text1 ] - - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - match: {test_index.mappings.text.mapping.text.type: text} - is_false: test_index.mappings.test_type.text1 --- @@ -56,21 +50,10 @@ setup: - do: indices.get_field_mapping: + include_type_name: false index: test_index - type: test_type fields: text include_defaults: true - - match: {test_index.mappings.test_type.text.mapping.text.type: text} - - match: {test_index.mappings.test_type.text.mapping.text.analyzer: default} - ---- -"Get field mapping should work without index specifying type and fields": - - - do: - indices.get_field_mapping: - type: test_type - fields: text - - - match: {test_index.mappings.test_type.text.mapping.text.type: text} - + - match: {test_index.mappings.text.mapping.text.type: text} + - match: {test_index.mappings.text.mapping.text.analyzer: default} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/11_basic_with_types.yml new file mode 100644 index 0000000000000..6cf0a0b7cf26c --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/11_basic_with_types.yml @@ -0,0 +1,76 @@ +--- +setup: + - do: + indices.create: + index: test_index + body: + mappings: + test_type: + properties: + text: + type: text + +--- +"Get field mapping with no index and type": + + - do: + indices.get_field_mapping: + fields: text + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + +--- +"Get field mapping by index only": + - do: + indices.get_field_mapping: + index: test_index + fields: text + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + +--- +"Get field mapping by type & field": + + - do: + indices.get_field_mapping: + index: test_index + type: test_type + fields: text + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + +--- +"Get field mapping by type & field, with another field that doesn't exist": + + - do: + indices.get_field_mapping: + index: test_index + type: test_type + fields: [ text , text1 ] + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - is_false: test_index.mappings.test_type.text1 + +--- +"Get field mapping with include_defaults": + + - do: + indices.get_field_mapping: + index: test_index + type: test_type + fields: text + include_defaults: true + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - match: {test_index.mappings.test_type.text.mapping.text.analyzer: default} + +--- +"Get field mapping should work without index specifying type and fields": + + - do: + indices.get_field_mapping: + type: test_type + fields: text + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml index 9b8c3efbce81a..61f1f409d2939 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml @@ -1,21 +1,22 @@ --- "Return empty object if field doesn't exist, but type and index do": - + - skip: + version: " - 6.99.99" + reason: types are required in requests before 7.0.0 - do: indices.create: + include_type_name: false index: test_index body: - mappings: - test_type: - properties: - text: - type: text - analyzer: whitespace + mappings: + properties: + text: + type: text + analyzer: whitespace - do: indices.get_field_mapping: index: test_index - type: test_type fields: not_existent - - - match: { '': {}} + + - match: { 'test_index.mappings': {}} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/21_missing_field_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/21_missing_field_with_types.yml new file mode 100644 index 0000000000000..c760561f09282 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/21_missing_field_with_types.yml @@ -0,0 +1,21 @@ +--- +"Return empty object if field doesn't exist, but type and index do": + + - do: + indices.create: + index: test_index + body: + mappings: + test_type: + properties: + text: + type: text + analyzer: whitespace + + - do: + indices.get_field_mapping: + index: test_index + type: test_type + fields: not_existent + + - match: { '': {}} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml index 7da516e116c3d..7c7b07b587849 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml @@ -5,7 +5,6 @@ catch: missing indices.get_field_mapping: index: test_index - type: type fields: field diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml index 9d62ab6101fc2..3ffecdcc72618 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml @@ -1,135 +1,142 @@ --- setup: + - skip: + version: " - 6.99.99" + reason: types are required in requests before 7.0.0 - do: indices.create: + include_type_name: false index: test_index body: mappings: - test_type: - properties: - t1: - type: text - t2: - type: text - obj: - properties: - t1: - type: text - i_t1: - type: text - i_t3: - type: text + properties: + t1: + type: text + t2: + type: text + obj: + properties: + t1: + type: text + i_t1: + type: text + i_t3: + type: text - do: indices.create: + include_type_name: false index: test_index_2 body: mappings: - test_type_2: - properties: - t1: - type: text - t2: - type: text - obj: - properties: - t1: - type: text - i_t1: - type: text - i_t3: - type: text + properties: + t1: + type: text + t2: + type: text + obj: + properties: + t1: + type: text + i_t1: + type: text + i_t3: + type: text --- "Get field mapping with * for fields": - do: indices.get_field_mapping: + include_type_name: false fields: "*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - match: {test_index.mappings.test_type.obj\.t1.full_name: obj.t1 } - - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } - - match: {test_index.mappings.test_type.obj\.i_t3.full_name: obj.i_t3 } + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - match: {test_index.mappings.obj\.t1.full_name: obj.t1 } + - match: {test_index.mappings.obj\.i_t1.full_name: obj.i_t1 } + - match: {test_index.mappings.obj\.i_t3.full_name: obj.i_t3 } --- "Get field mapping with t* for fields": - do: indices.get_field_mapping: + include_type_name: false index: test_index fields: "t*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - length: {test_index.mappings.test_type: 2} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - length: {test_index.mappings: 2} --- "Get field mapping with *t1 for fields": - do: indices.get_field_mapping: + include_type_name: false index: test_index fields: "*t1" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.obj\.t1.full_name: obj.t1 } - - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } - - length: {test_index.mappings.test_type: 3} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.obj\.t1.full_name: obj.t1 } + - match: {test_index.mappings.obj\.i_t1.full_name: obj.i_t1 } + - length: {test_index.mappings: 3} --- "Get field mapping with wildcarded relative names": - do: indices.get_field_mapping: + include_type_name: false index: test_index fields: "obj.i_*" - - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } - - match: {test_index.mappings.test_type.obj\.i_t3.full_name: obj.i_t3 } - - length: {test_index.mappings.test_type: 2} + - match: {test_index.mappings.obj\.i_t1.full_name: obj.i_t1 } + - match: {test_index.mappings.obj\.i_t3.full_name: obj.i_t3 } + - length: {test_index.mappings: 2} --- -"Get field mapping should work using '_all' for indices and types": +"Get field mapping should work using '_all' for index": - do: indices.get_field_mapping: + include_type_name: false index: _all - type: _all fields: "t*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - length: {test_index.mappings.test_type: 2} - - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } - - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } - - length: {test_index_2.mappings.test_type_2: 2} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - length: {test_index.mappings: 2} + - match: {test_index_2.mappings.t1.full_name: t1 } + - match: {test_index_2.mappings.t2.full_name: t2 } + - length: {test_index_2.mappings: 2} --- -"Get field mapping should work using '*' for indices and types": +"Get field mapping should work using '*' for index": - do: indices.get_field_mapping: + include_type_name: false index: '*' - type: '*' fields: "t*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - length: {test_index.mappings.test_type: 2} - - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } - - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } - - length: {test_index_2.mappings.test_type_2: 2} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - length: {test_index.mappings: 2} + - match: {test_index_2.mappings.t1.full_name: t1 } + - match: {test_index_2.mappings.t2.full_name: t2 } + - length: {test_index_2.mappings: 2} --- -"Get field mapping should work using comma_separated values for indices and types": +"Get field mapping should work using comma_separated values for indices": - do: indices.get_field_mapping: + include_type_name: false index: 'test_index,test_index_2' - type: 'test_type,test_type_2' fields: "t*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - length: {test_index.mappings.test_type: 2} - - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } - - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } - - length: {test_index_2.mappings.test_type_2: 2} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - length: {test_index.mappings: 2} + - match: {test_index_2.mappings.t1.full_name: t1 } + - match: {test_index_2.mappings.t2.full_name: t2 } + - length: {test_index_2.mappings: 2} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/51_field_wildcards_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/51_field_wildcards_with_types.yml new file mode 100644 index 0000000000000..9d62ab6101fc2 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/51_field_wildcards_with_types.yml @@ -0,0 +1,135 @@ +--- +setup: + - do: + indices.create: + index: test_index + body: + mappings: + test_type: + properties: + t1: + type: text + t2: + type: text + obj: + properties: + t1: + type: text + i_t1: + type: text + i_t3: + type: text + + - do: + indices.create: + index: test_index_2 + body: + mappings: + test_type_2: + properties: + t1: + type: text + t2: + type: text + obj: + properties: + t1: + type: text + i_t1: + type: text + i_t3: + type: text + +--- +"Get field mapping with * for fields": + + - do: + indices.get_field_mapping: + fields: "*" + + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - match: {test_index.mappings.test_type.obj\.t1.full_name: obj.t1 } + - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } + - match: {test_index.mappings.test_type.obj\.i_t3.full_name: obj.i_t3 } + +--- +"Get field mapping with t* for fields": + + - do: + indices.get_field_mapping: + index: test_index + fields: "t*" + + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - length: {test_index.mappings.test_type: 2} + +--- +"Get field mapping with *t1 for fields": + + - do: + indices.get_field_mapping: + index: test_index + fields: "*t1" + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.obj\.t1.full_name: obj.t1 } + - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } + - length: {test_index.mappings.test_type: 3} + +--- +"Get field mapping with wildcarded relative names": + + - do: + indices.get_field_mapping: + index: test_index + fields: "obj.i_*" + - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } + - match: {test_index.mappings.test_type.obj\.i_t3.full_name: obj.i_t3 } + - length: {test_index.mappings.test_type: 2} + +--- +"Get field mapping should work using '_all' for indices and types": + + - do: + indices.get_field_mapping: + index: _all + type: _all + fields: "t*" + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - length: {test_index.mappings.test_type: 2} + - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } + - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } + - length: {test_index_2.mappings.test_type_2: 2} + +--- +"Get field mapping should work using '*' for indices and types": + + - do: + indices.get_field_mapping: + index: '*' + type: '*' + fields: "t*" + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - length: {test_index.mappings.test_type: 2} + - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } + - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } + - length: {test_index_2.mappings.test_type_2: 2} + +--- +"Get field mapping should work using comma_separated values for indices and types": + + - do: + indices.get_field_mapping: + index: 'test_index,test_index_2' + type: 'test_type,test_type_2' + fields: "t*" + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - length: {test_index.mappings.test_type: 2} + - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } + - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } + - length: {test_index_2.mappings.test_type_2: 2} + diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/60_mix_typeless_typeful.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/60_mix_typeless_typeful.yml new file mode 100644 index 0000000000000..d7ea620bb6c58 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/60_mix_typeless_typeful.yml @@ -0,0 +1,24 @@ +--- +"GET mapping with typeless API on an index that has types": + + - skip: + version: " - 6.99.99" + reason: include_type_name was introduced in 7.0.0 + + - do: + indices.create: # not using include_type_name: false on purpose + index: index + body: + mappings: + not_doc: + properties: + foo: + type: "keyword" + + - do: + indices.get_field_mapping: + include_type_name: false + index: index + fields: foo + + - match: { index.mappings.foo.mapping.foo.type: "keyword" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml index a03a10c1a5a89..8fe244e0c6323 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml @@ -1,4 +1,7 @@ setup: + - skip: + version: " - 6.99.99" + reason: include_type_name is not supported before 7.0.0 - do: indices.put_template: name: test @@ -7,16 +10,44 @@ setup: settings: number_of_shards: 1 number_of_replicas: 0 + mappings: + _doc: + properties: + field: + type: keyword --- "Get template": - do: indices.get_template: + include_type_name: false name: test - match: {test.index_patterns: ["test-*"]} - match: {test.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}} + - match: {test.mappings: {properties: {field: {type: keyword}}}} + +--- +"Get template with no mappings": + + - do: + indices.put_template: + name: test_no_mappings + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + + - do: + indices.get_template: + include_type_name: false + name: test_no_mappings + + - match: {test_no_mappings.index_patterns: ["test-*"]} + - match: {test_no_mappings.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}} + - match: {test_no_mappings.mappings: {}} --- "Get all templates": diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/11_basic_with_types.yml new file mode 100644 index 0000000000000..c15f5dc6de4f1 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/11_basic_with_types.yml @@ -0,0 +1,45 @@ +setup: + - do: + indices.put_template: + name: test + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: + _doc: + properties: + field: + type: keyword + +--- +"Get template": + + - do: + indices.get_template: + name: test + + - match: {test.index_patterns: ["test-*"]} + - match: {test.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}} + - match: {test.mappings: {_doc: {properties: {field: {type: keyword}}}}} + +--- +"Get template with no mappings": + + - do: + indices.put_template: + name: test_no_mappings + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + + - do: + indices.get_template: + name: test_no_mappings + + - match: {test_no_mappings.index_patterns: ["test-*"]} + - match: {test_no_mappings.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}} + - match: {test_no_mappings.mappings: {}} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml index b4e66c23c605b..8637b3e6d1864 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml @@ -1,42 +1,87 @@ --- "Put template": + - skip: + version: " - 6.99.99" + reason: include_type_name is not supported before 7.0.0 - do: indices.put_template: + include_type_name: false name: test body: index_patterns: test-* settings: number_of_shards: 1 number_of_replicas: 0 + mappings: + properties: + field: + type: keyword - do: indices.get_template: + include_type_name: false name: test flat_settings: true - match: {test.index_patterns: ["test-*"]} - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + - match: {test.mappings: {properties: {field: {type: keyword}}}} --- "Put multiple template": + - skip: + version: " - 6.99.99" + reason: include_type_name was introduced in 7.0.0 - do: indices.put_template: + include_type_name: false name: test body: index_patterns: [test-*, test2-*] settings: number_of_shards: 1 number_of_replicas: 0 + mappings: + properties: + field: + type: text - do: indices.get_template: + include_type_name: false name: test flat_settings: true - match: {test.index_patterns: ["test-*", "test2-*"]} - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + - match: {test.mappings: {properties: {field: {type: text}}}} + +--- +"Put template with empty mappings": + - skip: + version: " - 6.99.99" + reason: include_type_name was introduced in 7.0.0 + + - do: + indices.put_template: + include_type_name: false + name: test + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: {} + + - do: + indices.get_template: + include_type_name: false + name: test + flat_settings: true + + - match: {test.mappings: {}} --- "Put template with aliases": diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/11_basic_with_types.yml new file mode 100644 index 0000000000000..1e14a9d3895a7 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/11_basic_with_types.yml @@ -0,0 +1,68 @@ +--- +"Put template": + - do: + indices.put_template: + name: test + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: + _doc: + properties: + field: + type: keyword + + - do: + indices.get_template: + name: test + flat_settings: true + + - match: {test.index_patterns: ["test-*"]} + - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + - match: {test.mappings: {_doc: {properties: {field: {type: keyword}}}}} + +--- +"Put multiple template": + - do: + indices.put_template: + name: test + body: + index_patterns: [test-*, test2-*] + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: + _doc: + properties: + field: + type: text + + - do: + indices.get_template: + name: test + flat_settings: true + + - match: {test.index_patterns: ["test-*", "test2-*"]} + - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + - match: {test.mappings: {_doc: {properties: {field: {type: text}}}}} + +--- +"Put template with empty mappings": + - do: + indices.put_template: + name: test + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: {} + + - do: + indices.get_template: + name: test + flat_settings: true + + - match: {test.mappings: {}} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java index 44a66f497c846..2c07ebc68d0ef 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java @@ -34,6 +34,8 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.mapper.Mapper; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.rest.BaseRestHandler; import java.io.IOException; import java.io.InputStream; @@ -112,19 +114,32 @@ public FieldMappingMetaData fieldMappings(String index, String type, String fiel @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeTypeName = params.paramAsBoolean(BaseRestHandler.INCLUDE_TYPE_NAME_PARAMETER, true); + builder.startObject(); for (Map.Entry>> indexEntry : mappings.entrySet()) { builder.startObject(indexEntry.getKey()); builder.startObject(MAPPINGS.getPreferredName()); - for (Map.Entry> typeEntry : indexEntry.getValue().entrySet()) { - builder.startObject(typeEntry.getKey()); - for (Map.Entry fieldEntry : typeEntry.getValue().entrySet()) { - builder.startObject(fieldEntry.getKey()); - fieldEntry.getValue().toXContent(builder, params); + + if (includeTypeName == false) { + Map mappings = null; + for (Map.Entry> typeEntry : indexEntry.getValue().entrySet()) { + if (typeEntry.getKey().equals(MapperService.DEFAULT_MAPPING) == false) { + assert mappings == null; + mappings = typeEntry.getValue(); + } + } + if (mappings != null) { + addFieldMappingsToBuilder(builder, params, mappings); + } + } else { + for (Map.Entry> typeEntry : indexEntry.getValue().entrySet()) { + builder.startObject(typeEntry.getKey()); + addFieldMappingsToBuilder(builder, params, typeEntry.getValue()); builder.endObject(); } - builder.endObject(); } + builder.endObject(); builder.endObject(); } @@ -132,6 +147,16 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + private void addFieldMappingsToBuilder(XContentBuilder builder, + Params params, + Map mappings) throws IOException { + for (Map.Entry fieldEntry : mappings.entrySet()) { + builder.startObject(fieldEntry.getKey()); + fieldEntry.getValue().toXContent(builder, params); + builder.endObject(); + } + } + public static GetFieldMappingsResponse fromXContent(XContentParser parser) throws IOException { ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetaData.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetaData.java index f9ef5786afdca..4055af3e2f460 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetaData.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetaData.java @@ -20,7 +20,6 @@ import com.carrotsearch.hppc.cursors.ObjectCursor; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; - import org.apache.logging.log4j.LogManager; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.Version; @@ -42,6 +41,8 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.rest.BaseRestHandler; import java.io.IOException; import java.util.ArrayList; @@ -342,6 +343,8 @@ public static void toXContent(IndexTemplateMetaData indexTemplateMetaData, XCont public static void toInnerXContent(IndexTemplateMetaData indexTemplateMetaData, XContentBuilder builder, ToXContent.Params params) throws IOException { + boolean includeTypeName = params.paramAsBoolean(BaseRestHandler.INCLUDE_TYPE_NAME_PARAMETER, true); + builder.field("order", indexTemplateMetaData.order()); if (indexTemplateMetaData.version() != null) { builder.field("version", indexTemplateMetaData.version()); @@ -353,18 +356,35 @@ public static void toInnerXContent(IndexTemplateMetaData indexTemplateMetaData, builder.endObject(); if (params.paramAsBoolean("reduce_mappings", false)) { - builder.startObject("mappings"); - for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) { - byte[] mappingSource = cursor.value.uncompressed(); - Map mapping = XContentHelper.convertToMap(new BytesArray(mappingSource), true).v2(); - if (mapping.size() == 1 && mapping.containsKey(cursor.key)) { - // the type name is the root value, reduce it - mapping = (Map) mapping.get(cursor.key); + // The parameter include_type_name is only ever used in the REST API, where reduce_mappings is + // always set to true. We therefore only check for include_type_name in this branch. + if (includeTypeName == false) { + Map documentMapping = null; + for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) { + if (!cursor.key.equals(MapperService.DEFAULT_MAPPING)) { + assert documentMapping == null; + byte[] mappingSource = cursor.value.uncompressed(); + Map mapping = XContentHelper.convertToMap(new BytesArray(mappingSource), true).v2(); + documentMapping = reduceMapping(cursor.key, mapping); + } + } + + if (documentMapping != null) { + builder.field("mappings", documentMapping); + } else { + builder.startObject("mappings").endObject(); + } + } else { + builder.startObject("mappings"); + for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) { + byte[] mappingSource = cursor.value.uncompressed(); + Map mapping = XContentHelper.convertToMap(new BytesArray(mappingSource), true).v2(); + mapping = reduceMapping(cursor.key, mapping); + builder.field(cursor.key); + builder.map(mapping); } - builder.field(cursor.key); - builder.map(mapping); + builder.endObject(); } - builder.endObject(); } else { builder.startArray("mappings"); for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) { @@ -381,6 +401,16 @@ public static void toInnerXContent(IndexTemplateMetaData indexTemplateMetaData, builder.endObject(); } + @SuppressWarnings("unchecked") + private static Map reduceMapping(String type, Map mapping) { + if (mapping.size() == 1 && mapping.containsKey(type)) { + // the type name is the root value, reduce it + return (Map) mapping.get(type); + } else { + return mapping; + } + } + public static IndexTemplateMetaData fromXContent(XContentParser parser, String templateName) throws IOException { Builder builder = new Builder(templateName); diff --git a/server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java b/server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java index 963c8089f342b..97b4e29d9a208 100644 --- a/server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java +++ b/server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java @@ -60,7 +60,7 @@ public abstract class BaseRestHandler extends AbstractComponent implements RestH /** * Parameter that controls whether certain REST apis should include type names in their requests or responses. - * Note: Support for this parameter will be removed after the transition perido to typeless APIs. + * Note: Support for this parameter will be removed after the transition period to typeless APIs. */ public static final String INCLUDE_TYPE_NAME_PARAMETER = "include_type_name"; diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java index c43f14dcddf26..f3a73fa29fd98 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java @@ -62,6 +62,13 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC final String[] indices = Strings.splitStringByCommaToArray(request.param("index")); final String[] types = request.paramAsStringArrayOrEmptyIfAll("type"); final String[] fields = Strings.splitStringByCommaToArray(request.param("fields")); + + boolean includeTypeName = request.paramAsBoolean(INCLUDE_TYPE_NAME_PARAMETER, true); + if (includeTypeName == false && types.length > 0) { + throw new IllegalArgumentException("Cannot set include_type_name=false and specify" + + " types at the same time."); + } + GetFieldMappingsRequest getMappingsRequest = new GetFieldMappingsRequest(); getMappingsRequest.indices(indices).types(types).fields(fields).includeDefaults(request.paramAsBoolean("include_defaults", false)); getMappingsRequest.indicesOptions(IndicesOptions.fromRequest(request, getMappingsRequest.indicesOptions())); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndexTemplateAction.java index 38c1cb76611f4..50370797aa6f2 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndexTemplateAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -31,6 +32,7 @@ import org.elasticsearch.rest.action.RestToXContentListener; import java.io.IOException; +import java.util.Collections; import java.util.Set; import static org.elasticsearch.rest.RestRequest.Method.GET; @@ -43,6 +45,9 @@ */ public class RestGetIndexTemplateAction extends BaseRestHandler { + private static final Set RESPONSE_PARAMETERS = Collections.unmodifiableSet(Sets.union( + Collections.singleton(INCLUDE_TYPE_NAME_PARAMETER), Settings.FORMAT_PARAMS)); + public RestGetIndexTemplateAction(final Settings settings, final RestController controller) { super(settings); controller.registerHandler(GET, "/_template", this); @@ -79,7 +84,7 @@ protected RestStatus getStatus(final GetIndexTemplatesResponse response) { @Override protected Set responseParams() { - return Settings.FORMAT_PARAMS; + return RESPONSE_PARAMETERS; } } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateAction.java index 258bb05a7d66c..f5cc3c6aad26d 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateAction.java @@ -25,6 +25,8 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -33,6 +35,8 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public class RestPutIndexTemplateAction extends BaseRestHandler { @@ -63,8 +67,23 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC putRequest.masterNodeTimeout(request.paramAsTime("master_timeout", putRequest.masterNodeTimeout())); putRequest.create(request.paramAsBoolean("create", false)); putRequest.cause(request.param("cause", "")); - putRequest.source(request.requiredContent(), request.getXContentType()); + + boolean includeTypeName = request.paramAsBoolean(INCLUDE_TYPE_NAME_PARAMETER, true); + Map sourceAsMap = prepareRequestSource(request, includeTypeName); + putRequest.source(sourceAsMap); + return channel -> client.admin().indices().putTemplate(putRequest, new RestToXContentListener<>(channel)); } + Map prepareRequestSource(RestRequest request, boolean includeTypeName) { + Map sourceAsMap = XContentHelper.convertToMap(request.requiredContent(), false, + request.getXContentType()).v2(); + if (includeTypeName == false && sourceAsMap.containsKey("mappings")) { + Map newSourceAsMap = new HashMap<>(sourceAsMap); + newSourceAsMap.put("mappings", Collections.singletonMap(MapperService.SINGLE_MAPPING_NAME, sourceAsMap.get("mappings"))); + return newSourceAsMap; + } else { + return sourceAsMap; + } + } } diff --git a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingActionTests.java new file mode 100644 index 0000000000000..dcf4237ae07bf --- /dev/null +++ b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingActionTests.java @@ -0,0 +1,61 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.rest.action.admin.indices; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.rest.FakeRestChannel; +import org.elasticsearch.test.rest.FakeRestRequest; +import org.elasticsearch.test.rest.RestActionTestCase; +import org.junit.Before; + +import java.util.HashMap; +import java.util.Map; + +import static org.elasticsearch.rest.BaseRestHandler.INCLUDE_TYPE_NAME_PARAMETER; + +public class RestGetFieldMappingActionTests extends RestActionTestCase { + + @Before + public void setUpAction() { + new RestGetFieldMappingAction(Settings.EMPTY, controller()); + } + + public void testTypeInPath() { + // Test that specifying a type while setting include_type_name to false + // results in an illegal argument exception. + Map params = new HashMap<>(); + params.put(INCLUDE_TYPE_NAME_PARAMETER, "false"); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()) + .withMethod(RestRequest.Method.GET) + .withPath("some_index/some_type/_mapping/field/some_field") + .withParams(params) + .build(); + + FakeRestChannel channel = new FakeRestChannel(request, false, 1); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + controller().dispatchRequest(request, channel, threadContext); + + assertEquals(1, channel.errors().get()); + assertEquals(RestStatus.BAD_REQUEST, channel.capturedResponse().status()); + } +} diff --git a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java new file mode 100644 index 0000000000000..ac0eb8f0d81a6 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java @@ -0,0 +1,81 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.rest.action.admin.indices; + +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.test.rest.FakeRestRequest; +import org.elasticsearch.test.rest.RestActionTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.util.Map; + +public class RestPutIndexTemplateActionTests extends RestActionTestCase { + private RestPutIndexTemplateAction action; + + @Before + public void setUpAction() { + action = new RestPutIndexTemplateAction(Settings.EMPTY, controller()); + } + + public void testPrepareTypelessRequest() throws IOException { + XContentBuilder content = XContentFactory.jsonBuilder().startObject() + .startObject("mappings") + .startObject("properties") + .startObject("field").field("type", "keyword").endObject() + .endObject() + .endObject() + .startObject("aliases") + .startObject("read_alias").endObject() + .endObject() + .endObject(); + + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()) + .withMethod(RestRequest.Method.PUT) + .withPath("/_template/_some_template") + .withContent(BytesReference.bytes(content), XContentType.JSON) + .build(); + boolean includeTypeName = false; + Map source = action.prepareRequestSource(request, includeTypeName); + + XContentBuilder expectedContent = XContentFactory.jsonBuilder().startObject() + .startObject("mappings") + .startObject("_doc") + .startObject("properties") + .startObject("field").field("type", "keyword").endObject() + .endObject() + .endObject() + .endObject() + .startObject("aliases") + .startObject("read_alias").endObject() + .endObject() + .endObject(); + Map expectedContentAsMap = XContentHelper.convertToMap( + BytesReference.bytes(expectedContent), true, expectedContent.contentType()).v2(); + + assertEquals(expectedContentAsMap, source); + } +}