mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 10:25:15 +00:00
Support numeric bounds with decimal parts for long/integer/short/byte datatypes (#21972)
Close #21600
This commit is contained in:
parent
b9a90eca20
commit
e1b8528ab8
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user