Allow big integers and decimals to be mapped dynamically. (#42827)
This PR proposes to model big integers as longs (and big decimals as doubles) in the context of dynamic mappings. Previously, the dynamic mapping logic did not recognize big integers or decimals, and would an error of the form "No matching token for number_type [BIG_INTEGER]" when a dynamic big integer was encountered. It now accepts these numeric types and interprets them as 'long' and 'double' respectively. This allows `dynamic_templates` to accept and and remap them as another type such as `keyword` or `scaled_float`. Addresses #37846.
This commit is contained in:
parent
3928c624a3
commit
4b1d8e4433
|
@ -114,7 +114,7 @@ public interface XContentParser extends Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NumberType {
|
enum NumberType {
|
||||||
INT, LONG, FLOAT, DOUBLE
|
INT, BIG_INTEGER, LONG, FLOAT, DOUBLE, BIG_DECIMAL
|
||||||
}
|
}
|
||||||
|
|
||||||
XContentType contentType();
|
XContentType contentType();
|
||||||
|
|
|
@ -199,12 +199,16 @@ public class JsonXContentParser extends AbstractXContentParser {
|
||||||
switch (numberType) {
|
switch (numberType) {
|
||||||
case INT:
|
case INT:
|
||||||
return NumberType.INT;
|
return NumberType.INT;
|
||||||
|
case BIG_INTEGER:
|
||||||
|
return NumberType.BIG_INTEGER;
|
||||||
case LONG:
|
case LONG:
|
||||||
return NumberType.LONG;
|
return NumberType.LONG;
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
return NumberType.FLOAT;
|
return NumberType.FLOAT;
|
||||||
case DOUBLE:
|
case DOUBLE:
|
||||||
return NumberType.DOUBLE;
|
return NumberType.DOUBLE;
|
||||||
|
case BIG_DECIMAL:
|
||||||
|
return NumberType.BIG_DECIMAL;
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("No matching token for number_type [" + numberType + "]");
|
throw new IllegalStateException("No matching token for number_type [" + numberType + "]");
|
||||||
}
|
}
|
||||||
|
|
|
@ -765,13 +765,17 @@ final class DocumentParser {
|
||||||
return builder;
|
return builder;
|
||||||
} else if (token == XContentParser.Token.VALUE_NUMBER) {
|
} else if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||||
XContentParser.NumberType numberType = context.parser().numberType();
|
XContentParser.NumberType numberType = context.parser().numberType();
|
||||||
if (numberType == XContentParser.NumberType.INT || numberType == XContentParser.NumberType.LONG) {
|
if (numberType == XContentParser.NumberType.INT
|
||||||
|
|| numberType == XContentParser.NumberType.LONG
|
||||||
|
|| numberType == XContentParser.NumberType.BIG_INTEGER) {
|
||||||
Mapper.Builder builder = context.root().findTemplateBuilder(context, currentFieldName, XContentFieldType.LONG);
|
Mapper.Builder builder = context.root().findTemplateBuilder(context, currentFieldName, XContentFieldType.LONG);
|
||||||
if (builder == null) {
|
if (builder == null) {
|
||||||
builder = newLongBuilder(currentFieldName, context.indexSettings().getIndexVersionCreated());
|
builder = newLongBuilder(currentFieldName, context.indexSettings().getIndexVersionCreated());
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
} else if (numberType == XContentParser.NumberType.FLOAT || numberType == XContentParser.NumberType.DOUBLE) {
|
} else if (numberType == XContentParser.NumberType.FLOAT
|
||||||
|
|| numberType == XContentParser.NumberType.DOUBLE
|
||||||
|
|| numberType == XContentParser.NumberType.BIG_DECIMAL) {
|
||||||
Mapper.Builder builder = context.root().findTemplateBuilder(context, currentFieldName, XContentFieldType.DOUBLE);
|
Mapper.Builder builder = context.root().findTemplateBuilder(context, currentFieldName, XContentFieldType.DOUBLE);
|
||||||
if (builder == null) {
|
if (builder == null) {
|
||||||
// no templates are defined, we use float by default instead of double
|
// no templates are defined, we use float by default instead of double
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
package org.elasticsearch.index.mapper;
|
package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.IndexableField;
|
||||||
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
@ -37,6 +39,8 @@ import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||||
import org.elasticsearch.test.InternalSettingsPlugin;
|
import org.elasticsearch.test.InternalSettingsPlugin;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -706,6 +710,62 @@ public class DocumentParserTests extends ESSingleNodeTestCase {
|
||||||
assertEquals(0, doc.rootDoc().getFields("foo").length);
|
assertEquals(0, doc.rootDoc().getFields("foo").length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testDynamicBigInteger() throws Exception {
|
||||||
|
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||||
|
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject()
|
||||||
|
.startObject("type")
|
||||||
|
.startArray("dynamic_templates").startObject()
|
||||||
|
.startObject("big-integer-to-keyword")
|
||||||
|
.field("match", "big-*")
|
||||||
|
.field("match_mapping_type", "long")
|
||||||
|
.startObject("mapping").field("type", "keyword").endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject().endArray()
|
||||||
|
.endObject()
|
||||||
|
.endObject());
|
||||||
|
|
||||||
|
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||||
|
BigInteger value = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
|
||||||
|
BytesReference bytes = BytesReference.bytes(XContentFactory.jsonBuilder().startObject()
|
||||||
|
.field("big-integer", value)
|
||||||
|
.endObject());
|
||||||
|
ParsedDocument doc = mapper.parse(new SourceToParse("test", "type", "1", bytes, XContentType.JSON));
|
||||||
|
|
||||||
|
IndexableField[] fields = doc.rootDoc().getFields("big-integer");
|
||||||
|
assertEquals(2, fields.length);
|
||||||
|
assertTrue(fields[0].fieldType() instanceof KeywordFieldMapper.KeywordFieldType);
|
||||||
|
assertEquals(new BytesRef(value.toString()), fields[0].binaryValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDynamicBigDecimal() throws Exception {
|
||||||
|
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||||
|
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject()
|
||||||
|
.startObject("type")
|
||||||
|
.startArray("dynamic_templates").startObject()
|
||||||
|
.startObject("big-decimal-to-scaled-float")
|
||||||
|
.field("match", "big-*")
|
||||||
|
.field("match_mapping_type", "double")
|
||||||
|
.startObject("mapping")
|
||||||
|
.field("type", "keyword")
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject().endArray()
|
||||||
|
.endObject()
|
||||||
|
.endObject());
|
||||||
|
|
||||||
|
BigDecimal value = BigDecimal.valueOf(Double.MAX_VALUE).add(BigDecimal.valueOf(10.1));
|
||||||
|
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||||
|
BytesReference bytes = BytesReference.bytes(XContentFactory.jsonBuilder().startObject()
|
||||||
|
.field("big-decimal", value)
|
||||||
|
.endObject());
|
||||||
|
ParsedDocument doc = mapper.parse(new SourceToParse("test", "type", "1", bytes, XContentType.JSON));
|
||||||
|
|
||||||
|
IndexableField[] fields = doc.rootDoc().getFields("big-decimal");
|
||||||
|
assertEquals(2, fields.length);
|
||||||
|
assertTrue(fields[0].fieldType() instanceof KeywordFieldMapper.KeywordFieldType);
|
||||||
|
assertEquals(new BytesRef(value.toString()), fields[0].binaryValue());
|
||||||
|
}
|
||||||
|
|
||||||
public void testDynamicDottedFieldNameLongArray() throws Exception {
|
public void testDynamicDottedFieldNameLongArray() throws Exception {
|
||||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
|
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
|
Loading…
Reference in New Issue