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")); + + + } +}