From 8819f91d4761b62fced5aea376a7cbd294dfa80d Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Mon, 14 Oct 2013 08:53:07 +0200 Subject: [PATCH] Add a GetFieldMapping API This new API allows to get the mapping for a specific set of fields rather than get the whole index mapping and traverse it. The fields to be retrieved can be specified by their full path, index name and field name and will be resolved in this order. In case multiple field match, the first one will be returned. Since we are now generating the output (rather then fall back to the stored mapping), you can specify `include_defaults`=true on the request to have default values returned. Closes #3941 --- docs/reference/indices.asciidoc | 4 + .../indices/get-field-mapping.asciidoc | 137 +++++++++++++ docs/reference/indices/get-mapping.asciidoc | 10 +- .../elasticsearch/action/ActionModule.java | 3 + .../mapping/get/GetFieldMappingsAction.java | 46 +++++ .../mapping/get/GetFieldMappingsRequest.java | 75 +++++++ .../get/GetFieldMappingsRequestBuilder.java | 52 +++++ .../mapping/get/GetFieldMappingsResponse.java | 156 +++++++++++++++ .../get/TransportGetFieldMappingsAction.java | 189 ++++++++++++++++++ .../client/IndicesAdminClient.java | 28 ++- .../support/AbstractIndicesAdminClient.java | 20 +- .../common/collect/MapBuilder.java | 4 + .../index/codec/CodecService.java | 10 +- .../DocValuesFormatService.java | 5 +- .../docvaluesformat/DocValuesFormats.java | 2 +- .../codec/postingsformat/PostingFormats.java | 2 +- .../postingsformat/PostingsFormatService.java | 9 +- .../index/mapper/FieldMapper.java | 2 +- .../mapper/core/AbstractFieldMapper.java | 82 +++++--- .../index/mapper/core/BinaryFieldMapper.java | 13 +- .../index/mapper/core/BooleanFieldMapper.java | 6 +- .../index/mapper/core/ByteFieldMapper.java | 11 +- .../index/mapper/core/DateFieldMapper.java | 28 ++- .../index/mapper/core/DoubleFieldMapper.java | 12 +- .../index/mapper/core/FloatFieldMapper.java | 12 +- .../index/mapper/core/IntegerFieldMapper.java | 14 +- .../index/mapper/core/LongFieldMapper.java | 11 +- .../index/mapper/core/NumberFieldMapper.java | 23 ++- .../index/mapper/core/ShortFieldMapper.java | 12 +- .../index/mapper/core/StringFieldMapper.java | 28 ++- .../index/mapper/geo/GeoShapeFieldMapper.java | 14 +- .../index/mapper/internal/AllFieldMapper.java | 59 ++++-- .../mapper/internal/BoostFieldMapper.java | 21 +- .../index/mapper/internal/IdFieldMapper.java | 41 +++- .../mapper/internal/IndexFieldMapper.java | 12 +- .../mapper/internal/RoutingFieldMapper.java | 14 +- .../mapper/internal/SizeFieldMapper.java | 10 +- .../mapper/internal/SourceFieldMapper.java | 19 +- .../index/mapper/internal/TTLFieldMapper.java | 8 +- .../mapper/internal/TimestampFieldMapper.java | 17 +- .../mapper/internal/TypeFieldMapper.java | 8 +- .../index/mapper/internal/UidFieldMapper.java | 24 ++- .../mapper/internal/VersionFieldMapper.java | 19 +- .../index/mapper/ip/IpFieldMapper.java | 12 +- .../mapper/multifield/MultiFieldMapper.java | 1 + .../index/similarity/Similarities.java | 6 +- .../similarity/SimilarityLookupService.java | 6 +- .../index/similarity/SimilarityService.java | 5 +- .../rest/action/RestActionModule.java | 2 + .../get/RestGetFieldMappingAction.java | 97 +++++++++ .../mapping/get/RestGetMappingAction.java | 2 - .../routing/RoutingTypeMapperTests.java | 10 +- .../timestamp/TimestampMappingTests.java | 6 +- .../mapping/SimpleGetFieldMappingsTests.java | 156 +++++++++++++++ 54 files changed, 1374 insertions(+), 201 deletions(-) create mode 100644 docs/reference/indices/get-field-mapping.asciidoc create mode 100644 src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsAction.java create mode 100644 src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsRequest.java create mode 100644 src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsRequestBuilder.java create mode 100644 src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java create mode 100644 src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java create mode 100644 src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetFieldMappingAction.java create mode 100644 src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsTests.java diff --git a/docs/reference/indices.asciidoc b/docs/reference/indices.asciidoc index c7274210fa3..70d801b8009 100644 --- a/docs/reference/indices.asciidoc +++ b/docs/reference/indices.asciidoc @@ -21,6 +21,8 @@ and warmers. == Mapping management: * <> +* <> +* <> * <> * <> @@ -68,6 +70,8 @@ include::indices/put-mapping.asciidoc[] include::indices/get-mapping.asciidoc[] +include::indices/get-field-mapping.asciidoc[] + include::indices/types-exists.asciidoc[] include::indices/delete-mapping.asciidoc[] diff --git a/docs/reference/indices/get-field-mapping.asciidoc b/docs/reference/indices/get-field-mapping.asciidoc new file mode 100644 index 00000000000..de6a28d60e2 --- /dev/null +++ b/docs/reference/indices/get-field-mapping.asciidoc @@ -0,0 +1,137 @@ +[[indices-get-field-mapping]] +== Get Field Mapping + +The get field mapping API allows you to retrieve mapping definitions for one or more fields. +This is useful when you do not need the complete type mapping returned by +the <> API. + +The following returns the mapping of the field `text` only: + +[source,js] +-------------------------------------------------- +curl -XGET 'http://localhost:9200/twitter/tweet/_mapping/field/text' +-------------------------------------------------- + +For which the response is (assuming `text` is a default string field): + +[source,js] +-------------------------------------------------- +{ + "twitter": { + "tweet": { + "text": { + "full_name": "text", + "mapping": { + "text": { "type": "string" } + } + } + } + } +} +-------------------------------------------------- + + + +[float] +=== Multiple Indices, Types and Fields + +The get field mapping API can be used to get the mapping of multiple fields from more than one index or type +with a single call. General usage of the API follows the +following syntax: `host:port/{index}/{type}/_mapping/field/{field}` where +`{index}`, `{type}` and `{field}` can stand for comma-separated list of names. To +get mappings for all indices you can use `_all` for `{index}`. The +following are some examples: + +[source,js] +-------------------------------------------------- +curl -XGET 'http://localhost:9200/twitter,kimchy/_mapping/field/message' + +curl -XGET 'http://localhost:9200/_all/tweet,book/_mapping/field/message,user.id' +-------------------------------------------------- + +[float] +=== Specifying fields + +The get mapping api allows you to specify fields using any of the following: + +[horizontal] +Full names:: the full path, including any parent object name the field is + part of (ex. `user.id`). +Index names:: the name of the lucene field (can be different than the + field name if the `index_name` option of the mapping is used). +Field names:: the name of the field without the path to it (ex. `id` for `{ "user" : { "id" : 1 } }`). + +The above options are specified in the order the `field` parameter is resolved. +The first field found which matches is returned. This is especially important +if index names or field names are used as those can be ambiguous. + +For example, consider the following mapping: + +[source,js] +-------------------------------------------------- + { + "article": { + "properties": { + "id": { "type": "string" }, + "title": { "type": "string", "index_name": "text" }, + "abstract": { "type": "string", "index_name": "text" }, + "author": { + "properties": { + "id": { "type": "string" }, + "name": { "type": "string", "index_name": "author" } + } + } + } + } + } +-------------------------------------------------- + +To select the `id` of the `author` field, you can use it's full name `author.id`. Using `text` will return +the mapping of `abstract` as it is one of the fields which map to the Lucene field `text`. `name` will return +the field `author.name`: + +[source,js] +-------------------------------------------------- +curl -XGET "http://localhost:9200/publications/article/_mapping/field/author.id,text,name" +-------------------------------------------------- + +returns: + +[source,js] +-------------------------------------------------- +{ + "publications": { + "article": { + "text": { + "full_name": "abstract", + "mapping": { + "abstract": { "type": "string", "index_name": "text" } + } + }, + "author.id": { + "full_name": "author.id", + "mapping": { + "id": { "type": "string" } + } + }, + "name": { + "full_name": "author.name", + "mapping": { + "name": { "type": "string", "index_name": "author" } + } + } + } + } +} +-------------------------------------------------- + +Note how the response always use the same fields specified in the request as keys. +The `full_name` in every entry contains the full name of the field whose mapping were returned. +This is useful when the request can refer to to multiple fields (like `text` above). + +[float] +=== Other options + +[horizontal] +include_defaults:: adding `include_defaults=true` to the query string will cause the response to +include default values, which are normally suppressed. diff --git a/docs/reference/indices/get-mapping.asciidoc b/docs/reference/indices/get-mapping.asciidoc index d084ffcfed0..a64b14df616 100644 --- a/docs/reference/indices/get-mapping.asciidoc +++ b/docs/reference/indices/get-mapping.asciidoc @@ -6,7 +6,7 @@ index/type. [source,js] -------------------------------------------------- -$ curl -XGET 'http://localhost:9200/twitter/tweet/_mapping' +curl -XGET 'http://localhost:9200/twitter/tweet/_mapping' -------------------------------------------------- [float] @@ -21,9 +21,9 @@ following are some examples: [source,js] -------------------------------------------------- -$ curl -XGET 'http://localhost:9200/twitter,kimchy/_mapping' +curl -XGET 'http://localhost:9200/twitter,kimchy/_mapping' -$ curl -XGET 'http://localhost:9200/_all/tweet,book/_mapping' +curl -XGET 'http://localhost:9200/_all/tweet,book/_mapping' -------------------------------------------------- If you want to get mappings of all indices and types then the following @@ -31,7 +31,7 @@ two examples are equivalent: [source,js] -------------------------------------------------- -$ curl -XGET 'http://localhost:9200/_all/_mapping' +curl -XGET 'http://localhost:9200/_all/_mapping' -$ curl -XGET 'http://localhost:9200/_mapping' +curl -XGET 'http://localhost:9200/_mapping' -------------------------------------------------- diff --git a/src/main/java/org/elasticsearch/action/ActionModule.java b/src/main/java/org/elasticsearch/action/ActionModule.java index 18b4c5967c0..a6b2aad4d81 100644 --- a/src/main/java/org/elasticsearch/action/ActionModule.java +++ b/src/main/java/org/elasticsearch/action/ActionModule.java @@ -68,7 +68,9 @@ import org.elasticsearch.action.admin.indices.gateway.snapshot.GatewaySnapshotAc import org.elasticsearch.action.admin.indices.gateway.snapshot.TransportGatewaySnapshotAction; import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingAction; import org.elasticsearch.action.admin.indices.mapping.delete.TransportDeleteMappingAction; +import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsAction; import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsAction; +import org.elasticsearch.action.admin.indices.mapping.get.TransportGetFieldMappingsAction; import org.elasticsearch.action.admin.indices.mapping.get.TransportGetMappingsAction; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; import org.elasticsearch.action.admin.indices.mapping.put.TransportPutMappingAction; @@ -200,6 +202,7 @@ public class ActionModule extends AbstractModule { registerAction(IndicesExistsAction.INSTANCE, TransportIndicesExistsAction.class); registerAction(TypesExistsAction.INSTANCE, TransportTypesExistsAction.class); registerAction(GetMappingsAction.INSTANCE, TransportGetMappingsAction.class); + registerAction(GetFieldMappingsAction.INSTANCE, TransportGetFieldMappingsAction.class); registerAction(PutMappingAction.INSTANCE, TransportPutMappingAction.class); registerAction(DeleteMappingAction.INSTANCE, TransportDeleteMappingAction.class); registerAction(IndicesAliasesAction.INSTANCE, TransportIndicesAliasesAction.class); diff --git a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsAction.java b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsAction.java new file mode 100644 index 00000000000..a3940c2a807 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsAction.java @@ -0,0 +1,46 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.admin.indices.mapping.get; + +import org.elasticsearch.action.admin.indices.IndicesAction; +import org.elasticsearch.client.IndicesAdminClient; +import org.elasticsearch.client.internal.InternalGenericClient; + +/** + */ +public class GetFieldMappingsAction extends IndicesAction { + + public static final GetFieldMappingsAction INSTANCE = new GetFieldMappingsAction(); + public static final String NAME = "mappings/fields/get"; + + private GetFieldMappingsAction() { + super(NAME); + } + + @Override + public GetFieldMappingsRequestBuilder newRequestBuilder(IndicesAdminClient client) { + return new GetFieldMappingsRequestBuilder((InternalGenericClient) client); + } + + @Override + public GetFieldMappingsResponse newResponse() { + return new GetFieldMappingsResponse(); + } +} diff --git a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsRequest.java b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsRequest.java new file mode 100644 index 00000000000..e8cbe9730c3 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsRequest.java @@ -0,0 +1,75 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.admin.indices.mapping.get; + +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.support.master.info.ClusterInfoRequest; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** Request the mappings of specific fields */ +public class GetFieldMappingsRequest extends ClusterInfoRequest { + + private String[] fields = Strings.EMPTY_ARRAY; + + private boolean includeDefaults = false; + + /** @param fields a list of fields to retrieve the mapping for */ + public GetFieldMappingsRequest fields(String... fields) { + this.fields = fields; + return this; + } + + public String[] fields() { + return fields; + } + + public boolean includeDefaults() { + return includeDefaults; + } + + /** Indicates whether default mapping settings should be returned */ + public GetFieldMappingsRequest includeDefaults(boolean includeDefaults) { + this.includeDefaults = includeDefaults; + return this; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeStringArray(fields); + out.writeBoolean(includeDefaults); + } + + @Override + public void readFrom(StreamInput in) throws IOException { + super.readFrom(in); + fields = in.readStringArray(); + includeDefaults = in.readBoolean(); + } +} diff --git a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsRequestBuilder.java b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsRequestBuilder.java new file mode 100644 index 00000000000..f2c61aa6e9c --- /dev/null +++ b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsRequestBuilder.java @@ -0,0 +1,52 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.admin.indices.mapping.get; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.master.info.ClusterInfoRequestBuilder; +import org.elasticsearch.client.IndicesAdminClient; +import org.elasticsearch.client.internal.InternalGenericClient; + +/** A helper class to build {@link GetFieldMappingsRequest} objects */ +public class GetFieldMappingsRequestBuilder extends ClusterInfoRequestBuilder { + + public GetFieldMappingsRequestBuilder(InternalGenericClient client, String... indices) { + super(client, new GetFieldMappingsRequest().indices(indices)); + } + + + /** Sets the fields to retrieve. */ + public GetFieldMappingsRequestBuilder setFields(String... fields) { + request.fields(fields); + return this; + } + + /** Indicates whether default mapping settings should be returned */ + public GetFieldMappingsRequestBuilder includeDefaults(boolean includeDefaults) { + request.includeDefaults(includeDefaults); + return this; + } + + + @Override + protected void doExecute(ActionListener listener) { + ((IndicesAdminClient) client).getFieldMappings(request, listener); + } +} diff --git a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java new file mode 100644 index 00000000000..d571768cec6 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java @@ -0,0 +1,156 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.admin.indices.mapping.get; + +import com.google.common.collect.ImmutableMap; +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.index.mapper.Mapper; + +import java.io.IOException; +import java.util.Map; + +/** Response object for {@link GetFieldMappingsRequest} API */ +public class GetFieldMappingsResponse extends ActionResponse implements ToXContent { + + private ImmutableMap>> mappings = ImmutableMap.of(); + + GetFieldMappingsResponse(ImmutableMap>> mappings) { + this.mappings = mappings; + } + + GetFieldMappingsResponse() { + } + + /** returns the retrieved field mapping. The return map keys are index, type, field (as specified in the request). */ + public ImmutableMap>> mappings() { + return mappings; + } + + /** + * Returns the mappings of a specific field. + * + * @param field field name as specified in the {@link GetFieldMappingsRequest} + * @return FieldMappingMetaData for the requested field or null if not found. + */ + public FieldMappingMetaData fieldMappings(String index, String type, String field) { + ImmutableMap> indexMapping = mappings.get(index); + if (indexMapping == null) { + return null; + } + ImmutableMap typeMapping = indexMapping.get(type); + if (typeMapping == null) { + return null; + } + return typeMapping.get(field); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + for (Map.Entry>> indexEntry : mappings.entrySet()) { + builder.startObject(indexEntry.getKey(), XContentBuilder.FieldCaseConversion.NONE); + for (Map.Entry> typeEntry : indexEntry.getValue().entrySet()) { + builder.startObject(typeEntry.getKey(), XContentBuilder.FieldCaseConversion.NONE); + for (Map.Entry fieldEntry : typeEntry.getValue().entrySet()) { + builder.startObject(fieldEntry.getKey()); + fieldEntry.getValue().toXContent(builder, params); + builder.endObject(); + } + builder.endObject(); + } + builder.endObject(); + } + return builder; + } + + public static class FieldMappingMetaData implements ToXContent { + private String fullName; + private BytesReference source; + + public FieldMappingMetaData(String fullName, BytesReference source) { + this.fullName = fullName; + this.source = source; + } + + public String fullName() { + return fullName; + } + + /** Returns the mappings as a map. Note that the returned map has a single key which is always the field's {@link Mapper#name}. */ + public Map sourceAsMap() { + return XContentHelper.convertToMap(source.array(), source.arrayOffset(), source.length(), true).v2(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.field("full_name", fullName); + XContentHelper.writeRawField("mapping", source, builder, params); + return builder; + } + } + + @Override + public void readFrom(StreamInput in) throws IOException { + super.readFrom(in); + int size = in.readVInt(); + ImmutableMap.Builder>> indexMapBuilder = ImmutableMap.builder(); + for (int i = 0; i < size; i++) { + String index = in.readString(); + int typesSize = in.readVInt(); + ImmutableMap.Builder> typeMapBuilder = ImmutableMap.builder(); + for (int j = 0; j < typesSize; j++) { + String type = in.readString(); + ImmutableMap.Builder fieldMapBuilder = ImmutableMap.builder(); + int fieldSize = in.readVInt(); + for (int k = 0; k < fieldSize; k++) { + fieldMapBuilder.put(in.readString(), new FieldMappingMetaData(in.readString(), in.readBytesReference())); + } + typeMapBuilder.put(type, fieldMapBuilder.build()); + } + indexMapBuilder.put(index, typeMapBuilder.build()); + } + mappings = indexMapBuilder.build(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeVInt(mappings.size()); + for (Map.Entry>> indexEntry : mappings.entrySet()) { + out.writeString(indexEntry.getKey()); + out.writeVInt(indexEntry.getValue().size()); + for (Map.Entry> typeEntry : indexEntry.getValue().entrySet()) { + out.writeString(typeEntry.getKey()); + out.writeVInt(typeEntry.getValue().size()); + for (Map.Entry fieldEntry : typeEntry.getValue().entrySet()) { + out.writeString(fieldEntry.getKey()); + FieldMappingMetaData fieldMapping = fieldEntry.getValue(); + out.writeString(fieldMapping.fullName()); + out.writeBytesReference(fieldMapping.source); + } + } + } + } +} diff --git a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java new file mode 100644 index 00000000000..d37a446abe4 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java @@ -0,0 +1,189 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.admin.indices.mapping.get; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import org.elasticsearch.ElasticSearchException; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse.FieldMappingMetaData; +import org.elasticsearch.action.support.master.info.TransportClusterInfoAction; +import org.elasticsearch.cluster.ClusterService; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.common.collect.MapBuilder; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.regex.Regex; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.mapper.DocumentMapper; +import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.service.IndexService; +import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; + +import java.io.IOException; +import java.util.Collection; + +/** + */ +public class TransportGetFieldMappingsAction extends TransportClusterInfoAction { + + private final IndicesService indicesService; + + @Inject + public TransportGetFieldMappingsAction(Settings settings, TransportService transportService, ClusterService clusterService, + IndicesService indicesService, ThreadPool threadPool) { + super(settings, transportService, clusterService, threadPool); + this.indicesService = indicesService; + } + + @Override + protected String transportAction() { + return GetFieldMappingsAction.NAME; + } + + @Override + protected GetFieldMappingsRequest newRequest() { + return new GetFieldMappingsRequest(); + } + + @Override + protected GetFieldMappingsResponse newResponse() { + return new GetFieldMappingsResponse(); + } + + @Override + protected void doMasterOperation(final GetFieldMappingsRequest request, final ClusterState state, final ActionListener listener) throws ElasticSearchException { + + listener.onResponse(new GetFieldMappingsResponse(findMappings(request.indices(), request.types(), request.fields(), request.includeDefaults()))); + } + + private ImmutableMap>> findMappings(String[] concreteIndices, + final String[] types, + final String[] fields, + boolean includeDefaults) { + assert types != null; + assert concreteIndices != null; + if (concreteIndices.length == 0) { + return ImmutableMap.of(); + } + + ImmutableMap.Builder>> indexMapBuilder = ImmutableMap.builder(); + Sets.SetView intersection = Sets.intersection(Sets.newHashSet(concreteIndices), indicesService.indices()); + for (String index : intersection) { + IndexService indexService = indicesService.indexService(index); + Collection typeIntersection; + if (types.length == 0) { + typeIntersection = indexService.mapperService().types(); + + } else { + typeIntersection = Collections2.filter(indexService.mapperService().types(), new Predicate() { + + @Override + public boolean apply(String type) { + return Regex.simpleMatch(types, type); + } + + }); + } + + MapBuilder> typeMappings = new MapBuilder>(); + for (String type : typeIntersection) { + DocumentMapper documentMapper = indexService.mapperService().documentMapper(type); + ImmutableMap fieldMapping = findFieldMappingsByType(documentMapper, fields, includeDefaults); + if (!fieldMapping.isEmpty()) { + typeMappings.put(type, fieldMapping); + } + } + + if (!typeMappings.isEmpty()) { + indexMapBuilder.put(index, typeMappings.immutableMap()); + } + } + + return indexMapBuilder.build(); + } + + private static final ToXContent.Params includeDefaultsParams = new ToXContent.Params() { + + final static String INCLUDE_DEFAULTS = "include_defaults"; + + @Override + public String param(String key) { + if (INCLUDE_DEFAULTS.equals(key)) { + return "true"; + } + return null; + } + + @Override + public String param(String key, String defaultValue) { + if (INCLUDE_DEFAULTS.equals(key)) { + return "true"; + } + return defaultValue; + } + + @Override + public boolean paramAsBoolean(String key, boolean defaultValue) { + if (INCLUDE_DEFAULTS.equals(key)) { + return true; + } + return defaultValue; + } + + @Override + public Boolean paramAsBooleanOptional(String key, Boolean defaultValue) { + if (INCLUDE_DEFAULTS.equals(key)) { + return true; + } + return defaultValue; + + } + }; + + private ImmutableMap findFieldMappingsByType(DocumentMapper documentMapper, String[] fields, + boolean includeDefaults) throws ElasticSearchException { + MapBuilder fieldMappings = new MapBuilder(); + for (String field : fields) { + FieldMapper fieldMapper = documentMapper.mappers().smartNameFieldMapper(field); + if (fieldMapper != null) { + try { + XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + builder.startObject(); + fieldMapper.toXContent(builder, includeDefaults ? includeDefaultsParams : ToXContent.EMPTY_PARAMS); + builder.endObject(); + fieldMappings.put(field, new FieldMappingMetaData(fieldMapper.names().fullName(), builder.bytes())); + } catch (IOException e) { + throw new ElasticSearchException("failed to serialize XContent of field [" + field + "]", e); + } + } + } + return fieldMappings.immutableMap(); + } + + +} \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/client/IndicesAdminClient.java b/src/main/java/org/elasticsearch/client/IndicesAdminClient.java index 5df537e97bc..d77f63feff7 100644 --- a/src/main/java/org/elasticsearch/client/IndicesAdminClient.java +++ b/src/main/java/org/elasticsearch/client/IndicesAdminClient.java @@ -59,9 +59,7 @@ import org.elasticsearch.action.admin.indices.gateway.snapshot.GatewaySnapshotRe import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingRequest; import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingRequestBuilder; import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingResponse; -import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest; -import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequestBuilder; -import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; +import org.elasticsearch.action.admin.indices.mapping.get.*; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse; @@ -398,12 +396,36 @@ public interface IndicesAdminClient { */ OptimizeRequestBuilder prepareOptimize(String... indices); + /** + * Get the complete mappings of one or more types + */ void getMappings(GetMappingsRequest request, ActionListener listener); + /** + * Get the complete mappings of one or more types + */ ActionFuture getMappings(GetMappingsRequest request); + /** + * Get the complete mappings of one or more types + */ GetMappingsRequestBuilder prepareGetMappings(String... indices); + /** + * Get the mappings of specific fields + */ + void getFieldMappings(GetFieldMappingsRequest request, ActionListener listener); + + /** + * Get the mappings of specific fields + */ + GetFieldMappingsRequestBuilder prepareGetFieldMappings(String... indices); + + /** + * Get the mappings of specific fields + */ + ActionFuture getFieldMappings(GetFieldMappingsRequest request); + /** * Add mapping definition for a type into one or more indices. * diff --git a/src/main/java/org/elasticsearch/client/support/AbstractIndicesAdminClient.java b/src/main/java/org/elasticsearch/client/support/AbstractIndicesAdminClient.java index 04ce96c45bd..152900545bc 100644 --- a/src/main/java/org/elasticsearch/client/support/AbstractIndicesAdminClient.java +++ b/src/main/java/org/elasticsearch/client/support/AbstractIndicesAdminClient.java @@ -72,10 +72,7 @@ import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingAction import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingRequest; import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingRequestBuilder; import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingResponse; -import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsAction; -import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest; -import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequestBuilder; -import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; +import org.elasticsearch.action.admin.indices.mapping.get.*; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder; @@ -334,6 +331,11 @@ public abstract class AbstractIndicesAdminClient implements InternalIndicesAdmin execute(GetMappingsAction.INSTANCE, request, listener); } + @Override + public void getFieldMappings(GetFieldMappingsRequest request, ActionListener listener) { + execute(GetFieldMappingsAction.INSTANCE, request, listener); + } + @Override public GetMappingsRequestBuilder prepareGetMappings(String... indices) { return new GetMappingsRequestBuilder(this, indices); @@ -344,6 +346,16 @@ public abstract class AbstractIndicesAdminClient implements InternalIndicesAdmin return execute(GetMappingsAction.INSTANCE, request); } + @Override + public GetFieldMappingsRequestBuilder prepareGetFieldMappings(String... indices) { + return new GetFieldMappingsRequestBuilder(this, indices); + } + + @Override + public ActionFuture getFieldMappings(GetFieldMappingsRequest request) { + return execute(GetFieldMappingsAction.INSTANCE, request); + } + @Override public ActionFuture putMapping(final PutMappingRequest request) { return execute(PutMappingAction.INSTANCE, request); diff --git a/src/main/java/org/elasticsearch/common/collect/MapBuilder.java b/src/main/java/org/elasticsearch/common/collect/MapBuilder.java index 6d107c13147..428e67f6f4c 100644 --- a/src/main/java/org/elasticsearch/common/collect/MapBuilder.java +++ b/src/main/java/org/elasticsearch/common/collect/MapBuilder.java @@ -76,6 +76,10 @@ public class MapBuilder { return map.containsKey(key); } + public boolean isEmpty() { + return map.isEmpty(); + } + public Map map() { return this.map; } diff --git a/src/main/java/org/elasticsearch/index/codec/CodecService.java b/src/main/java/org/elasticsearch/index/codec/CodecService.java index 2178a7bb275..5876e9bd61b 100644 --- a/src/main/java/org/elasticsearch/index/codec/CodecService.java +++ b/src/main/java/org/elasticsearch/index/codec/CodecService.java @@ -38,7 +38,7 @@ import org.elasticsearch.index.settings.IndexSettings; * codec layer that allows to use use-case specific file formats & * data-structures per field. ElasticSearch exposes the full * {@link Codec} capabilities through this {@link CodecService}. - * + * * @see PostingsFormatService * @see DocValuesFormatService */ @@ -49,6 +49,8 @@ public class CodecService extends AbstractIndexComponent { private final MapperService mapperService; private final ImmutableMap codecs; + public final static String DEFAULT_CODEC = "default"; + public CodecService(Index index) { this(index, ImmutableSettings.Builder.EMPTY_SETTINGS); } @@ -66,9 +68,11 @@ public class CodecService extends AbstractIndexComponent { this.mapperService = mapperService; MapBuilder codecs = MapBuilder.newMapBuilder(); if (mapperService == null) { - codecs.put("default", Codec.getDefault()); + codecs.put(DEFAULT_CODEC, Codec.getDefault()); } else { - codecs.put("default", new PerFieldMappingPostingFormatCodec(mapperService, postingsFormatService.get("default").get(), docValuesFormatService.get("default").get(), logger)); + codecs.put(DEFAULT_CODEC, new PerFieldMappingPostingFormatCodec(mapperService, + postingsFormatService.get(PostingsFormatService.DEFAULT_FORMAT).get(), + docValuesFormatService.get(DocValuesFormatService.DEFAULT_FORMAT).get(), logger)); } for (String codec : Codec.availableCodecs()) { codecs.put(codec, Codec.forName(codec)); diff --git a/src/main/java/org/elasticsearch/index/codec/docvaluesformat/DocValuesFormatService.java b/src/main/java/org/elasticsearch/index/codec/docvaluesformat/DocValuesFormatService.java index 435cfe06995..a014b228b59 100644 --- a/src/main/java/org/elasticsearch/index/codec/docvaluesformat/DocValuesFormatService.java +++ b/src/main/java/org/elasticsearch/index/codec/docvaluesformat/DocValuesFormatService.java @@ -36,14 +36,15 @@ import java.util.Map; * The {@link DocValuesFormatService} provides access to * all configured {@link DocValuesFormatProvider} instances by * {@link DocValuesFormatProvider#name() name}. - * + * * @see CodecService - * */ public class DocValuesFormatService extends AbstractIndexComponent { private final ImmutableMap providers; + public final static String DEFAULT_FORMAT = "default"; + public DocValuesFormatService(Index index) { this(index, ImmutableSettings.Builder.EMPTY_SETTINGS); } diff --git a/src/main/java/org/elasticsearch/index/codec/docvaluesformat/DocValuesFormats.java b/src/main/java/org/elasticsearch/index/codec/docvaluesformat/DocValuesFormats.java index ddb115ad581..85a70c8a11e 100644 --- a/src/main/java/org/elasticsearch/index/codec/docvaluesformat/DocValuesFormats.java +++ b/src/main/java/org/elasticsearch/index/codec/docvaluesformat/DocValuesFormats.java @@ -38,7 +38,7 @@ public class DocValuesFormats { builtInDocValuesFormatsX.put(name, new PreBuiltDocValuesFormatProvider.Factory(DocValuesFormat.forName(name))); } // LUCENE UPGRADE: update those DVF if necessary - builtInDocValuesFormatsX.put("default", new PreBuiltDocValuesFormatProvider.Factory("default", DocValuesFormat.forName("Lucene45"))); + builtInDocValuesFormatsX.put(DocValuesFormatService.DEFAULT_FORMAT, new PreBuiltDocValuesFormatProvider.Factory(DocValuesFormatService.DEFAULT_FORMAT, DocValuesFormat.forName("Lucene45"))); builtInDocValuesFormatsX.put("memory", new PreBuiltDocValuesFormatProvider.Factory("memory", DocValuesFormat.forName("Memory"))); builtInDocValuesFormatsX.put("disk", new PreBuiltDocValuesFormatProvider.Factory("disk", DocValuesFormat.forName("Disk"))); builtInDocValuesFormats = builtInDocValuesFormatsX.immutableMap(); diff --git a/src/main/java/org/elasticsearch/index/codec/postingsformat/PostingFormats.java b/src/main/java/org/elasticsearch/index/codec/postingsformat/PostingFormats.java index 9016374d891..4459dfeb67b 100644 --- a/src/main/java/org/elasticsearch/index/codec/postingsformat/PostingFormats.java +++ b/src/main/java/org/elasticsearch/index/codec/postingsformat/PostingFormats.java @@ -71,7 +71,7 @@ public class PostingFormats { buildInPostingFormatsX.put("memory", new PreBuiltPostingsFormatProvider.Factory("memory", PostingsFormat.forName("Memory"))); // LUCENE UPGRADE: Need to change this to the relevant ones on a lucene upgrade buildInPostingFormatsX.put("pulsing", new PreBuiltPostingsFormatProvider.Factory("pulsing", PostingsFormat.forName("Pulsing41"))); - buildInPostingFormatsX.put("default", new PreBuiltPostingsFormatProvider.Factory("default", defaultFormat)); + buildInPostingFormatsX.put(PostingsFormatService.DEFAULT_FORMAT, new PreBuiltPostingsFormatProvider.Factory(PostingsFormatService.DEFAULT_FORMAT, defaultFormat)); buildInPostingFormatsX.put("bloom_pulsing", new PreBuiltPostingsFormatProvider.Factory("bloom_pulsing", wrapInBloom(PostingsFormat.forName("Pulsing41")))); buildInPostingFormatsX.put("bloom_default", new PreBuiltPostingsFormatProvider.Factory("bloom_default", wrapInBloom(PostingsFormat.forName("Lucene41")))); diff --git a/src/main/java/org/elasticsearch/index/codec/postingsformat/PostingsFormatService.java b/src/main/java/org/elasticsearch/index/codec/postingsformat/PostingsFormatService.java index d0189733596..31841ec624c 100644 --- a/src/main/java/org/elasticsearch/index/codec/postingsformat/PostingsFormatService.java +++ b/src/main/java/org/elasticsearch/index/codec/postingsformat/PostingsFormatService.java @@ -35,15 +35,16 @@ import java.util.Map; /** * The {@link PostingsFormatService} provides access to * all configured {@link PostingsFormatProvider} instances by - * {@link PostingsFormatProvider#name() name}. - * - * @see CodecService - * + * {@link PostingsFormatProvider#name() name}. + * + * @see CodecService */ public class PostingsFormatService extends AbstractIndexComponent { private final ImmutableMap providers; + public final static String DEFAULT_FORMAT = "default"; + public PostingsFormatService(Index index) { this(index, ImmutableSettings.Builder.EMPTY_SETTINGS); } diff --git a/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java index 62945e4e95b..4036a07c32a 100644 --- a/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java @@ -38,7 +38,7 @@ import java.util.List; /** * */ -public interface FieldMapper { +public interface FieldMapper extends Mapper { public static final String DOC_VALUES_FORMAT = "doc_values_format"; diff --git a/src/main/java/org/elasticsearch/index/mapper/core/AbstractFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/AbstractFieldMapper.java index 03537c87f0c..1a4907b741d 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/AbstractFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/AbstractFieldMapper.java @@ -39,11 +39,14 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatProvider; +import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatService; import org.elasticsearch.index.codec.postingsformat.PostingFormats; import org.elasticsearch.index.codec.postingsformat.PostingsFormatProvider; +import org.elasticsearch.index.codec.postingsformat.PostingsFormatService; import org.elasticsearch.index.fielddata.FieldDataType; import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.similarity.SimilarityLookupService; import org.elasticsearch.index.similarity.SimilarityProvider; import java.io.IOException; @@ -54,7 +57,7 @@ import java.util.Map; /** * */ -public abstract class AbstractFieldMapper implements FieldMapper, Mapper { +public abstract class AbstractFieldMapper implements FieldMapper { public static class Defaults { public static final FieldType FIELD_TYPE = new FieldType(); @@ -350,9 +353,7 @@ public abstract class AbstractFieldMapper implements FieldMapper, Mapper { /** Parse the field value and populate fields. */ protected abstract void parseCreateField(ParseContext context, List fields) throws IOException; - /** - * Derived classes can override it to specify that boost value is set by derived classes. - */ + /** Derived classes can override it to specify that boost value is set by derived classes. */ protected boolean customBoost() { return false; } @@ -550,68 +551,103 @@ public abstract class AbstractFieldMapper implements FieldMapper, Mapper { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(names.name()); - doXContentBody(builder); - builder.endObject(); - return builder; + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + doXContentBody(builder, includeDefaults, params); + return builder.endObject(); } - protected void doXContentBody(XContentBuilder builder) throws IOException { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + builder.field("type", contentType()); - if (!names.name().equals(names.indexNameClean())) { + if (includeDefaults || !names.name().equals(names.indexNameClean())) { builder.field("index_name", names.indexNameClean()); } - if (boost != 1.0f) { + if (includeDefaults || boost != 1.0f) { builder.field("boost", boost); } FieldType defaultFieldType = defaultFieldType(); - if (fieldType.indexed() != defaultFieldType.indexed() || + if (includeDefaults || fieldType.indexed() != defaultFieldType.indexed() || fieldType.tokenized() != defaultFieldType.tokenized()) { builder.field("index", indexTokenizeOptionToString(fieldType.indexed(), fieldType.tokenized())); } - if (fieldType.stored() != defaultFieldType.stored()) { + if (includeDefaults || fieldType.stored() != defaultFieldType.stored()) { builder.field("store", fieldType.stored()); } - if (fieldType.storeTermVectors() != defaultFieldType.storeTermVectors()) { + if (includeDefaults || fieldType.storeTermVectors() != defaultFieldType.storeTermVectors()) { builder.field("term_vector", termVectorOptionsToString(fieldType)); } - if (fieldType.omitNorms() != defaultFieldType.omitNorms()) { + if (includeDefaults || fieldType.omitNorms() != defaultFieldType.omitNorms()) { builder.field("omit_norms", fieldType.omitNorms()); } - if (fieldType.indexOptions() != defaultFieldType.indexOptions()) { + if (includeDefaults || fieldType.indexOptions() != defaultFieldType.indexOptions()) { builder.field("index_options", indexOptionToString(fieldType.indexOptions())); } - if (indexAnalyzer != null && searchAnalyzer != null && indexAnalyzer.name().equals(searchAnalyzer.name()) && !indexAnalyzer.name().startsWith("_") && !indexAnalyzer.name().equals("default")) { - // same analyzers, output it once - builder.field("analyzer", indexAnalyzer.name()); - } else { - if (indexAnalyzer != null && !indexAnalyzer.name().startsWith("_") && !indexAnalyzer.name().equals("default")) { + if (indexAnalyzer == null && searchAnalyzer == null) { + if (includeDefaults) { + builder.field("analyzer", "default"); + } + } else if (indexAnalyzer == null) { + // searchAnalyzer != null + if (includeDefaults || (!searchAnalyzer.name().startsWith("_") && !searchAnalyzer.name().equals("default"))) { + builder.field("search_analyzer", searchAnalyzer.name()); + } + } else if (searchAnalyzer == null) { + // indexAnalyzer != null + if (includeDefaults || (!indexAnalyzer.name().startsWith("_") && !indexAnalyzer.name().equals("default"))) { builder.field("index_analyzer", indexAnalyzer.name()); } - if (searchAnalyzer != null && !searchAnalyzer.name().startsWith("_") && !searchAnalyzer.name().equals("default")) { + } else if (indexAnalyzer.name().equals(searchAnalyzer.name())) { + // indexAnalyzer == searchAnalyzer + if (includeDefaults || (!indexAnalyzer.name().startsWith("_") && !indexAnalyzer.name().equals("default"))) { + builder.field("analyzer", indexAnalyzer.name()); + } + } else { + // both are there but different + if (includeDefaults || (!indexAnalyzer.name().startsWith("_") && !indexAnalyzer.name().equals("default"))) { + builder.field("index_analyzer", indexAnalyzer.name()); + } + if (includeDefaults || (!searchAnalyzer.name().startsWith("_") && !searchAnalyzer.name().equals("default"))) { builder.field("search_analyzer", searchAnalyzer.name()); } } + if (postingsFormat != null) { - if (!postingsFormat.name().equals(defaultPostingFormat())) { + if (includeDefaults || !postingsFormat.name().equals(defaultPostingFormat())) { builder.field("postings_format", postingsFormat.name()); } + } else if (includeDefaults) { + String format = defaultPostingFormat(); + if (format == null) { + format = PostingsFormatService.DEFAULT_FORMAT; + } + builder.field("postings_format", format); } if (docValuesFormat != null) { - if (!docValuesFormat.name().equals(defaultDocValuesFormat())) { + if (includeDefaults || !docValuesFormat.name().equals(defaultDocValuesFormat())) { builder.field(DOC_VALUES_FORMAT, docValuesFormat.name()); } + } else if (includeDefaults) { + String format = defaultDocValuesFormat(); + if (format == null) { + format = DocValuesFormatService.DEFAULT_FORMAT; + } + builder.field(DOC_VALUES_FORMAT, format); } if (similarity() != null) { builder.field("similarity", similarity().name()); + } else if (includeDefaults) { + builder.field("similariry", SimilarityLookupService.DEFAULT_SIMILARITY); } if (customFieldDataSettings != null) { builder.field("fielddata", (Map) customFieldDataSettings.getAsMap()); + } else if (includeDefaults) { + builder.field("fielddata", (Map) fieldDataType.getSettings().getAsMap()); } } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/BinaryFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/BinaryFieldMapper.java index f3432e702ca..577f431801e 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/BinaryFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/BinaryFieldMapper.java @@ -200,23 +200,24 @@ public class BinaryFieldMapper extends AbstractFieldMapper { } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(names.name()); + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { builder.field("type", contentType()); - if (!names.name().equals(names.indexNameClean())) { + if (includeDefaults || !names.name().equals(names.indexNameClean())) { builder.field("index_name", names.indexNameClean()); } if (compress != null) { builder.field("compress", compress); + } else if (includeDefaults) { + builder.field("compress", false); } if (compressThreshold != -1) { builder.field("compress_threshold", new ByteSizeValue(compressThreshold).toString()); + } else if (includeDefaults) { + builder.field("compress_threshold", -1); } - if (fieldType.stored() != defaultFieldType().stored()) { + if (includeDefaults || fieldType.stored() != defaultFieldType().stored()) { builder.field("store", fieldType.stored()); } - builder.endObject(); - return builder; } @Override diff --git a/src/main/java/org/elasticsearch/index/mapper/core/BooleanFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/BooleanFieldMapper.java index 378a666e7dc..2a0c4aefb32 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/BooleanFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/BooleanFieldMapper.java @@ -227,9 +227,9 @@ public class BooleanFieldMapper extends AbstractFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (nullValue != null) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/ByteFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/ByteFieldMapper.java index caa85079492..64742a0ab81 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/ByteFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/ByteFieldMapper.java @@ -346,16 +346,19 @@ public class ByteFieldMapper extends NumberFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (precisionStep != Defaults.PRECISION_STEP) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || precisionStep != Defaults.PRECISION_STEP) { builder.field("precision_step", precisionStep); } - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java index 5b41a12904d..2e738ba2cf0 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java @@ -244,9 +244,7 @@ public class DateFieldMapper extends NumberFieldMapper { return parseStringValue(value.toString()); } - /** - * Dates should return as a string. - */ + /** Dates should return as a string. */ @Override public Object valueForSearch(Object value) { if (value instanceof String) { @@ -306,11 +304,11 @@ public class DateFieldMapper extends NumberFieldMapper { return NumericRangeQuery.newLongRange(names.indexName(), precisionStep, lValue, lValue, true, true); } - + public long parseToMilliseconds(Object value, @Nullable QueryParseContext context) { return parseToMilliseconds(value, context, false); } - + public long parseToMilliseconds(Object value, @Nullable QueryParseContext context, boolean includeUpper) { long now = context == null ? System.currentTimeMillis() : context.nowInMillis(); return includeUpper && roundCeil ? dateMathParser.parseRoundCeil(convertToString(value), now) : dateMathParser.parse(convertToString(value), now); @@ -450,24 +448,34 @@ public class DateFieldMapper extends NumberFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (precisionStep != Defaults.PRECISION_STEP) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || precisionStep != Defaults.PRECISION_STEP) { builder.field("precision_step", precisionStep); } builder.field("format", dateTimeFormatter.format()); - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } - if (timeUnit != Defaults.TIME_UNIT) { + + if (includeDefaults || timeUnit != Defaults.TIME_UNIT) { builder.field("numeric_resolution", timeUnit.name().toLowerCase(Locale.ROOT)); } // only serialize locale if needed, ROOT is the default, so no need to serialize that case as well... if (dateTimeFormatter.locale() != null && dateTimeFormatter.locale() != Locale.ROOT) { builder.field("locale", dateTimeFormatter.locale()); + } else if (includeDefaults) { + if (dateTimeFormatter.locale() == null) { + builder.field("locale", Locale.ROOT); + } else { + builder.field("locale", dateTimeFormatter.locale()); + } } } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java index 746404e04d0..f26e804f6ab 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java @@ -342,17 +342,21 @@ public class DoubleFieldMapper extends NumberFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (precisionStep != Defaults.PRECISION_STEP) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || precisionStep != Defaults.PRECISION_STEP) { builder.field("precision_step", precisionStep); } - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } + } public static class CustomDoubleNumericField extends CustomNumericField { diff --git a/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java index 6e3fe3b6564..8ad6ec4b083 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java @@ -339,17 +339,21 @@ public class FloatFieldMapper extends NumberFieldMapper { @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (precisionStep != Defaults.PRECISION_STEP) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || precisionStep != Defaults.PRECISION_STEP) { builder.field("precision_step", precisionStep); } - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } + } public static class CustomFloatNumericField extends CustomNumericField { diff --git a/src/main/java/org/elasticsearch/index/mapper/core/IntegerFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/IntegerFieldMapper.java index d08baf6b2d7..451ae4a94f4 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/IntegerFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/IntegerFieldMapper.java @@ -125,7 +125,7 @@ public class IntegerFieldMapper extends NumberFieldMapper { super(names, precisionStep, boost, fieldType, ignoreMalformed, NumericIntegerAnalyzer.buildNamedAnalyzer(precisionStep), NumericIntegerAnalyzer.buildNamedAnalyzer(Integer.MAX_VALUE), postingsProvider, docValuesProvider, similarity, fieldDataSettings, indexSettings); - this.nullValue = nullValue; + this.nullValue = nullValue; this.nullValueAsString = nullValue == null ? null : nullValue.toString(); } @@ -344,17 +344,21 @@ public class IntegerFieldMapper extends NumberFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (precisionStep != Defaults.PRECISION_STEP) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || precisionStep != Defaults.PRECISION_STEP) { builder.field("precision_step", precisionStep); } - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } + } public static class CustomIntegerNumericField extends CustomNumericField { diff --git a/src/main/java/org/elasticsearch/index/mapper/core/LongFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/LongFieldMapper.java index 852b8fccca6..34431b6cd5f 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/LongFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/LongFieldMapper.java @@ -340,16 +340,19 @@ public class LongFieldMapper extends NumberFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (precisionStep != Defaults.PRECISION_STEP) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || precisionStep != Defaults.PRECISION_STEP) { builder.field("precision_step", precisionStep); } - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/NumberFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/NumberFieldMapper.java index 53fb48106b2..86e2a269ce2 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/NumberFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/NumberFieldMapper.java @@ -182,26 +182,34 @@ public abstract class NumberFieldMapper extends AbstractFieldM } } - /** Utility method to convert a long to a doc values field using {@link NumericUtils} encoding. */ + /** + * Utility method to convert a long to a doc values field using {@link NumericUtils} encoding. + */ protected final Field toDocValues(long l) { final BytesRef bytes = new BytesRef(); NumericUtils.longToPrefixCoded(l, 0, bytes); return new SortedSetDocValuesField(names().indexName(), bytes); } - /** Utility method to convert an int to a doc values field using {@link NumericUtils} encoding. */ + /** + * Utility method to convert an int to a doc values field using {@link NumericUtils} encoding. + */ protected final Field toDocValues(int i) { final BytesRef bytes = new BytesRef(); NumericUtils.intToPrefixCoded(i, 0, bytes); return new SortedSetDocValuesField(names().indexName(), bytes); } - /** Utility method to convert a float to a doc values field using {@link NumericUtils} encoding. */ + /** + * Utility method to convert a float to a doc values field using {@link NumericUtils} encoding. + */ protected final Field toDocValues(float f) { return toDocValues(NumericUtils.floatToSortableInt(f)); } - /** Utility method to convert a double to a doc values field using {@link NumericUtils} encoding. */ + /** + * Utility method to convert a double to a doc values field using {@link NumericUtils} encoding. + */ protected final Field toDocValues(double d) { return toDocValues(NumericUtils.doubleToSortableLong(d)); } @@ -318,9 +326,10 @@ public abstract class NumberFieldMapper extends AbstractFieldM } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (ignoreMalformed.explicit()) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || ignoreMalformed.explicit()) { builder.field("ignore_malformed", ignoreMalformed.value()); } } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/ShortFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/ShortFieldMapper.java index 9e133570ee0..78dde8d462a 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/ShortFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/ShortFieldMapper.java @@ -345,17 +345,21 @@ public class ShortFieldMapper extends NumberFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (precisionStep != Defaults.PRECISION_STEP) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || precisionStep != Defaults.PRECISION_STEP) { builder.field("precision_step", precisionStep); } - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } + } public static class CustomShortNumericField extends CustomNumericField { diff --git a/src/main/java/org/elasticsearch/index/mapper/core/StringFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/StringFieldMapper.java index be2cff6444e..61cbdab4ef3 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/StringFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/StringFieldMapper.java @@ -329,28 +329,36 @@ public class StringFieldMapper extends AbstractFieldMapper implements Al } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (nullValue != null) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } - if (positionOffsetGap != Defaults.POSITION_OFFSET_GAP) { + + if (includeDefaults || positionOffsetGap != Defaults.POSITION_OFFSET_GAP) { builder.field("position_offset_gap", positionOffsetGap); } if (searchQuotedAnalyzer != null && searchAnalyzer != searchQuotedAnalyzer) { builder.field("search_quote_analyzer", searchQuotedAnalyzer.name()); + } else if (includeDefaults) { + if (searchQuotedAnalyzer == null) { + builder.field("search_quote_analyzer", "default"); + } else { + builder.field("search_quote_analyzer", searchQuotedAnalyzer.name()); + } } - if (ignoreAbove != Defaults.IGNORE_ABOVE) { + if (includeDefaults || ignoreAbove != Defaults.IGNORE_ABOVE) { builder.field("ignore_above", ignoreAbove); } } - /** - * Extension of {@link Field} supporting reuse of a cached TokenStream for not-tokenized values. - */ + /** Extension of {@link Field} supporting reuse of a cached TokenStream for not-tokenized values. */ static class StringField extends Field { public StringField(String name, String value, FieldType fieldType) { @@ -394,9 +402,7 @@ public class StringFieldMapper extends AbstractFieldMapper implements Al StringTokenStream() { } - /** - * Sets the string value. - */ + /** Sets the string value. */ StringTokenStream setValue(String value) { this.value = value; return this; diff --git a/src/main/java/org/elasticsearch/index/mapper/geo/GeoShapeFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/geo/GeoShapeFieldMapper.java index e73c4ad8e5b..5cba94f07ef 100644 --- a/src/main/java/org/elasticsearch/index/mapper/geo/GeoShapeFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/geo/GeoShapeFieldMapper.java @@ -152,7 +152,7 @@ public class GeoShapeFieldMapper extends AbstractFieldMapper { return new GeoShapeFieldMapper(names, prefixTree, strategyName, distanceErrorPct, fieldType, postingsProvider, docValuesProvider); } } - + private static final int getLevels(int treeLevels, double precisionInMeters, int defaultLevels, boolean geoHash) { if (treeLevels > 0 || precisionInMeters >= 0) { return Math.max(treeLevels, precisionInMeters >= 0 ? (geoHash ? GeoUtils.geoHashLevelsForPrecision(precisionInMeters) @@ -161,7 +161,7 @@ public class GeoShapeFieldMapper extends AbstractFieldMapper { return defaultLevels; } - + public static class TypeParser implements Mapper.TypeParser { @Override @@ -220,7 +220,7 @@ public class GeoShapeFieldMapper extends AbstractFieldMapper { public void parse(ParseContext context) throws IOException { try { ShapeBuilder shape = ShapeBuilder.parse(context.parser()); - if(shape == null) { + if (shape == null) { return; } Field[] fields = defaultStrategy.createIndexableFields(shape.build()); @@ -245,24 +245,24 @@ public class GeoShapeFieldMapper extends AbstractFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { builder.field("type", contentType()); // TODO: Come up with a better way to get the name, maybe pass it from builder if (defaultStrategy.getGrid() instanceof GeohashPrefixTree) { // Don't emit the tree name since GeohashPrefixTree is the default // Only emit the tree levels if it isn't the default value - if (defaultStrategy.getGrid().getMaxLevels() != Defaults.GEOHASH_LEVELS) { + if (includeDefaults || defaultStrategy.getGrid().getMaxLevels() != Defaults.GEOHASH_LEVELS) { builder.field(Names.TREE_LEVELS, defaultStrategy.getGrid().getMaxLevels()); } } else { builder.field(Names.TREE, Names.TREE_QUADTREE); - if (defaultStrategy.getGrid().getMaxLevels() != Defaults.QUADTREE_LEVELS) { + if (includeDefaults || defaultStrategy.getGrid().getMaxLevels() != Defaults.QUADTREE_LEVELS) { builder.field(Names.TREE_LEVELS, defaultStrategy.getGrid().getMaxLevels()); } } - if (defaultStrategy.getDistErrPct() != Defaults.DISTANCE_ERROR_PCT) { + if (includeDefaults || defaultStrategy.getDistErrPct() != Defaults.DISTANCE_ERROR_PCT) { builder.field(Names.DISTANCE_ERROR_PCT, defaultStrategy.getDistErrPct()); } } diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/AllFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/AllFieldMapper.java index af470aef3b9..620591cad66 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/AllFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/AllFieldMapper.java @@ -41,6 +41,7 @@ import org.elasticsearch.index.fielddata.FieldDataType; import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.core.AbstractFieldMapper; import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.similarity.SimilarityLookupService; import org.elasticsearch.index.similarity.SimilarityProvider; import java.io.IOException; @@ -145,7 +146,7 @@ public class AllFieldMapper extends AbstractFieldMapper implements Interna DocValuesFormatProvider docValuesProvider, SimilarityProvider similarity, @Nullable Settings fieldDataSettings, Settings indexSettings) { super(new Names(name, name, name, name), 1.0f, fieldType, indexAnalyzer, searchAnalyzer, postingsProvider, docValuesProvider, - similarity, fieldDataSettings, indexSettings); + similarity, fieldDataSettings, indexSettings); this.enabled = enabled; this.autoBoost = autoBoost; @@ -256,50 +257,78 @@ public class AllFieldMapper extends AbstractFieldMapper implements Interna @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { // if all are defaults, no need to write it at all - if (enabled == Defaults.ENABLED && fieldType.stored() == Defaults.FIELD_TYPE.stored() && + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + + if (!includeDefaults && enabled == Defaults.ENABLED && fieldType.stored() == Defaults.FIELD_TYPE.stored() && fieldType.storeTermVectors() == Defaults.FIELD_TYPE.storeTermVectors() && indexAnalyzer == null && searchAnalyzer == null && customFieldDataSettings == null) { return builder; } builder.startObject(CONTENT_TYPE); - if (enabled != Defaults.ENABLED) { + if (includeDefaults || enabled != Defaults.ENABLED) { builder.field("enabled", enabled); } - if (autoBoost != false) { + if (includeDefaults || autoBoost != false) { builder.field("auto_boost", autoBoost); } - if (fieldType.stored() != Defaults.FIELD_TYPE.stored()) { + if (includeDefaults || fieldType.stored() != Defaults.FIELD_TYPE.stored()) { builder.field("store", fieldType.stored()); } - if (fieldType.storeTermVectors() != Defaults.FIELD_TYPE.storeTermVectors()) { + if (includeDefaults || fieldType.storeTermVectors() != Defaults.FIELD_TYPE.storeTermVectors()) { builder.field("store_term_vector", fieldType.storeTermVectors()); } - if (fieldType.storeTermVectorOffsets() != Defaults.FIELD_TYPE.storeTermVectorOffsets()) { + if (includeDefaults || fieldType.storeTermVectorOffsets() != Defaults.FIELD_TYPE.storeTermVectorOffsets()) { builder.field("store_term_vector_offsets", fieldType.storeTermVectorOffsets()); } - if (fieldType.storeTermVectorPositions() != Defaults.FIELD_TYPE.storeTermVectorPositions()) { + if (includeDefaults || fieldType.storeTermVectorPositions() != Defaults.FIELD_TYPE.storeTermVectorPositions()) { builder.field("store_term_vector_positions", fieldType.storeTermVectorPositions()); } - if (fieldType.storeTermVectorPayloads() != Defaults.FIELD_TYPE.storeTermVectorPayloads()) { + if (includeDefaults || fieldType.storeTermVectorPayloads() != Defaults.FIELD_TYPE.storeTermVectorPayloads()) { builder.field("store_term_vector_payloads", fieldType.storeTermVectorPayloads()); } - if (indexAnalyzer != null && searchAnalyzer != null && indexAnalyzer.name().equals(searchAnalyzer.name()) && !indexAnalyzer.name().startsWith("_")) { - // same analyzers, output it once - builder.field("analyzer", indexAnalyzer.name()); - } else { - if (indexAnalyzer != null && !indexAnalyzer.name().startsWith("_")) { + + + if (indexAnalyzer == null && searchAnalyzer == null) { + if (includeDefaults) { + builder.field("analyzer", "default"); + } + } else if (indexAnalyzer == null) { + // searchAnalyzer != null + if (includeDefaults || !searchAnalyzer.name().startsWith("_")) { + builder.field("search_analyzer", searchAnalyzer.name()); + } + } else if (searchAnalyzer == null) { + // indexAnalyzer != null + if (includeDefaults || !indexAnalyzer.name().startsWith("_")) { builder.field("index_analyzer", indexAnalyzer.name()); } - if (searchAnalyzer != null && !searchAnalyzer.name().startsWith("_")) { + } else if (indexAnalyzer.name().equals(searchAnalyzer.name())) { + // indexAnalyzer == searchAnalyzer + if (includeDefaults || !indexAnalyzer.name().startsWith("_")) { + builder.field("analyzer", indexAnalyzer.name()); + } + } else { + // both are there but different + if (includeDefaults || !indexAnalyzer.name().startsWith("_")) { + builder.field("index_analyzer", indexAnalyzer.name()); + } + if (includeDefaults || !searchAnalyzer.name().startsWith("_")) { builder.field("search_analyzer", searchAnalyzer.name()); } } + if (similarity() != null) { builder.field("similarity", similarity().name()); + } else if (includeDefaults) { + builder.field("similariry", SimilarityLookupService.DEFAULT_SIMILARITY); } + if (customFieldDataSettings != null) { builder.field("fielddata", (Map) customFieldDataSettings.getAsMap()); + } else if (includeDefaults) { + builder.field("fielddata", (Map) fieldDataType.getSettings().getAsMap()); } + builder.endObject(); return builder; } diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/BoostFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/BoostFieldMapper.java index c973abcfa35..2fb388451af 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/BoostFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/BoostFieldMapper.java @@ -123,7 +123,7 @@ public class BoostFieldMapper extends NumberFieldMapper implements Intern } protected BoostFieldMapper(String name, String indexName, int precisionStep, float boost, FieldType fieldType, Float nullValue, - PostingsFormatProvider postingsProvider, DocValuesFormatProvider docValuesProvider, @Nullable Settings fieldDataSettings, Settings indexSettings) { + PostingsFormatProvider postingsProvider, DocValuesFormatProvider docValuesProvider, @Nullable Settings fieldDataSettings, Settings indexSettings) { super(new Names(name, indexName, indexName, name), precisionStep, boost, fieldType, Defaults.IGNORE_MALFORMED, NumericFloatAnalyzer.buildNamedAnalyzer(precisionStep), NumericFloatAnalyzer.buildNamedAnalyzer(Integer.MAX_VALUE), postingsProvider, docValuesProvider, null, fieldDataSettings, indexSettings); @@ -284,28 +284,33 @@ public class BoostFieldMapper extends NumberFieldMapper implements Intern @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // all are defaults, don't write it at all - if (name().equals(Defaults.NAME) && nullValue == null && - fieldType.indexed() == Defaults.FIELD_TYPE.indexed() && + if (!includeDefaults && name().equals(Defaults.NAME) && nullValue == null && + fieldType.indexed() == Defaults.FIELD_TYPE.indexed() && fieldType.stored() == Defaults.FIELD_TYPE.stored() && customFieldDataSettings == null) { return builder; } builder.startObject(contentType()); - if (!name().equals(Defaults.NAME)) { + if (includeDefaults || !name().equals(Defaults.NAME)) { builder.field("name", name()); } - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); - } - if (fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { + } + if (includeDefaults || fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { builder.field("index", fieldType.indexed()); } - if (fieldType.stored() != Defaults.FIELD_TYPE.stored()) { + if (includeDefaults || fieldType.stored() != Defaults.FIELD_TYPE.stored()) { builder.field("store", fieldType.stored()); } if (customFieldDataSettings != null) { builder.field("fielddata", (Map) customFieldDataSettings.getAsMap()); + } else if (includeDefaults) { + builder.field("fielddata", (Map) fieldDataType.getSettings().getAsMap()); + } builder.endObject(); return builder; diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/IdFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/IdFieldMapper.java index ed990c2b003..53630e16953 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/IdFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/IdFieldMapper.java @@ -39,7 +39,9 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatProvider; +import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatService; import org.elasticsearch.index.codec.postingsformat.PostingsFormatProvider; +import org.elasticsearch.index.codec.postingsformat.PostingsFormatService; import org.elasticsearch.index.fielddata.FieldDataType; import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.core.AbstractFieldMapper; @@ -325,8 +327,10 @@ public class IdFieldMapper extends AbstractFieldMapper implements Intern @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // if all are defaults, no sense to write it at all - if (fieldType.stored() == Defaults.FIELD_TYPE.stored() + if (!includeDefaults && fieldType.stored() == Defaults.FIELD_TYPE.stored() && fieldType.indexed() == Defaults.FIELD_TYPE.indexed() && path == Defaults.PATH && customFieldDataSettings == null @@ -335,23 +339,44 @@ public class IdFieldMapper extends AbstractFieldMapper implements Intern return builder; } builder.startObject(CONTENT_TYPE); - if (fieldType.stored() != Defaults.FIELD_TYPE.stored()) { + if (includeDefaults || fieldType.stored() != Defaults.FIELD_TYPE.stored()) { builder.field("store", fieldType.stored()); } - if (fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { + if (includeDefaults || fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { builder.field("index", indexTokenizeOptionToString(fieldType.indexed(), fieldType.tokenized())); } - if (path != Defaults.PATH) { + if (includeDefaults || path != Defaults.PATH) { builder.field("path", path); } - if (postingsFormat != null && !postingsFormat.name().equals(defaultPostingFormat())) { - builder.field("postings_format", postingsFormat.name()); + + if (postingsFormat != null) { + if (includeDefaults || !postingsFormat.name().equals(defaultPostingFormat())) { + builder.field("postings_format", postingsFormat.name()); + } + } else if (includeDefaults) { + String format = defaultPostingFormat(); + if (format == null) { + format = PostingsFormatService.DEFAULT_FORMAT; + } + builder.field("postings_format", format); } - if (docValuesFormat != null && !docValuesFormat.name().equals(defaultDocValuesFormat())) { - builder.field(DOC_VALUES_FORMAT, docValuesFormat.name()); + + if (docValuesFormat != null) { + if (includeDefaults || !docValuesFormat.name().equals(defaultDocValuesFormat())) { + builder.field(DOC_VALUES_FORMAT, docValuesFormat.name()); + } + } else if (includeDefaults) { + String format = defaultDocValuesFormat(); + if (format == null) { + format = DocValuesFormatService.DEFAULT_FORMAT; + } + builder.field(DOC_VALUES_FORMAT, format); } + if (customFieldDataSettings != null) { builder.field("fielddata", (Map) customFieldDataSettings.getAsMap()); + } else if (includeDefaults) { + builder.field("fielddata", (Map) fieldDataType.getSettings().getAsMap()); } builder.endObject(); return builder; diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/IndexFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/IndexFieldMapper.java index 6729f1ed70e..1fb8fe3b424 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/IndexFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/IndexFieldMapper.java @@ -195,19 +195,25 @@ public class IndexFieldMapper extends AbstractFieldMapper implements Int @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // if all defaults, no need to write it at all - if (fieldType().stored() == Defaults.FIELD_TYPE.stored() && enabledState == Defaults.ENABLED_STATE) { + if (!includeDefaults && fieldType().stored() == Defaults.FIELD_TYPE.stored() && enabledState == Defaults.ENABLED_STATE) { return builder; } builder.startObject(CONTENT_TYPE); - if (fieldType().stored() != Defaults.FIELD_TYPE.stored() && enabledState.enabled) { + if (includeDefaults || fieldType().stored() != Defaults.FIELD_TYPE.stored() && enabledState.enabled) { builder.field("store", fieldType().stored()); } - if (enabledState != Defaults.ENABLED_STATE) { + if (includeDefaults || enabledState != Defaults.ENABLED_STATE) { builder.field("enabled", enabledState.enabled); } + if (customFieldDataSettings != null) { builder.field("fielddata", (Map) customFieldDataSettings.getAsMap()); + } else if (includeDefaults) { + builder.field("fielddata", (Map) fieldDataType.getSettings().getAsMap()); + } builder.endObject(); return builder; diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/RoutingFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/RoutingFieldMapper.java index 5d056cc4789..394da26d421 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/RoutingFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/RoutingFieldMapper.java @@ -124,7 +124,7 @@ public class RoutingFieldMapper extends AbstractFieldMapper implements I } protected RoutingFieldMapper(FieldType fieldType, boolean required, String path, PostingsFormatProvider postingsProvider, - DocValuesFormatProvider docValuesProvider, @Nullable Settings fieldDataSettings, Settings indexSettings) { + DocValuesFormatProvider docValuesProvider, @Nullable Settings fieldDataSettings, Settings indexSettings) { super(new Names(Defaults.NAME, Defaults.NAME, Defaults.NAME, Defaults.NAME), 1.0f, fieldType, Lucene.KEYWORD_ANALYZER, Lucene.KEYWORD_ANALYZER, postingsProvider, docValuesProvider, null, fieldDataSettings, indexSettings); this.required = required; @@ -238,22 +238,24 @@ public class RoutingFieldMapper extends AbstractFieldMapper implements I @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // if all are defaults, no sense to write it at all - if (fieldType.indexed() == Defaults.FIELD_TYPE.indexed() && + if (!includeDefaults && fieldType.indexed() == Defaults.FIELD_TYPE.indexed() && fieldType.stored() == Defaults.FIELD_TYPE.stored() && required == Defaults.REQUIRED && path == Defaults.PATH) { return builder; } builder.startObject(CONTENT_TYPE); - if (fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { + if (includeDefaults || fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { builder.field("index", indexTokenizeOptionToString(fieldType.indexed(), fieldType.tokenized())); } - if (fieldType.stored() != Defaults.FIELD_TYPE.stored()) { + if (includeDefaults || fieldType.stored() != Defaults.FIELD_TYPE.stored()) { builder.field("store", fieldType.stored()); } - if (required != Defaults.REQUIRED) { + if (includeDefaults || required != Defaults.REQUIRED) { builder.field("required", required); } - if (path != Defaults.PATH) { + if (includeDefaults || path != Defaults.PATH) { builder.field("path", path); } builder.endObject(); diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/SizeFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/SizeFieldMapper.java index 7f5f2a62e9c..d2bf07057d7 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/SizeFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/SizeFieldMapper.java @@ -99,7 +99,7 @@ public class SizeFieldMapper extends IntegerFieldMapper implements RootMapper { } public SizeFieldMapper(EnabledAttributeMapper enabled, FieldType fieldType, PostingsFormatProvider postingsProvider, - DocValuesFormatProvider docValuesProvider, @Nullable Settings fieldDataSettings, Settings indexSettings) { + DocValuesFormatProvider docValuesProvider, @Nullable Settings fieldDataSettings, Settings indexSettings) { super(new Names(Defaults.NAME), Defaults.PRECISION_STEP, Defaults.BOOST, fieldType, Defaults.NULL_VALUE, Defaults.IGNORE_MALFORMED, postingsProvider, docValuesProvider, null, fieldDataSettings, indexSettings); this.enabledState = enabled; @@ -156,15 +156,17 @@ public class SizeFieldMapper extends IntegerFieldMapper implements RootMapper { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // all are defaults, no need to write it at all - if (enabledState == Defaults.ENABLED_STATE && fieldType().stored() == Defaults.SIZE_FIELD_TYPE.stored()) { + if (!includeDefaults && enabledState == Defaults.ENABLED_STATE && fieldType().stored() == Defaults.SIZE_FIELD_TYPE.stored()) { return builder; } builder.startObject(contentType()); - if (enabledState != Defaults.ENABLED_STATE) { + if (includeDefaults || enabledState != Defaults.ENABLED_STATE) { builder.field("enabled", enabledState.enabled); } - if (fieldType().stored() != Defaults.SIZE_FIELD_TYPE.stored() && enabledState.enabled) { + if (includeDefaults || fieldType().stored() != Defaults.SIZE_FIELD_TYPE.stored() && enabledState.enabled) { builder.field("store", fieldType().stored()); } builder.endObject(); diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/SourceFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/SourceFieldMapper.java index 46ffd460407..07978fe84b1 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/SourceFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/SourceFieldMapper.java @@ -370,29 +370,42 @@ public class SourceFieldMapper extends AbstractFieldMapper implements In @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // all are defaults, no need to write it at all - if (enabled == Defaults.ENABLED && compress == null && compressThreshold == -1 && includes == null && excludes == null) { + if (!includeDefaults && enabled == Defaults.ENABLED && compress == null && compressThreshold == -1 && includes == null && excludes == null) { return builder; } builder.startObject(contentType()); - if (enabled != Defaults.ENABLED) { + if (includeDefaults || enabled != Defaults.ENABLED) { builder.field("enabled", enabled); } - if (!Objects.equal(format, Defaults.FORMAT)) { + if (includeDefaults || !Objects.equal(format, Defaults.FORMAT)) { builder.field("format", format); } if (compress != null) { builder.field("compress", compress); + } else if (includeDefaults) { + builder.field("compress", false); } if (compressThreshold != -1) { builder.field("compress_threshold", new ByteSizeValue(compressThreshold).toString()); + } else if (includeDefaults) { + builder.field("compress_threshold", -1); } + if (includes != null) { builder.field("includes", includes); + } else if (includeDefaults) { + builder.field("includes", Strings.EMPTY_ARRAY); } + if (excludes != null) { builder.field("excludes", excludes); + } else if (includeDefaults) { + builder.field("excludes", Strings.EMPTY_ARRAY); } + builder.endObject(); return builder; } diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/TTLFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/TTLFieldMapper.java index 360f8604ada..eb55c445628 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/TTLFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/TTLFieldMapper.java @@ -221,15 +221,17 @@ public class TTLFieldMapper extends LongFieldMapper implements InternalMapper, R @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // if all are defaults, no sense to write it at all - if (enabledState == Defaults.ENABLED_STATE && defaultTTL == Defaults.DEFAULT) { + if (!includeDefaults && enabledState == Defaults.ENABLED_STATE && defaultTTL == Defaults.DEFAULT) { return builder; } builder.startObject(CONTENT_TYPE); - if (enabledState != Defaults.ENABLED_STATE) { + if (includeDefaults || enabledState != Defaults.ENABLED_STATE) { builder.field("enabled", enabledState.enabled); } - if (defaultTTL != Defaults.DEFAULT && enabledState.enabled) { + if (includeDefaults || defaultTTL != Defaults.DEFAULT && enabledState.enabled) { builder.field("default", defaultTTL); } builder.endObject(); diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/TimestampFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/TimestampFieldMapper.java index 1e1e4b41974..f9bcbe3572b 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/TimestampFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/TimestampFieldMapper.java @@ -224,32 +224,37 @@ public class TimestampFieldMapper extends DateFieldMapper implements InternalMap @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // if all are defaults, no sense to write it at all - if (fieldType.indexed() == Defaults.FIELD_TYPE.indexed() && customFieldDataSettings == null && + if (!includeDefaults && fieldType.indexed() == Defaults.FIELD_TYPE.indexed() && customFieldDataSettings == null && fieldType.stored() == Defaults.FIELD_TYPE.stored() && enabledState == Defaults.ENABLED && path == Defaults.PATH && dateTimeFormatter.format().equals(Defaults.DATE_TIME_FORMATTER.format())) { return builder; } builder.startObject(CONTENT_TYPE); - if (enabledState != Defaults.ENABLED) { + if (includeDefaults || enabledState != Defaults.ENABLED) { builder.field("enabled", enabledState.enabled); } if (enabledState.enabled) { - if (fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { + if (includeDefaults || fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { builder.field("index", indexTokenizeOptionToString(fieldType.indexed(), fieldType.tokenized())); } - if (fieldType.stored() != Defaults.FIELD_TYPE.stored()) { + if (includeDefaults || fieldType.stored() != Defaults.FIELD_TYPE.stored()) { builder.field("store", fieldType.stored()); } - if (path != Defaults.PATH) { + if (includeDefaults || path != Defaults.PATH) { builder.field("path", path); } - if (!dateTimeFormatter.format().equals(Defaults.DATE_TIME_FORMATTER.format())) { + if (includeDefaults || !dateTimeFormatter.format().equals(Defaults.DATE_TIME_FORMATTER.format())) { builder.field("format", dateTimeFormatter.format()); } if (customFieldDataSettings != null) { builder.field("fielddata", (Map) customFieldDataSettings.getAsMap()); + } else if (includeDefaults) { + builder.field("fielddata", (Map) fieldDataType.getSettings().getAsMap()); } + } builder.endObject(); return builder; diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/TypeFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/TypeFieldMapper.java index 121bc193187..055b399bb9f 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/TypeFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/TypeFieldMapper.java @@ -194,15 +194,17 @@ public class TypeFieldMapper extends AbstractFieldMapper implements Inte @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // if all are defaults, no sense to write it at all - if (fieldType.stored() == Defaults.FIELD_TYPE.stored() && fieldType.indexed() == Defaults.FIELD_TYPE.indexed()) { + if (!includeDefaults && fieldType.stored() == Defaults.FIELD_TYPE.stored() && fieldType.indexed() == Defaults.FIELD_TYPE.indexed()) { return builder; } builder.startObject(CONTENT_TYPE); - if (fieldType.stored() != Defaults.FIELD_TYPE.stored()) { + if (includeDefaults || fieldType.stored() != Defaults.FIELD_TYPE.stored()) { builder.field("store", fieldType.stored()); } - if (fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { + if (includeDefaults || fieldType.indexed() != Defaults.FIELD_TYPE.indexed()) { builder.field("index", indexTokenizeOptionToString(fieldType.indexed(), fieldType.tokenized())); } builder.endObject(); diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/UidFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/UidFieldMapper.java index 0b6c4329613..c0249027116 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/UidFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/UidFieldMapper.java @@ -33,7 +33,9 @@ import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatProvider; +import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatService; import org.elasticsearch.index.codec.postingsformat.PostingsFormatProvider; +import org.elasticsearch.index.codec.postingsformat.PostingsFormatService; import org.elasticsearch.index.fielddata.FieldDataType; import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.core.AbstractFieldMapper; @@ -204,8 +206,10 @@ public class UidFieldMapper extends AbstractFieldMapper implements Internal @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + // if defaults, don't output - if (customFieldDataSettings == null + if (!includeDefaults && customFieldDataSettings == null && (postingsFormat == null || postingsFormat.name().equals(defaultPostingFormat())) && (docValuesFormat == null || docValuesFormat.name().equals(defaultDocValuesFormat()))) { return builder; @@ -214,19 +218,33 @@ public class UidFieldMapper extends AbstractFieldMapper implements Internal builder.startObject(CONTENT_TYPE); if (postingsFormat != null) { - if (!postingsFormat.name().equals(defaultPostingFormat())) { + if (includeDefaults || !postingsFormat.name().equals(defaultPostingFormat())) { builder.field("postings_format", postingsFormat.name()); } + } else if (includeDefaults) { + String format = defaultPostingFormat(); + if (format == null) { + format = PostingsFormatService.DEFAULT_FORMAT; + } + builder.field("postings_format", format); } if (docValuesFormat != null) { - if (!docValuesFormat.equals(defaultDocValuesFormat())) { + if (includeDefaults || !docValuesFormat.name().equals(defaultDocValuesFormat())) { builder.field(DOC_VALUES_FORMAT, docValuesFormat.name()); } + } else if (includeDefaults) { + String format = defaultDocValuesFormat(); + if (format == null) { + format = DocValuesFormatService.DEFAULT_FORMAT; + } + builder.field(DOC_VALUES_FORMAT, format); } if (customFieldDataSettings != null) { builder.field("fielddata", (Map) customFieldDataSettings.getAsMap()); + } else if (includeDefaults) { + builder.field("fielddata", (Map) fieldDataType.getSettings().getAsMap()); } builder.endObject(); diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/VersionFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/VersionFieldMapper.java index cf05869d358..9154c1efbdb 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/VersionFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/VersionFieldMapper.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatProvider; +import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatService; import org.elasticsearch.index.fielddata.FieldDataType; import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.core.AbstractFieldMapper; @@ -169,11 +170,25 @@ public class VersionFieldMapper extends AbstractFieldMapper implements Int @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - if (docValuesFormat == null || docValuesFormat.name().equals(defaultDocValuesFormat())) { + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + + if (!includeDefaults && (docValuesFormat == null || docValuesFormat.name().equals(defaultDocValuesFormat()))) { return builder; } + builder.startObject(CONTENT_TYPE); - builder.field(DOC_VALUES_FORMAT, docValuesFormat.name()); + if (docValuesFormat != null) { + if (includeDefaults || !docValuesFormat.name().equals(defaultDocValuesFormat())) { + builder.field(DOC_VALUES_FORMAT, docValuesFormat.name()); + } + } else { + String format = defaultDocValuesFormat(); + if (format == null) { + format = DocValuesFormatService.DEFAULT_FORMAT; + } + builder.field(DOC_VALUES_FORMAT, format); + } + builder.endObject(); return builder; } diff --git a/src/main/java/org/elasticsearch/index/mapper/ip/IpFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/ip/IpFieldMapper.java index 7510667e4b6..826b21e3752 100644 --- a/src/main/java/org/elasticsearch/index/mapper/ip/IpFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/ip/IpFieldMapper.java @@ -321,17 +321,21 @@ public class IpFieldMapper extends NumberFieldMapper { } @Override - protected void doXContentBody(XContentBuilder builder) throws IOException { - super.doXContentBody(builder); - if (precisionStep != Defaults.PRECISION_STEP) { + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || precisionStep != Defaults.PRECISION_STEP) { builder.field("precision_step", precisionStep); } - if (nullValue != null) { + if (includeDefaults || nullValue != null) { builder.field("null_value", nullValue); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); + } else if (includeDefaults) { + builder.field("include_in_all", false); } + } public static class NumericIpAnalyzer extends NumericAnalyzer { diff --git a/src/main/java/org/elasticsearch/index/mapper/multifield/MultiFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/multifield/MultiFieldMapper.java index a22f126030b..74932fabd05 100644 --- a/src/main/java/org/elasticsearch/index/mapper/multifield/MultiFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/multifield/MultiFieldMapper.java @@ -324,6 +324,7 @@ public class MultiFieldMapper implements Mapper, AllFieldMapper.IncludeInAll { builder.field("path", pathType.name().toLowerCase(Locale.ROOT)); } + builder.startObject("fields"); if (defaultMapper != null) { defaultMapper.toXContent(builder, params); diff --git a/src/main/java/org/elasticsearch/index/similarity/Similarities.java b/src/main/java/org/elasticsearch/index/similarity/Similarities.java index 8b8fab28cfe..9fb3780e041 100644 --- a/src/main/java/org/elasticsearch/index/similarity/Similarities.java +++ b/src/main/java/org/elasticsearch/index/similarity/Similarities.java @@ -21,7 +21,8 @@ package org.elasticsearch.index.similarity; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMap; -import org.apache.lucene.search.similarities.*; +import org.apache.lucene.search.similarities.BM25Similarity; +import org.apache.lucene.search.similarities.DefaultSimilarity; import org.elasticsearch.common.collect.MapBuilder; /** @@ -33,7 +34,8 @@ public class Similarities { static { MapBuilder similarities = MapBuilder.newMapBuilder(); - similarities.put("default", new PreBuiltSimilarityProvider.Factory("default", new DefaultSimilarity())); + similarities.put(SimilarityLookupService.DEFAULT_SIMILARITY, + new PreBuiltSimilarityProvider.Factory(SimilarityLookupService.DEFAULT_SIMILARITY, new DefaultSimilarity())); similarities.put("BM25", new PreBuiltSimilarityProvider.Factory("BM25", new BM25Similarity())); PRE_BUILT_SIMILARITIES = similarities.immutableMap(); diff --git a/src/main/java/org/elasticsearch/index/similarity/SimilarityLookupService.java b/src/main/java/org/elasticsearch/index/similarity/SimilarityLookupService.java index 88029ba6775..b06d7da17a2 100644 --- a/src/main/java/org/elasticsearch/index/similarity/SimilarityLookupService.java +++ b/src/main/java/org/elasticsearch/index/similarity/SimilarityLookupService.java @@ -32,16 +32,18 @@ import java.util.Map; /** * Service for looking up configured {@link SimilarityProvider} implementations by name. - * + *

