diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java index 7713480066c01..a5289a78002e6 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java @@ -611,6 +611,32 @@ private void serializeValue(final ParseContext context, String currentFieldName, } } } + if (!resolved && context.root().numericDetection()) { + try { + Long.parseLong(text); + Mapper.Builder builder = context.root().findTemplateBuilder(context, currentFieldName, "long"); + if (builder == null) { + builder = longField(currentFieldName); + } + mapper = builder.build(builderContext); + resolved = true; + } catch (Exception e) { + // not a long number + } + if (!resolved) { + try { + Double.parseDouble(text); + Mapper.Builder builder = context.root().findTemplateBuilder(context, currentFieldName, "double"); + if (builder == null) { + builder = doubleField(currentFieldName); + } + mapper = builder.build(builderContext); + resolved = true; + } catch (Exception e) { + // not a long number + } + } + } // DON'T do automatic ip detection logic, since it messes up with docs that have hosts and ips // check if its an ip // if (!resolved && text.indexOf('.') != -1) { diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/object/RootObjectMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/object/RootObjectMapper.java index ea044be37a184..25f514637193f 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/object/RootObjectMapper.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/object/RootObjectMapper.java @@ -54,6 +54,7 @@ public static class Defaults { Joda.forPattern("yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z") }; public static final boolean DATE_DETECTION = true; + public static final boolean NUMERIC_DETECTION = false; } public static class Builder extends ObjectMapper.Builder { @@ -65,6 +66,7 @@ public static class Builder extends ObjectMapper.Builder dynamicDateTimeFormatters = newArrayList(); protected boolean dateDetection = Defaults.DATE_DETECTION; + protected boolean numericDetection = Defaults.NUMERIC_DETECTION; public Builder(String name) { super(name); @@ -117,7 +119,7 @@ public Builder add(DynamicTemplate... dynamicTemplate) { return new RootObjectMapper(name, enabled, dynamic, pathType, mappers, dates, dynamicTemplates.toArray(new DynamicTemplate[dynamicTemplates.size()]), - dateDetection); + dateDetection, numericDetection); } } @@ -165,6 +167,8 @@ public static class TypeParser extends ObjectMapper.TypeParser { } } else if (fieldName.equals("date_detection")) { ((Builder) builder).dateDetection = nodeBooleanValue(fieldNode); + } else if (fieldName.equals("numeric_detection")) { + ((Builder) builder).numericDetection = nodeBooleanValue(fieldNode); } } } @@ -172,21 +176,27 @@ public static class TypeParser extends ObjectMapper.TypeParser { private final FormatDateTimeFormatter[] dynamicDateTimeFormatters; private final boolean dateDetection; + private final boolean numericDetection; private volatile DynamicTemplate dynamicTemplates[]; RootObjectMapper(String name, boolean enabled, Dynamic dynamic, ContentPath.Type pathType, Map mappers, - FormatDateTimeFormatter[] dynamicDateTimeFormatters, DynamicTemplate dynamicTemplates[], boolean dateDetection) { + FormatDateTimeFormatter[] dynamicDateTimeFormatters, DynamicTemplate dynamicTemplates[], boolean dateDetection, boolean numericDetection) { super(name, name, enabled, Nested.NO, dynamic, pathType, mappers); this.dynamicTemplates = dynamicTemplates; this.dynamicDateTimeFormatters = dynamicDateTimeFormatters; this.dateDetection = dateDetection; + this.numericDetection = numericDetection; } public boolean dateDetection() { return this.dateDetection; } + public boolean numericDetection() { + return this.numericDetection; + } + public FormatDateTimeFormatter[] dynamicDateTimeFormatters() { return dynamicDateTimeFormatters; } @@ -255,5 +265,8 @@ public DynamicTemplate findTemplate(ContentPath path, String name, String dynami if (dateDetection != Defaults.DATE_DETECTION) { builder.field("date_detection", dateDetection); } + if (numericDetection != Defaults.NUMERIC_DETECTION) { + builder.field("numeric_detection", numericDetection); + } } } diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/numeric/SimpleNumericTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/numeric/SimpleNumericTests.java new file mode 100644 index 0000000000000..83c64b72ec7f8 --- /dev/null +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/numeric/SimpleNumericTests.java @@ -0,0 +1,80 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.index.mapper.numeric; + +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.index.mapper.DocumentMapper; +import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.mapper.MapperTests; +import org.elasticsearch.index.mapper.ParsedDocument; +import org.elasticsearch.index.mapper.core.DoubleFieldMapper; +import org.elasticsearch.index.mapper.core.LongFieldMapper; +import org.elasticsearch.index.mapper.core.StringFieldMapper; +import org.testng.annotations.Test; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +/** + */ +@Test +public class SimpleNumericTests { + + public void testNumericDetectionEnabled() throws Exception { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .field("numeric_detection", true) + .endObject().endObject().string(); + + DocumentMapper defaultMapper = MapperTests.newParser().parse(mapping); + + ParsedDocument doc = defaultMapper.parse("type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("s_long", "100") + .field("s_double", "100.0") + .endObject() + .copiedBytes()); + + FieldMapper mapper = defaultMapper.mappers().smartNameFieldMapper("s_long"); + assertThat(mapper, instanceOf(LongFieldMapper.class)); + + mapper = defaultMapper.mappers().smartNameFieldMapper("s_double"); + assertThat(mapper, instanceOf(DoubleFieldMapper.class)); + } + + public void testNumericDetectionDefault() throws Exception { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .endObject().endObject().string(); + + DocumentMapper defaultMapper = MapperTests.newParser().parse(mapping); + + ParsedDocument doc = defaultMapper.parse("type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("s_long", "100") + .field("s_double", "100.0") + .endObject() + .copiedBytes()); + + FieldMapper mapper = defaultMapper.mappers().smartNameFieldMapper("s_long"); + assertThat(mapper, instanceOf(StringFieldMapper.class)); + + mapper = defaultMapper.mappers().smartNameFieldMapper("s_double"); + assertThat(mapper, instanceOf(StringFieldMapper.class)); + } +}