Support numeric bounds with decimal parts for long/integer/short/byte datatypes (#21972)

Close #21600
This commit is contained in:
Stéphane Campinas 2016-12-22 14:20:15 +00:00 committed by Adrien Grand
parent b9a90eca20
commit e1b8528ab8
4 changed files with 279 additions and 78 deletions

View File

@ -39,6 +39,7 @@ import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.NumericUtils;
import org.elasticsearch.action.fieldstats.FieldStats;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
@ -54,6 +55,7 @@ import org.joda.time.DateTimeZone;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -145,7 +147,7 @@ public class NumberFieldMapper extends FieldMapper {
if (propNode == null) {
throw new MapperParsingException("Property [null_value] cannot be null.");
}
builder.nullValue(type.parse(propNode));
builder.nullValue(type.parse(propNode, false));
iterator.remove();
} else if (propName.equals("ignore_malformed")) {
builder.ignoreMalformed(TypeParsers.nodeBooleanValue("ignore_malformed", propNode, parserContext));
@ -162,8 +164,8 @@ public class NumberFieldMapper extends FieldMapper {
public enum NumberType {
HALF_FLOAT("half_float", NumericType.HALF_FLOAT) {
@Override
Float parse(Object value) {
return (Float) FLOAT.parse(value);
Float parse(Object value, boolean coerce) {
return (Float) FLOAT.parse(value, false);
}
@Override
@ -173,7 +175,7 @@ public class NumberFieldMapper extends FieldMapper {
@Override
Query termQuery(String field, Object value) {
float v = parse(value);
float v = parse(value, false);
return HalfFloatPoint.newExactQuery(field, v);
}
@ -181,7 +183,7 @@ public class NumberFieldMapper extends FieldMapper {
Query termsQuery(String field, List<Object> values) {
float[] v = new float[values.size()];
for (int i = 0; i < values.size(); ++i) {
v[i] = parse(values.get(i));
v[i] = parse(values.get(i), false);
}
return HalfFloatPoint.newSetQuery(field, v);
}
@ -216,14 +218,14 @@ public class NumberFieldMapper extends FieldMapper {
float l = Float.NEGATIVE_INFINITY;
float u = Float.POSITIVE_INFINITY;
if (lowerTerm != null) {
l = parse(lowerTerm);
l = parse(lowerTerm, false);
if (includeLower) {
l = nextDown(l);
}
l = HalfFloatPoint.nextUp(l);
}
if (upperTerm != null) {
u = parse(upperTerm);
u = parse(upperTerm, false);
if (includeUpper) {
u = nextUp(u);
}
@ -270,7 +272,7 @@ public class NumberFieldMapper extends FieldMapper {
},
FLOAT("float", NumericType.FLOAT) {
@Override
Float parse(Object value) {
Float parse(Object value, boolean coerce) {
if (value instanceof Number) {
return ((Number) value).floatValue();
}
@ -287,7 +289,7 @@ public class NumberFieldMapper extends FieldMapper {
@Override
Query termQuery(String field, Object value) {
float v = parse(value);
float v = parse(value, false);
return FloatPoint.newExactQuery(field, v);
}
@ -295,7 +297,7 @@ public class NumberFieldMapper extends FieldMapper {
Query termsQuery(String field, List<Object> values) {
float[] v = new float[values.size()];
for (int i = 0; i < values.size(); ++i) {
v[i] = parse(values.get(i));
v[i] = parse(values.get(i), false);
}
return FloatPoint.newSetQuery(field, v);
}
@ -330,13 +332,13 @@ public class NumberFieldMapper extends FieldMapper {
float l = Float.NEGATIVE_INFINITY;
float u = Float.POSITIVE_INFINITY;
if (lowerTerm != null) {
l = parse(lowerTerm);
l = parse(lowerTerm, false);
if (includeLower == false) {
l = nextUp(l);
}
}
if (upperTerm != null) {
u = parse(upperTerm);
u = parse(upperTerm, false);
if (includeUpper == false) {
u = nextDown(u);
}
@ -382,7 +384,7 @@ public class NumberFieldMapper extends FieldMapper {
},
DOUBLE("double", NumericType.DOUBLE) {
@Override
Double parse(Object value) {
Double parse(Object value, boolean coerce) {
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
@ -399,7 +401,7 @@ public class NumberFieldMapper extends FieldMapper {
@Override
Query termQuery(String field, Object value) {
double v = parse(value);
double v = parse(value, false);
return DoublePoint.newExactQuery(field, v);
}
@ -407,7 +409,7 @@ public class NumberFieldMapper extends FieldMapper {
Query termsQuery(String field, List<Object> values) {
double[] v = new double[values.size()];
for (int i = 0; i < values.size(); ++i) {
v[i] = parse(values.get(i));
v[i] = parse(values.get(i), false);
}
return DoublePoint.newSetQuery(field, v);
}
@ -442,13 +444,13 @@ public class NumberFieldMapper extends FieldMapper {
double l = Double.NEGATIVE_INFINITY;
double u = Double.POSITIVE_INFINITY;
if (lowerTerm != null) {
l = parse(lowerTerm);
l = parse(lowerTerm, false);
if (includeLower == false) {
l = nextUp(l);
}
}
if (upperTerm != null) {
u = parse(upperTerm);
u = parse(upperTerm, false);
if (includeUpper == false) {
u = nextDown(u);
}
@ -494,13 +496,13 @@ public class NumberFieldMapper extends FieldMapper {
},
BYTE("byte", NumericType.BYTE) {
@Override
Byte parse(Object value) {
Byte parse(Object value, boolean coerce) {
if (value instanceof Number) {
double doubleValue = ((Number) value).doubleValue();
if (doubleValue < Byte.MIN_VALUE || doubleValue > Byte.MAX_VALUE) {
throw new IllegalArgumentException("Value [" + value + "] is out of range for a byte");
}
if (doubleValue % 1 != 0) {
if (!coerce && doubleValue % 1 != 0) {
throw new IllegalArgumentException("Value [" + value + "] has a decimal part");
}
return ((Number) value).byteValue();
@ -555,13 +557,13 @@ public class NumberFieldMapper extends FieldMapper {
},
SHORT("short", NumericType.SHORT) {
@Override
Short parse(Object value) {
Short parse(Object value, boolean coerce) {
if (value instanceof Number) {
double doubleValue = ((Number) value).doubleValue();
if (doubleValue < Short.MIN_VALUE || doubleValue > Short.MAX_VALUE) {
throw new IllegalArgumentException("Value [" + value + "] is out of range for a short");
}
if (doubleValue % 1 != 0) {
if (!coerce && doubleValue % 1 != 0) {
throw new IllegalArgumentException("Value [" + value + "] has a decimal part");
}
return ((Number) value).shortValue();
@ -616,13 +618,13 @@ public class NumberFieldMapper extends FieldMapper {
},
INTEGER("integer", NumericType.INT) {
@Override
Integer parse(Object value) {
Integer parse(Object value, boolean coerce) {
if (value instanceof Number) {
double doubleValue = ((Number) value).doubleValue();
if (doubleValue < Integer.MIN_VALUE || doubleValue > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Value [" + value + "] is out of range for an integer");
}
if (doubleValue % 1 != 0) {
if (!coerce && doubleValue % 1 != 0) {
throw new IllegalArgumentException("Value [" + value + "] has a decimal part");
}
return ((Number) value).intValue();
@ -640,15 +642,30 @@ public class NumberFieldMapper extends FieldMapper {
@Override
Query termQuery(String field, Object value) {
int v = parse(value);
if (hasDecimalPart(value)) {
return Queries.newMatchNoDocsQuery("Value [" + value + "] has a decimal part");
}
int v = parse(value, true);
return IntPoint.newExactQuery(field, v);
}
@Override
Query termsQuery(String field, List<Object> values) {
int[] v = new int[values.size()];
for (int i = 0; i < values.size(); ++i) {
v[i] = parse(values.get(i));
int upTo = 0;
for (int i = 0; i < values.size(); i++) {
Object value = values.get(i);
if (!hasDecimalPart(value)) {
v[upTo++] = parse(value, true);
}
}
if (upTo == 0) {
return Queries.newMatchNoDocsQuery("All values have a decimal part");
}
if (upTo != v.length) {
v = Arrays.copyOf(v, upTo);
}
return IntPoint.newSetQuery(field, v);
}
@ -659,8 +676,15 @@ public class NumberFieldMapper extends FieldMapper {
int l = Integer.MIN_VALUE;
int u = Integer.MAX_VALUE;
if (lowerTerm != null) {
l = parse(lowerTerm);
if (includeLower == false) {
l = parse(lowerTerm, true);
// if the lower bound is decimal:
// - if the bound is positive then we increment it:
// if lowerTerm=1.5 then the (inclusive) bound becomes 2
// - if the bound is negative then we leave it as is:
// if lowerTerm=-1.5 then the (inclusive) bound becomes -1 due to the call to longValue
boolean lowerTermHasDecimalPart = hasDecimalPart(lowerTerm);
if ((lowerTermHasDecimalPart == false && includeLower == false) ||
(lowerTermHasDecimalPart && signum(lowerTerm) > 0)) {
if (l == Integer.MAX_VALUE) {
return new MatchNoDocsQuery();
}
@ -668,8 +692,10 @@ public class NumberFieldMapper extends FieldMapper {
}
}
if (upperTerm != null) {
u = parse(upperTerm);
if (includeUpper == false) {
u = parse(upperTerm, true);
boolean upperTermHasDecimalPart = hasDecimalPart(upperTerm);
if ((upperTermHasDecimalPart == false && includeUpper == false) ||
(upperTermHasDecimalPart && signum(upperTerm) < 0)) {
if (u == Integer.MIN_VALUE) {
return new MatchNoDocsQuery();
}
@ -716,13 +742,13 @@ public class NumberFieldMapper extends FieldMapper {
},
LONG("long", NumericType.LONG) {
@Override
Long parse(Object value) {
Long parse(Object value, boolean coerce) {
if (value instanceof Number) {
double doubleValue = ((Number) value).doubleValue();
if (doubleValue < Long.MIN_VALUE || doubleValue > Long.MAX_VALUE) {
throw new IllegalArgumentException("Value [" + value + "] is out of range for a long");
}
if (doubleValue % 1 != 0) {
if (!coerce && doubleValue % 1 != 0) {
throw new IllegalArgumentException("Value [" + value + "] has a decimal part");
}
return ((Number) value).longValue();
@ -740,15 +766,30 @@ public class NumberFieldMapper extends FieldMapper {
@Override
Query termQuery(String field, Object value) {
long v = parse(value);
if (hasDecimalPart(value)) {
return Queries.newMatchNoDocsQuery("Value [" + value + "] has a decimal part");
}
long v = parse(value, true);
return LongPoint.newExactQuery(field, v);
}
@Override
Query termsQuery(String field, List<Object> values) {
long[] v = new long[values.size()];
for (int i = 0; i < values.size(); ++i) {
v[i] = parse(values.get(i));
int upTo = 0;
for (int i = 0; i < values.size(); i++) {
Object value = values.get(i);
if (!hasDecimalPart(value)) {
v[upTo++] = parse(value, true);
}
}
if (upTo == 0) {
return Queries.newMatchNoDocsQuery("All values have a decimal part");
}
if (upTo != v.length) {
v = Arrays.copyOf(v, upTo);
}
return LongPoint.newSetQuery(field, v);
}
@ -759,8 +800,15 @@ public class NumberFieldMapper extends FieldMapper {
long l = Long.MIN_VALUE;
long u = Long.MAX_VALUE;
if (lowerTerm != null) {
l = parse(lowerTerm);
if (includeLower == false) {
l = parse(lowerTerm, true);
// if the lower bound is decimal:
// - if the bound is positive then we increment it:
// if lowerTerm=1.5 then the (inclusive) bound becomes 2
// - if the bound is negative then we leave it as is:
// if lowerTerm=-1.5 then the (inclusive) bound becomes -1 due to the call to longValue
boolean lowerTermHasDecimalPart = hasDecimalPart(lowerTerm);
if ((lowerTermHasDecimalPart == false && includeLower == false) ||
(lowerTermHasDecimalPart && signum(lowerTerm) > 0)) {
if (l == Long.MAX_VALUE) {
return new MatchNoDocsQuery();
}
@ -768,8 +816,10 @@ public class NumberFieldMapper extends FieldMapper {
}
}
if (upperTerm != null) {
u = parse(upperTerm);
if (includeUpper == false) {
u = parse(upperTerm, true);
boolean upperTermHasDecimalPart = hasDecimalPart(upperTerm);
if ((upperTermHasDecimalPart == false && includeUpper == false) ||
(upperTermHasDecimalPart && signum(upperTerm) < 0)) {
if (u == Long.MIN_VALUE) {
return new MatchNoDocsQuery();
}
@ -827,7 +877,7 @@ public class NumberFieldMapper extends FieldMapper {
public final String typeName() {
return name;
}
/** Get the associated numerit type */
/** Get the associated numeric type */
final NumericType numericType() {
return numericType;
}
@ -836,7 +886,7 @@ public class NumberFieldMapper extends FieldMapper {
abstract Query rangeQuery(String field, Object lowerTerm, Object upperTerm,
boolean includeLower, boolean includeUpper);
abstract Number parse(XContentParser parser, boolean coerce) throws IOException;
abstract Number parse(Object value);
abstract Number parse(Object value, boolean coerce);
public abstract List<Field> createFields(String name, Number value, boolean indexed,
boolean docValued, boolean stored);
abstract FieldStats<? extends Number> stats(IndexReader reader, String fieldName,
@ -844,6 +894,38 @@ public class NumberFieldMapper extends FieldMapper {
Number valueForSearch(Number value) {
return value;
}
/**
* Returns true if the object is a number and has a decimal part
*/
boolean hasDecimalPart(Object number) {
if (number instanceof Number) {
double doubleValue = ((Number) number).doubleValue();
return doubleValue % 1 != 0;
}
if (number instanceof BytesRef) {
number = ((BytesRef) number).utf8ToString();
}
if (number instanceof String) {
return Double.parseDouble((String) number) % 1 != 0;
}
return false;
}
/**
* Returns -1, 0, or 1 if the value is lower than, equal to, or greater than 0
*/
double signum(Object value) {
if (value instanceof Number) {
double doubleValue = ((Number) value).doubleValue();
return Math.signum(doubleValue);
}
if (value instanceof BytesRef) {
value = ((BytesRef) value).utf8ToString();
}
return Math.signum(Double.parseDouble(value.toString()));
}
}
public static final class NumberFieldType extends MappedFieldType {
@ -1014,7 +1096,7 @@ public class NumberFieldMapper extends FieldMapper {
}
if (numericValue == null) {
numericValue = fieldType().type.parse(value);
numericValue = fieldType().type.parse(value, coerce.value());
}
if (includeInAll) {

View File

@ -728,8 +728,8 @@ public class RangeFieldMapper extends FieldMapper {
public Query rangeQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo,
ShapeRelation relation, @Nullable DateTimeZone timeZone, @Nullable DateMathParser dateMathParser,
QueryShardContext context) {
Number lower = from == null ? minValue() : numberType.parse(from);
Number upper = to == null ? maxValue() : numberType.parse(to);
Number lower = from == null ? minValue() : numberType.parse(from, false);
Number upper = to == null ? maxValue() : numberType.parse(to, false);
if (relation == ShapeRelation.WITHIN) {
return withinQuery(field, lower, upper, includeFrom, includeTo);
} else if (relation == ShapeRelation.CONTAINS) {

View File

@ -144,7 +144,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
if (propNode == null) {
throw new MapperParsingException("Property [null_value] cannot be null.");
}
builder.nullValue(NumberFieldMapper.NumberType.DOUBLE.parse(propNode));
builder.nullValue(NumberFieldMapper.NumberType.DOUBLE.parse(propNode, false));
iterator.remove();
} else if (propName.equals("ignore_malformed")) {
builder.ignoreMalformed(TypeParsers.nodeBooleanValue("ignore_malformed", propNode, parserContext));
@ -153,7 +153,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
builder.coerce(TypeParsers.nodeBooleanValue("coerce", propNode, parserContext));
iterator.remove();
} else if (propName.equals("scaling_factor")) {
builder.scalingFactor(NumberFieldMapper.NumberType.DOUBLE.parse(propNode).doubleValue());
builder.scalingFactor(NumberFieldMapper.NumberType.DOUBLE.parse(propNode, false).doubleValue());
iterator.remove();
}
}
@ -207,7 +207,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
@Override
public Query termQuery(Object value, QueryShardContext context) {
failIfNotIndexed();
double queryValue = NumberFieldMapper.NumberType.DOUBLE.parse(value).doubleValue();
double queryValue = NumberFieldMapper.NumberType.DOUBLE.parse(value, false).doubleValue();
long scaledValue = Math.round(queryValue * scalingFactor);
Query query = NumberFieldMapper.NumberType.LONG.termQuery(name(), scaledValue);
if (boost() != 1f) {
@ -221,7 +221,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
failIfNotIndexed();
List<Long> scaledValues = new ArrayList<>(values.size());
for (Object value : values) {
double queryValue = NumberFieldMapper.NumberType.DOUBLE.parse(value).doubleValue();
double queryValue = NumberFieldMapper.NumberType.DOUBLE.parse(value, false).doubleValue();
long scaledValue = Math.round(queryValue * scalingFactor);
scaledValues.add(scaledValue);
}
@ -237,7 +237,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
failIfNotIndexed();
Long lo = null;
if (lowerTerm != null) {
double dValue = NumberFieldMapper.NumberType.DOUBLE.parse(lowerTerm).doubleValue();
double dValue = NumberFieldMapper.NumberType.DOUBLE.parse(lowerTerm, false).doubleValue();
if (includeLower == false) {
dValue = Math.nextUp(dValue);
}
@ -245,7 +245,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
}
Long hi = null;
if (upperTerm != null) {
double dValue = NumberFieldMapper.NumberType.DOUBLE.parse(upperTerm).doubleValue();
double dValue = NumberFieldMapper.NumberType.DOUBLE.parse(upperTerm, false).doubleValue();
if (includeUpper == false) {
dValue = Math.nextDown(dValue);
}
@ -404,7 +404,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
}
if (numericValue == null) {
numericValue = NumberFieldMapper.NumberType.DOUBLE.parse(value);
numericValue = NumberFieldMapper.NumberType.DOUBLE.parse(value, false);
}
if (includeInAll) {

View File

@ -20,26 +20,26 @@
package org.elasticsearch.index.mapper;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.FloatPoint;
import org.apache.lucene.document.HalfFloatPoint;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.NumberFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType.Relation;
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
import org.junit.Before;
import java.io.IOException;
import java.util.Arrays;
public class NumberFieldTypeTests extends FieldTypeTestCase {
@ -62,6 +62,52 @@ public class NumberFieldTypeTests extends FieldTypeTestCase {
randomBoolean(), randomBoolean(), null, null, null));
}
public void testIntegerTermsQueryWithDecimalPart() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.INTEGER);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertEquals(IntPoint.newSetQuery("field", 1), ft.termsQuery(Arrays.asList(1, 2.1), null));
assertEquals(IntPoint.newSetQuery("field", 1), ft.termsQuery(Arrays.asList(1.0, 2.1), null));
assertTrue(ft.termsQuery(Arrays.asList(1.1, 2.1), null) instanceof MatchNoDocsQuery);
}
public void testLongTermsQueryWithDecimalPart() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.LONG);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertEquals(LongPoint.newSetQuery("field", 1), ft.termsQuery(Arrays.asList(1, 2.1), null));
assertEquals(LongPoint.newSetQuery("field", 1), ft.termsQuery(Arrays.asList(1.0, 2.1), null));
assertTrue(ft.termsQuery(Arrays.asList(1.1, 2.1), null) instanceof MatchNoDocsQuery);
}
public void testByteTermQueryWithDecimalPart() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.BYTE);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertTrue(ft.termQuery(42.1, null) instanceof MatchNoDocsQuery);
}
public void testShortTermQueryWithDecimalPart() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.SHORT);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertTrue(ft.termQuery(42.1, null) instanceof MatchNoDocsQuery);
}
public void testIntegerTermQueryWithDecimalPart() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.INTEGER);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertTrue(ft.termQuery(42.1, null) instanceof MatchNoDocsQuery);
}
public void testLongTermQueryWithDecimalPart() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertTrue(ft.termQuery(42.1, null) instanceof MatchNoDocsQuery);
}
public void testTermQuery() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG);
ft.setName("field");
@ -74,6 +120,82 @@ public class NumberFieldTypeTests extends FieldTypeTestCase {
assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage());
}
public void testRangeQueryWithNegativeBounds() {
MappedFieldType ftInt = new NumberFieldMapper.NumberFieldType(NumberType.INTEGER);
ftInt.setName("field");
ftInt.setIndexOptions(IndexOptions.DOCS);
assertEquals(IntPoint.newRangeQuery("field", -3, -3), ftInt.rangeQuery(-3.5, -2.5, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", -3, -3), ftInt.rangeQuery(-3.5, -2.5, false, false, null));
assertEquals(IntPoint.newRangeQuery("field", 0, 0), ftInt.rangeQuery(-0.5, 0.5, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 0, 0), ftInt.rangeQuery(-0.5, 0.5, false, false, null));
assertEquals(IntPoint.newRangeQuery("field", 1, 2), ftInt.rangeQuery(0.5, 2.5, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 1, 2), ftInt.rangeQuery(0.5, 2.5, false, false, null));
assertEquals(IntPoint.newRangeQuery("field", 0, 2), ftInt.rangeQuery(-0.5, 2.5, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 0, 2), ftInt.rangeQuery(-0.5, 2.5, false, false, null));
assertEquals(IntPoint.newRangeQuery("field", -2, 0), ftInt.rangeQuery(-2.5, 0.5, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", -2, 0), ftInt.rangeQuery(-2.5, 0.5, false, false, null));
assertEquals(IntPoint.newRangeQuery("field", -2, -1), ftInt.rangeQuery(-2.5, -0.5, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", -2, -1), ftInt.rangeQuery(-2.5, -0.5, false, false, null));
MappedFieldType ftLong = new NumberFieldMapper.NumberFieldType(NumberType.LONG);
ftLong.setName("field");
ftLong.setIndexOptions(IndexOptions.DOCS);
assertEquals(LongPoint.newRangeQuery("field", -3, -3), ftLong.rangeQuery(-3.5, -2.5, true, true, null));
assertEquals(LongPoint.newRangeQuery("field", -3, -3), ftLong.rangeQuery(-3.5, -2.5, false, false, null));
assertEquals(LongPoint.newRangeQuery("field", 0, 0), ftLong.rangeQuery(-0.5, 0.5, true, true, null));
assertEquals(LongPoint.newRangeQuery("field", 0, 0), ftLong.rangeQuery(-0.5, 0.5, false, false, null));
assertEquals(LongPoint.newRangeQuery("field", 1, 2), ftLong.rangeQuery(0.5, 2.5, true, true, null));
assertEquals(LongPoint.newRangeQuery("field", 1, 2), ftLong.rangeQuery(0.5, 2.5, false, false, null));
assertEquals(LongPoint.newRangeQuery("field", 0, 2), ftLong.rangeQuery(-0.5, 2.5, true, true, null));
assertEquals(LongPoint.newRangeQuery("field", 0, 2), ftLong.rangeQuery(-0.5, 2.5, false, false, null));
assertEquals(LongPoint.newRangeQuery("field", -2, 0), ftLong.rangeQuery(-2.5, 0.5, true, true, null));
assertEquals(LongPoint.newRangeQuery("field", -2, 0), ftLong.rangeQuery(-2.5, 0.5, false, false, null));
assertEquals(LongPoint.newRangeQuery("field", -2, -1), ftLong.rangeQuery(-2.5, -0.5, true, true, null));
assertEquals(LongPoint.newRangeQuery("field", -2, -1), ftLong.rangeQuery(-2.5, -0.5, false, false, null));
}
public void testByteRangeQueryWithDecimalParts() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.BYTE);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertEquals(IntPoint.newRangeQuery("field", 2, 10), ft.rangeQuery(1.1, 10, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 2, 10), ft.rangeQuery(1.1, 10, false, true, null));
assertEquals(IntPoint.newRangeQuery("field", 1, 10), ft.rangeQuery(1, 10.1, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 1, 10), ft.rangeQuery(1, 10.1, true, false, null));
}
public void testShortRangeQueryWithDecimalParts() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.SHORT);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertEquals(IntPoint.newRangeQuery("field", 2, 10), ft.rangeQuery(1.1, 10, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 2, 10), ft.rangeQuery(1.1, 10, false, true, null));
assertEquals(IntPoint.newRangeQuery("field", 1, 10), ft.rangeQuery(1, 10.1, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 1, 10), ft.rangeQuery(1, 10.1, true, false, null));
}
public void testIntegerRangeQueryWithDecimalParts() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.INTEGER);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertEquals(IntPoint.newRangeQuery("field", 2, 10), ft.rangeQuery(1.1, 10, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 2, 10), ft.rangeQuery(1.1, 10, false, true, null));
assertEquals(IntPoint.newRangeQuery("field", 1, 10), ft.rangeQuery(1, 10.1, true, true, null));
assertEquals(IntPoint.newRangeQuery("field", 1, 10), ft.rangeQuery(1, 10.1, true, false, null));
}
public void testLongRangeQueryWithDecimalParts() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.LONG);
ft.setName("field");
ft.setIndexOptions(IndexOptions.DOCS);
assertEquals(LongPoint.newRangeQuery("field", 2, 10), ft.rangeQuery(1.1, 10, true, true, null));
assertEquals(LongPoint.newRangeQuery("field", 2, 10), ft.rangeQuery(1.1, 10, false, true, null));
assertEquals(LongPoint.newRangeQuery("field", 1, 10), ft.rangeQuery(1, 10.1, true, true, null));
assertEquals(LongPoint.newRangeQuery("field", 1, 10), ft.rangeQuery(1, 10.1, true, false, null));
}
public void testRangeQuery() {
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG);
ft.setName("field");
@ -87,36 +209,33 @@ public class NumberFieldTypeTests extends FieldTypeTestCase {
}
public void testConversions() {
assertEquals((byte) 3, NumberType.BYTE.parse(3d));
assertEquals((short) 3, NumberType.SHORT.parse(3d));
assertEquals(3, NumberType.INTEGER.parse(3d));
assertEquals(3L, NumberType.LONG.parse(3d));
assertEquals(3f, NumberType.HALF_FLOAT.parse(3d));
assertEquals(3f, NumberType.FLOAT.parse(3d));
assertEquals(3d, NumberType.DOUBLE.parse(3d));
assertEquals((byte) 3, NumberType.BYTE.parse(3d, true));
assertEquals((short) 3, NumberType.SHORT.parse(3d, true));
assertEquals(3, NumberType.INTEGER.parse(3d, true));
assertEquals(3L, NumberType.LONG.parse(3d, true));
assertEquals(3f, NumberType.HALF_FLOAT.parse(3d, true));
assertEquals(3f, NumberType.FLOAT.parse(3d, true));
assertEquals(3d, NumberType.DOUBLE.parse(3d, true));
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> NumberType.BYTE.parse(3.5));
assertEquals("Value [3.5] has a decimal part", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> NumberType.SHORT.parse(3.5));
assertEquals("Value [3.5] has a decimal part", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> NumberType.INTEGER.parse(3.5));
assertEquals("Value [3.5] has a decimal part", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> NumberType.LONG.parse(3.5));
assertEquals("Value [3.5] has a decimal part", e.getMessage());
assertEquals(3.5f, NumberType.FLOAT.parse(3.5));
assertEquals(3.5d, NumberType.DOUBLE.parse(3.5));
assertEquals((byte) 3, NumberType.BYTE.parse(3.5, true));
assertEquals((short) 3, NumberType.SHORT.parse(3.5, true));
assertEquals(3, NumberType.INTEGER.parse(3.5, true));
assertEquals(3L, NumberType.LONG.parse(3.5, true));
e = expectThrows(IllegalArgumentException.class, () -> NumberType.BYTE.parse(128));
assertEquals(3.5f, NumberType.FLOAT.parse(3.5, true));
assertEquals(3.5d, NumberType.DOUBLE.parse(3.5, true));
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> NumberType.BYTE.parse(128, true));
assertEquals("Value [128] is out of range for a byte", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> NumberType.SHORT.parse(65536));
e = expectThrows(IllegalArgumentException.class, () -> NumberType.SHORT.parse(65536, true));
assertEquals("Value [65536] is out of range for a short", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> NumberType.INTEGER.parse(2147483648L));
e = expectThrows(IllegalArgumentException.class, () -> NumberType.INTEGER.parse(2147483648L, true));
assertEquals("Value [2147483648] is out of range for an integer", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> NumberType.LONG.parse(10000000000000000000d));
e = expectThrows(IllegalArgumentException.class, () -> NumberType.LONG.parse(10000000000000000000d, true));
assertEquals("Value [1.0E19] is out of range for a long", e.getMessage());
assertEquals(1.1f, NumberType.HALF_FLOAT.parse(1.1));
assertEquals(1.1f, NumberType.FLOAT.parse(1.1));
assertEquals(1.1d, NumberType.DOUBLE.parse(1.1));
assertEquals(1.1f, NumberType.HALF_FLOAT.parse(1.1, true));
assertEquals(1.1f, NumberType.FLOAT.parse(1.1, true));
assertEquals(1.1d, NumberType.DOUBLE.parse(1.1, true));
}
public void testHalfFloatRange() throws IOException {