* The service instantiates the Providers through their Factories using configuration * values found with the {@link SimilarityModule#SIMILARITY_SETTINGS_PREFIX} prefix. */ public class SimilarityLookupService extends AbstractIndexComponent { + public final static String DEFAULT_SIMILARITY = "default"; + private final ImmutableMap similarities; public SimilarityLookupService(Index index, Settings indexSettings) { - this (index, indexSettings, ImmutableMap.of()); + this(index, indexSettings, ImmutableMap.of()); } @Inject diff --git a/src/main/java/org/elasticsearch/index/similarity/SimilarityService.java b/src/main/java/org/elasticsearch/index/similarity/SimilarityService.java index 8060037a8fa..1b6e90c04d7 100644 --- a/src/main/java/org/elasticsearch/index/similarity/SimilarityService.java +++ b/src/main/java/org/elasticsearch/index/similarity/SimilarityService.java @@ -22,7 +22,6 @@ package org.elasticsearch.index.similarity; import org.apache.lucene.search.similarities.PerFieldSimilarityWrapper; import org.apache.lucene.search.similarities.Similarity; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.inject.name.Named; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.AbstractIndexComponent; @@ -46,7 +45,7 @@ public class SimilarityService extends AbstractIndexComponent { } public SimilarityService(Index index, Settings settings) { - this (index, settings, new SimilarityLookupService(index, settings), null); + this(index, settings, new SimilarityLookupService(index, settings), null); } @Inject @@ -56,7 +55,7 @@ public class SimilarityService extends AbstractIndexComponent { this.similarityLookupService = similarityLookupService; this.mapperService = mapperService; - Similarity defaultSimilarity = similarityLookupService.similarity("default").get(); + Similarity defaultSimilarity = similarityLookupService.similarity(SimilarityLookupService.DEFAULT_SIMILARITY).get(); // Expert users can configure the base type as being different to default, but out-of-box we use default. Similarity baseSimilarity = (similarityLookupService.similarity("base") != null) ? similarityLookupService.similarity("base").get() : defaultSimilarity; diff --git a/src/main/java/org/elasticsearch/rest/action/RestActionModule.java b/src/main/java/org/elasticsearch/rest/action/RestActionModule.java index 0505440fc18..ff8f889cf17 100644 --- a/src/main/java/org/elasticsearch/rest/action/RestActionModule.java +++ b/src/main/java/org/elasticsearch/rest/action/RestActionModule.java @@ -50,6 +50,7 @@ import org.elasticsearch.rest.action.admin.indices.exists.types.RestTypesExistsA import org.elasticsearch.rest.action.admin.indices.flush.RestFlushAction; import org.elasticsearch.rest.action.admin.indices.gateway.snapshot.RestGatewaySnapshotAction; import org.elasticsearch.rest.action.admin.indices.mapping.delete.RestDeleteMappingAction; +import org.elasticsearch.rest.action.admin.indices.mapping.get.RestGetFieldMappingAction; import org.elasticsearch.rest.action.admin.indices.mapping.get.RestGetMappingAction; import org.elasticsearch.rest.action.admin.indices.mapping.put.RestPutMappingAction; import org.elasticsearch.rest.action.admin.indices.open.RestOpenIndexAction; @@ -161,6 +162,7 @@ public class RestActionModule extends AbstractModule { bind(RestPutMappingAction.class).asEagerSingleton(); bind(RestDeleteMappingAction.class).asEagerSingleton(); bind(RestGetMappingAction.class).asEagerSingleton(); + bind(RestGetFieldMappingAction.class).asEagerSingleton(); bind(RestGatewaySnapshotAction.class).asEagerSingleton(); diff --git a/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetFieldMappingAction.java b/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetFieldMappingAction.java new file mode 100644 index 00000000000..678da32038d --- /dev/null +++ b/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetFieldMappingAction.java @@ -0,0 +1,97 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.mapping.get; + +import com.google.common.collect.ImmutableMap; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsRequest; +import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse; +import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse.FieldMappingMetaData; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.rest.*; +import org.elasticsearch.rest.action.support.RestXContentBuilder; + +import java.io.IOException; + +import static org.elasticsearch.rest.RestRequest.Method.GET; +import static org.elasticsearch.rest.RestStatus.NOT_FOUND; +import static org.elasticsearch.rest.RestStatus.OK; + +/** + * + */ +public class RestGetFieldMappingAction extends BaseRestHandler { + + @Inject + public RestGetFieldMappingAction(Settings settings, Client client, RestController controller) { + super(settings, client); + controller.registerHandler(GET, "/_mapping/field/{fields}", this); + controller.registerHandler(GET, "/{index}/_mapping/field/{fields}", this); + controller.registerHandler(GET, "/{index}/{type}/_mapping/field/{fields}", this); + } + + @Override + public void handleRequest(final RestRequest request, final RestChannel channel) { + final String[] indices = Strings.splitStringByCommaToArray(request.param("index")); + final String[] types = Strings.splitStringByCommaToArray(request.param("type")); + boolean local = request.paramAsBooleanOptional("local", false); + final String[] fields = Strings.splitStringByCommaToArray(request.param("fields")); + + GetFieldMappingsRequest getMappingsRequest = new GetFieldMappingsRequest(); + getMappingsRequest.indices(indices).types(types).local(local).fields(fields).includeDefaults(request.paramAsBoolean("include_defaults", false)); + client.admin().indices().getFieldMappings(getMappingsRequest, new ActionListener() { + + @SuppressWarnings("unchecked") + @Override + public void onResponse(GetFieldMappingsResponse response) { + try { + XContentBuilder builder = RestXContentBuilder.restContentBuilder(request); + builder.startObject(); + + ImmutableMap>> mappingsByIndex = response.mappings(); + RestStatus status = OK; + if (mappingsByIndex.isEmpty() && fields.length > 0) { + status = NOT_FOUND; + } + response.toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.endObject(); + channel.sendResponse(new XContentRestResponse(request, status, builder)); + } catch (Throwable e) { + onFailure(e); + } + } + + @Override + public void onFailure(Throwable e) { + try { + channel.sendResponse(new XContentThrowableRestResponse(request, e)); + } catch (IOException e1) { + logger.error("Failed to send failure response", e1); + } + } + }); + } + +} diff --git a/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetMappingAction.java b/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetMappingAction.java index 1b12a2d735a..299b9667c2d 100644 --- a/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetMappingAction.java +++ b/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetMappingAction.java @@ -25,7 +25,6 @@ import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest; import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.metadata.MappingMetaData; -import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; @@ -60,7 +59,6 @@ public class RestGetMappingAction extends BaseRestHandler { final String[] indices = Strings.splitStringByCommaToArray(request.param("index")); final String[] types = Strings.splitStringByCommaToArray(request.param("type")); boolean local = request.paramAsBooleanOptional("local", false); - GetMappingsRequest getMappingsRequest = new GetMappingsRequest(); getMappingsRequest.indices(indices).types(types).local(local); client.admin().indices().getMappings(getMappingsRequest, new ActionListener() { diff --git a/src/test/java/org/elasticsearch/index/mapper/routing/RoutingTypeMapperTests.java b/src/test/java/org/elasticsearch/index/mapper/routing/RoutingTypeMapperTests.java index 70e175ceb48..8a387ef7576 100644 --- a/src/test/java/org/elasticsearch/index/mapper/routing/RoutingTypeMapperTests.java +++ b/src/test/java/org/elasticsearch/index/mapper/routing/RoutingTypeMapperTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.mapper.routing; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.json.JsonXContent; @@ -31,7 +32,6 @@ import org.junit.Test; import java.util.Map; -import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; /** @@ -59,9 +59,9 @@ public class RoutingTypeMapperTests extends ElasticsearchTestCase { public void testSetValues() throws Exception { String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") .startObject("_routing") - .field("store", "no") - .field("index", "no") - .field("path", "route") + .field("store", "no") + .field("index", "no") + .field("path", "route") .endObject() .endObject().endObject().string(); DocumentMapper docMapper = MapperTestUtils.newParser().parse(mapping); @@ -78,7 +78,7 @@ public class RoutingTypeMapperTests extends ElasticsearchTestCase { DocumentMapper enabledMapper = MapperTestUtils.newParser().parse(enabledMapping); XContentBuilder builder = JsonXContent.contentBuilder().startObject(); - enabledMapper.routingFieldMapper().toXContent(builder, null).endObject(); + enabledMapper.routingFieldMapper().toXContent(builder, ToXContent.EMPTY_PARAMS).endObject(); builder.close(); Map serializedMap = JsonXContent.jsonXContent.createParser(builder.bytes()).mapAndClose(); assertThat(serializedMap, hasKey("_routing")); diff --git a/src/test/java/org/elasticsearch/index/mapper/timestamp/TimestampMappingTests.java b/src/test/java/org/elasticsearch/index/mapper/timestamp/TimestampMappingTests.java index 4881622f22f..6a344f6f9d9 100644 --- a/src/test/java/org/elasticsearch/index/mapper/timestamp/TimestampMappingTests.java +++ b/src/test/java/org/elasticsearch/index/mapper/timestamp/TimestampMappingTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.mapper.timestamp; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.json.JsonXContent; @@ -34,7 +35,6 @@ import org.junit.Test; import java.util.Locale; import java.util.Map; -import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; /** @@ -128,7 +128,7 @@ public class TimestampMappingTests extends ElasticsearchTestCase { DocumentMapper mapper = MapperTestUtils.newParser().parse(mapping); XContentBuilder builder = XContentFactory.jsonBuilder().startObject(); - mapper.timestampFieldMapper().toXContent(builder, null); + mapper.timestampFieldMapper().toXContent(builder, ToXContent.EMPTY_PARAMS); builder.endObject(); assertThat(builder.string(), is(String.format(Locale.ROOT, "{\"%s\":{}}", TimestampFieldMapper.NAME))); @@ -142,7 +142,7 @@ public class TimestampMappingTests extends ElasticsearchTestCase { DocumentMapper enabledMapper = MapperTestUtils.newParser().parse(enabledMapping); XContentBuilder builder = JsonXContent.contentBuilder().startObject(); - enabledMapper.timestampFieldMapper().toXContent(builder, null).endObject(); + enabledMapper.timestampFieldMapper().toXContent(builder, ToXContent.EMPTY_PARAMS).endObject(); builder.close(); Map serializedMap = JsonXContent.jsonXContent.createParser(builder.bytes()).mapAndClose(); assertThat(serializedMap, hasKey("_timestamp")); diff --git a/src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsTests.java b/src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsTests.java new file mode 100644 index 00000000000..b36f3c9870c --- /dev/null +++ b/src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsTests.java @@ -0,0 +1,156 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.indices.mapping; + +import com.google.common.base.Predicate; +import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.test.AbstractIntegrationTest; +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.hamcrest.Matchers.*; + +/** + * + */ +public class SimpleGetFieldMappingsTests extends AbstractIntegrationTest { + + @Test + public void getMappingsWhereThereAreNone() { + createIndex("index"); + GetFieldMappingsResponse response = client().admin().indices().prepareGetFieldMappings().get(); + assertThat(response.mappings().size(), equalTo(0)); + + assertThat(response.fieldMappings("index", "type", "field"), Matchers.nullValue()); + } + + private XContentBuilder getMappingForType(String type) throws IOException { + return jsonBuilder().startObject().startObject(type).startObject("properties") + .startObject("field1").field("type", "string").endObject() + .startObject("obj").startObject("properties").startObject("subfield").field("type", "string").field("index", "not_analyzed").endObject().endObject().endObject() + .endObject().endObject().endObject(); + } + + @Test + public void simpleGetFieldMappings() throws Exception { + + + assertTrue(client().admin().indices().prepareCreate("indexa") + .addMapping("typeA", getMappingForType("typeA")) + .addMapping("typeB", getMappingForType("typeB")) + .get().isAcknowledged()); + assertTrue(client().admin().indices().prepareCreate("indexb") + .addMapping("typeA", getMappingForType("typeA")) + .addMapping("typeB", getMappingForType("typeB")) + .get().isAcknowledged()); + + // Get mappings by full name + GetFieldMappingsResponse response = client().admin().indices().prepareGetFieldMappings("indexa").setTypes("typeA").setFields("field1", "obj.subfield").get(); + assertThat(response.fieldMappings("indexa", "typeA", "field1").fullName(), equalTo("field1")); + assertThat(response.fieldMappings("indexa", "typeA", "field1").sourceAsMap(), hasKey("field1")); + assertThat(response.fieldMappings("indexa", "typeA", "obj.subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexa", "typeA", "obj.subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.mappings().get("indexa"), not(hasKey("typeB"))); + assertThat(response.fieldMappings("indexa", "typeB", "field1"), nullValue()); + assertThat(response.mappings(), not(hasKey("indexb"))); + assertThat(response.fieldMappings("indexb", "typeB", "field1"), nullValue()); + + // Get mappings by name + response = client().admin().indices().prepareGetFieldMappings("indexa").setTypes("typeA").setFields("field1", "subfield").get(); + assertThat(response.fieldMappings("indexa", "typeA", "field1").fullName(), equalTo("field1")); + assertThat(response.fieldMappings("indexa", "typeA", "field1").sourceAsMap(), hasKey("field1")); + assertThat(response.fieldMappings("indexa", "typeA", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexa", "typeA", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexa", "typeB", "field1"), nullValue()); + assertThat(response.fieldMappings("indexb", "typeB", "field1"), nullValue()); + + // get mappings by name across multiple indices + response = client().admin().indices().prepareGetFieldMappings().setTypes("typeA").setFields("subfield").get(); + assertThat(response.fieldMappings("indexa", "typeA", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexa", "typeA", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexa", "typeB", "subfield"), nullValue()); + assertThat(response.fieldMappings("indexb", "typeA", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexb", "typeA", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexb", "typeB", "subfield"), nullValue()); + + // get mappings by name across multiple types + response = client().admin().indices().prepareGetFieldMappings("indexa").setFields("subfield").get(); + assertThat(response.fieldMappings("indexa", "typeA", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexa", "typeA", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexa", "typeA", "field1"), nullValue()); + assertThat(response.fieldMappings("indexa", "typeB", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexa", "typeB", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexa", "typeB", "field1"), nullValue()); + assertThat(response.fieldMappings("indexb", "typeA", "subfield"), nullValue()); + assertThat(response.fieldMappings("indexb", "typeA", "field1"), nullValue()); + assertThat(response.fieldMappings("indexb", "typeB", "subfield"), nullValue()); + assertThat(response.fieldMappings("indexb", "typeB", "field1"), nullValue()); + + // get mappings by name across multiple types & indices + response = client().admin().indices().prepareGetFieldMappings().setFields("subfield").get(); + assertThat(response.fieldMappings("indexa", "typeA", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexa", "typeA", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexa", "typeA", "field1"), nullValue()); + assertThat(response.fieldMappings("indexa", "typeB", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexa", "typeB", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexa", "typeB", "field1"), nullValue()); + assertThat(response.fieldMappings("indexb", "typeA", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexb", "typeA", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexb", "typeB", "field1"), nullValue()); + assertThat(response.fieldMappings("indexb", "typeB", "subfield").fullName(), equalTo("obj.subfield")); + assertThat(response.fieldMappings("indexb", "typeB", "subfield").sourceAsMap(), hasKey("subfield")); + assertThat(response.fieldMappings("indexb", "typeB", "field1"), nullValue()); + + } + + @SuppressWarnings("unchecked") + @Test + public void simpleGetFieldMappingsWithDefaults() throws Exception { + client().admin().indices().prepareCreate("test") + .addMapping("type", getMappingForType("type")).get(); + + client().prepareIndex("test", "type", "1").setSource("num", 1).get(); + + awaitBusy(new Predicate() { + @Override + public boolean apply(@Nullable java.lang.Object input) { + GetFieldMappingsResponse response = client().admin().indices().prepareGetFieldMappings().setFields("num").get(); + return response.fieldMappings("test", "type", "num") != null; + } + }); + + GetFieldMappingsResponse response = client().admin().indices().prepareGetFieldMappings().setFields("num", "field1", "subfield").includeDefaults(true).get(); + + assertThat((Map) response.fieldMappings("test", "type", "num").sourceAsMap().get("num"), hasEntry("index", (Object) "not_analyzed")); + assertThat((Map) response.fieldMappings("test", "type", "num").sourceAsMap().get("num"), hasEntry("type", (Object) "long")); + assertThat((Map) response.fieldMappings("test", "type", "field1").sourceAsMap().get("field1"), hasEntry("index", (Object) "analyzed")); + assertThat((Map) response.fieldMappings("test", "type", "field1").sourceAsMap().get("field1"), hasEntry("type", (Object) "string")); + assertThat((Map) response.fieldMappings("test", "type", "subfield").sourceAsMap().get("subfield"), hasEntry("index", (Object) "not_analyzed")); + assertThat((Map) response.fieldMappings("test", "type", "subfield").sourceAsMap().get("subfield"), hasEntry("type", (Object) "string")); + + + } +}