Add new ip_range field type
This commit adds support for indexing and searching a new ip_range field type. Both IPv4 and IPv6 formats are supported. Tests are updated and docs are added.
This commit is contained in:
parent
e3766d2828
commit
0c4eb0a029
|
@ -22,6 +22,8 @@ import org.apache.lucene.document.Field;
|
|||
import org.apache.lucene.document.DoubleRange;
|
||||
import org.apache.lucene.document.FloatRange;
|
||||
import org.apache.lucene.document.IntRange;
|
||||
import org.apache.lucene.document.InetAddressPoint;
|
||||
import org.apache.lucene.document.InetAddressRange;
|
||||
import org.apache.lucene.document.LongRange;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
|
@ -29,12 +31,12 @@ import org.apache.lucene.index.IndexableField;
|
|||
import org.apache.lucene.search.BoostQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.elasticsearch.common.Explicit;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.network.InetAddresses;
|
||||
import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.LocaleUtils;
|
||||
|
@ -45,6 +47,7 @@ import org.elasticsearch.index.query.QueryShardContext;
|
|||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -341,8 +344,8 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
RangeFieldType fieldType = fieldType();
|
||||
RangeType rangeType = fieldType.rangeType;
|
||||
String fieldName = null;
|
||||
Number from = rangeType.minValue();
|
||||
Number to = rangeType.maxValue();
|
||||
Object from = rangeType.minValue();
|
||||
Object to = rangeType.maxValue();
|
||||
boolean includeFrom = DEFAULT_INCLUDE_LOWER;
|
||||
boolean includeTo = DEFAULT_INCLUDE_UPPER;
|
||||
XContentParser.Token token;
|
||||
|
@ -427,10 +430,72 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
|
||||
/** Enum defining the type of range */
|
||||
public enum RangeType {
|
||||
IP("ip_range") {
|
||||
@Override
|
||||
public Field getRangeField(String name, Range r) {
|
||||
return new InetAddressRange(name, (InetAddress)r.from, (InetAddress)r.to);
|
||||
}
|
||||
@Override
|
||||
public InetAddress parseFrom(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included)
|
||||
throws IOException {
|
||||
InetAddress address = InetAddresses.forString(parser.text());
|
||||
return included ? address : nextUp(address);
|
||||
}
|
||||
@Override
|
||||
public InetAddress parseTo(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included)
|
||||
throws IOException {
|
||||
InetAddress address = InetAddresses.forString(parser.text());
|
||||
return included ? address : nextDown(address);
|
||||
}
|
||||
@Override
|
||||
public InetAddress parse(Object value, boolean coerce) {
|
||||
return value instanceof InetAddress ? (InetAddress) value : InetAddresses.forString((String) value);
|
||||
}
|
||||
@Override
|
||||
public InetAddress minValue() {
|
||||
return InetAddressPoint.MIN_VALUE;
|
||||
}
|
||||
@Override
|
||||
public InetAddress maxValue() {
|
||||
return InetAddressPoint.MAX_VALUE;
|
||||
}
|
||||
@Override
|
||||
public InetAddress nextUp(Object value) {
|
||||
return InetAddressPoint.nextUp((InetAddress)value);
|
||||
}
|
||||
@Override
|
||||
public InetAddress nextDown(Object value) {
|
||||
return InetAddressPoint.nextDown((InetAddress)value);
|
||||
}
|
||||
@Override
|
||||
public Query withinQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) {
|
||||
InetAddress lower = (InetAddress)from;
|
||||
InetAddress upper = (InetAddress)to;
|
||||
return InetAddressRange.newWithinQuery(field,
|
||||
includeLower ? lower : nextUp(lower), includeUpper ? upper : nextDown(upper));
|
||||
}
|
||||
@Override
|
||||
public Query containsQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) {
|
||||
InetAddress lower = (InetAddress)from;
|
||||
InetAddress upper = (InetAddress)to;
|
||||
return InetAddressRange.newContainsQuery(field,
|
||||
includeLower ? lower : nextUp(lower), includeUpper ? upper : nextDown(upper));
|
||||
}
|
||||
@Override
|
||||
public Query intersectsQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) {
|
||||
InetAddress lower = (InetAddress)from;
|
||||
InetAddress upper = (InetAddress)to;
|
||||
return InetAddressRange.newIntersectsQuery(field,
|
||||
includeLower ? lower : nextUp(lower), includeUpper ? upper : nextDown(upper));
|
||||
}
|
||||
public String toString(InetAddress address) {
|
||||
return InetAddresses.toAddrString(address);
|
||||
}
|
||||
},
|
||||
DATE("date_range", NumberType.LONG) {
|
||||
@Override
|
||||
public Field getRangeField(String name, Range r) {
|
||||
return new LongRange(name, new long[] {r.from.longValue()}, new long[] {r.to.longValue()});
|
||||
return new LongRange(name, new long[] {((Number)r.from).longValue()}, new long[] {((Number)r.to).longValue()});
|
||||
}
|
||||
private Number parse(DateMathParser dateMathParser, String dateStr) {
|
||||
return dateMathParser.parse(dateStr, () -> {throw new IllegalArgumentException("now is not used at indexing time");});
|
||||
|
@ -456,16 +521,12 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
return Long.MAX_VALUE;
|
||||
}
|
||||
@Override
|
||||
public Number nextUp(Number value) {
|
||||
return LONG.nextUp(value);
|
||||
public Long nextUp(Object value) {
|
||||
return (long) LONG.nextUp(value);
|
||||
}
|
||||
@Override
|
||||
public Number nextDown(Number value) {
|
||||
return LONG.nextDown(value);
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes(Range r) {
|
||||
return LONG.getBytes(r);
|
||||
public Long nextDown(Object value) {
|
||||
return (long) LONG.nextDown(value);
|
||||
}
|
||||
@Override
|
||||
public Query rangeQuery(String field, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper,
|
||||
|
@ -484,15 +545,15 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
return super.rangeQuery(field, low, high, includeLower, includeUpper, relation, zone, dateMathParser, context);
|
||||
}
|
||||
@Override
|
||||
public Query withinQuery(String field, Number from, Number to, boolean includeLower, boolean includeUpper) {
|
||||
public Query withinQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) {
|
||||
return LONG.withinQuery(field, from, to, includeLower, includeUpper);
|
||||
}
|
||||
@Override
|
||||
public Query containsQuery(String field, Number from, Number to, boolean includeLower, boolean includeUpper) {
|
||||
public Query containsQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) {
|
||||
return LONG.containsQuery(field, from, to, includeLower, includeUpper);
|
||||
}
|
||||
@Override
|
||||
public Query intersectsQuery(String field, Number from, Number to, boolean includeLower, boolean includeUpper) {
|
||||
public Query intersectsQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) {
|
||||
return LONG.intersectsQuery(field, from, to, includeLower, includeUpper);
|
||||
}
|
||||
},
|
||||
|
@ -507,38 +568,31 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
return Float.POSITIVE_INFINITY;
|
||||
}
|
||||
@Override
|
||||
public Float nextUp(Number value) {
|
||||
return Math.nextUp(value.floatValue());
|
||||
public Float nextUp(Object value) {
|
||||
return Math.nextUp(((Number)value).floatValue());
|
||||
}
|
||||
@Override
|
||||
public Float nextDown(Number value) {
|
||||
return Math.nextDown(value.floatValue());
|
||||
public Float nextDown(Object value) {
|
||||
return Math.nextDown(((Number)value).floatValue());
|
||||
}
|
||||
@Override
|
||||
public Field getRangeField(String name, Range r) {
|
||||
return new FloatRange(name, new float[] {r.from.floatValue()}, new float[] {r.to.floatValue()});
|
||||
return new FloatRange(name, new float[] {((Number)r.from).floatValue()}, new float[] {((Number)r.to).floatValue()});
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes(Range r) {
|
||||
byte[] b = new byte[Float.BYTES*2];
|
||||
NumericUtils.intToSortableBytes(NumericUtils.floatToSortableInt(r.from.floatValue()), b, 0);
|
||||
NumericUtils.intToSortableBytes(NumericUtils.floatToSortableInt(r.to.floatValue()), b, Float.BYTES);
|
||||
return b;
|
||||
}
|
||||
@Override
|
||||
public Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return FloatRange.newWithinQuery(field,
|
||||
new float[] {includeFrom ? (Float)from : Math.nextUp((Float)from)},
|
||||
new float[] {includeTo ? (Float)to : Math.nextDown((Float)to)});
|
||||
}
|
||||
@Override
|
||||
public Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return FloatRange.newContainsQuery(field,
|
||||
new float[] {includeFrom ? (Float)from : Math.nextUp((Float)from)},
|
||||
new float[] {includeTo ? (Float)to : Math.nextDown((Float)to)});
|
||||
}
|
||||
@Override
|
||||
public Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return FloatRange.newIntersectsQuery(field,
|
||||
new float[] {includeFrom ? (Float)from : Math.nextUp((Float)from)},
|
||||
new float[] {includeTo ? (Float)to : Math.nextDown((Float)to)});
|
||||
|
@ -554,38 +608,31 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
@Override
|
||||
public Double nextUp(Number value) {
|
||||
return Math.nextUp(value.doubleValue());
|
||||
public Double nextUp(Object value) {
|
||||
return Math.nextUp(((Number)value).doubleValue());
|
||||
}
|
||||
@Override
|
||||
public Double nextDown(Number value) {
|
||||
return Math.nextDown(value.doubleValue());
|
||||
public Double nextDown(Object value) {
|
||||
return Math.nextDown(((Number)value).doubleValue());
|
||||
}
|
||||
@Override
|
||||
public Field getRangeField(String name, Range r) {
|
||||
return new DoubleRange(name, new double[] {r.from.doubleValue()}, new double[] {r.to.doubleValue()});
|
||||
return new DoubleRange(name, new double[] {((Number)r.from).doubleValue()}, new double[] {((Number)r.to).doubleValue()});
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes(Range r) {
|
||||
byte[] b = new byte[Double.BYTES*2];
|
||||
NumericUtils.longToSortableBytes(NumericUtils.doubleToSortableLong(r.from.doubleValue()), b, 0);
|
||||
NumericUtils.longToSortableBytes(NumericUtils.doubleToSortableLong(r.to.doubleValue()), b, Double.BYTES);
|
||||
return b;
|
||||
}
|
||||
@Override
|
||||
public Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return DoubleRange.newWithinQuery(field,
|
||||
new double[] {includeFrom ? (Double)from : Math.nextUp((Double)from)},
|
||||
new double[] {includeTo ? (Double)to : Math.nextDown((Double)to)});
|
||||
}
|
||||
@Override
|
||||
public Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return DoubleRange.newContainsQuery(field,
|
||||
new double[] {includeFrom ? (Double)from : Math.nextUp((Double)from)},
|
||||
new double[] {includeTo ? (Double)to : Math.nextDown((Double)to)});
|
||||
}
|
||||
@Override
|
||||
public Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return DoubleRange.newIntersectsQuery(field,
|
||||
new double[] {includeFrom ? (Double)from : Math.nextUp((Double)from)},
|
||||
new double[] {includeTo ? (Double)to : Math.nextDown((Double)to)});
|
||||
|
@ -603,36 +650,29 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
return Integer.MAX_VALUE;
|
||||
}
|
||||
@Override
|
||||
public Integer nextUp(Number value) {
|
||||
return value.intValue() + 1;
|
||||
public Integer nextUp(Object value) {
|
||||
return ((Number)value).intValue() + 1;
|
||||
}
|
||||
@Override
|
||||
public Integer nextDown(Number value) {
|
||||
return value.intValue() - 1;
|
||||
public Integer nextDown(Object value) {
|
||||
return ((Number)value).intValue() - 1;
|
||||
}
|
||||
@Override
|
||||
public Field getRangeField(String name, Range r) {
|
||||
return new IntRange(name, new int[] {r.from.intValue()}, new int[] {r.to.intValue()});
|
||||
return new IntRange(name, new int[] {((Number)r.from).intValue()}, new int[] {((Number)r.to).intValue()});
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes(Range r) {
|
||||
byte[] b = new byte[Integer.BYTES*2];
|
||||
NumericUtils.intToSortableBytes(r.from.intValue(), b, 0);
|
||||
NumericUtils.intToSortableBytes(r.to.intValue(), b, Integer.BYTES);
|
||||
return b;
|
||||
}
|
||||
@Override
|
||||
public Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return IntRange.newWithinQuery(field, new int[] {(Integer)from + (includeFrom ? 0 : 1)},
|
||||
new int[] {(Integer)to - (includeTo ? 0 : 1)});
|
||||
}
|
||||
@Override
|
||||
public Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return IntRange.newContainsQuery(field, new int[] {(Integer)from + (includeFrom ? 0 : 1)},
|
||||
new int[] {(Integer)to - (includeTo ? 0 : 1)});
|
||||
}
|
||||
@Override
|
||||
public Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return IntRange.newIntersectsQuery(field, new int[] {(Integer)from + (includeFrom ? 0 : 1)},
|
||||
new int[] {(Integer)to - (includeTo ? 0 : 1)});
|
||||
}
|
||||
|
@ -647,43 +687,40 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
return Long.MAX_VALUE;
|
||||
}
|
||||
@Override
|
||||
public Long nextUp(Number value) {
|
||||
return value.longValue() + 1;
|
||||
public Long nextUp(Object value) {
|
||||
return ((Number)value).longValue() + 1;
|
||||
}
|
||||
@Override
|
||||
public Long nextDown(Number value) {
|
||||
return value.longValue() - 1;
|
||||
public Long nextDown(Object value) {
|
||||
return ((Number)value).longValue() - 1;
|
||||
}
|
||||
@Override
|
||||
public Field getRangeField(String name, Range r) {
|
||||
return new LongRange(name, new long[] {r.from.longValue()}, new long[] {r.to.longValue()});
|
||||
return new LongRange(name, new long[] {((Number)r.from).longValue()},
|
||||
new long[] {((Number)r.to).longValue()});
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes(Range r) {
|
||||
byte[] b = new byte[Long.BYTES*2];
|
||||
long from = r.from == null ? Long.MIN_VALUE : r.from.longValue();
|
||||
long to = r.to == null ? Long.MAX_VALUE : r.to.longValue();
|
||||
NumericUtils.longToSortableBytes(from, b, 0);
|
||||
NumericUtils.longToSortableBytes(to, b, Long.BYTES);
|
||||
return b;
|
||||
}
|
||||
@Override
|
||||
public Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return LongRange.newWithinQuery(field, new long[] {(Long)from + (includeFrom ? 0 : 1)},
|
||||
new long[] {(Long)to - (includeTo ? 0 : 1)});
|
||||
}
|
||||
@Override
|
||||
public Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return LongRange.newContainsQuery(field, new long[] {(Long)from + (includeFrom ? 0 : 1)},
|
||||
new long[] {(Long)to - (includeTo ? 0 : 1)});
|
||||
}
|
||||
@Override
|
||||
public Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
return LongRange.newIntersectsQuery(field, new long[] {(Long)from + (includeFrom ? 0 : 1)},
|
||||
new long[] {(Long)to - (includeTo ? 0 : 1)});
|
||||
}
|
||||
};
|
||||
|
||||
RangeType(String name) {
|
||||
this.name = name;
|
||||
this.numberType = null;
|
||||
}
|
||||
|
||||
RangeType(String name, NumberType type) {
|
||||
this.name = name;
|
||||
this.numberType = type;
|
||||
|
@ -694,7 +731,6 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
return name;
|
||||
}
|
||||
|
||||
protected abstract byte[] getBytes(Range range);
|
||||
public abstract Field getRangeField(String name, Range range);
|
||||
public List<IndexableField> createFields(String name, Range range, boolean indexed, boolean docValued, boolean stored) {
|
||||
assert range != null : "range cannot be null when creating fields";
|
||||
|
@ -709,29 +745,31 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
return fields;
|
||||
}
|
||||
/** parses from value. rounds according to included flag */
|
||||
public Number parseFrom(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) throws IOException {
|
||||
public Object parseFrom(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) throws IOException {
|
||||
Number value = numberType.parse(parser, coerce);
|
||||
return included ? value : nextUp(value);
|
||||
return included ? value : (Number)nextUp(value);
|
||||
}
|
||||
/** parses to value. rounds according to included flag */
|
||||
public Number parseTo(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) throws IOException {
|
||||
public Object parseTo(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) throws IOException {
|
||||
Number value = numberType.parse(parser, coerce);
|
||||
return included ? value : nextDown(value);
|
||||
return included ? value : (Number)nextDown(value);
|
||||
}
|
||||
|
||||
public abstract Number minValue();
|
||||
public abstract Number maxValue();
|
||||
public abstract Number nextUp(Number value);
|
||||
public abstract Number nextDown(Number value);
|
||||
public abstract Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo);
|
||||
public abstract Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo);
|
||||
public abstract Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo);
|
||||
|
||||
public abstract Object minValue();
|
||||
public abstract Object maxValue();
|
||||
public abstract Object nextUp(Object value);
|
||||
public abstract Object nextDown(Object value);
|
||||
public abstract Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo);
|
||||
public abstract Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo);
|
||||
public abstract Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo);
|
||||
public Object parse(Object value, boolean coerce) {
|
||||
return numberType.parse(value, coerce);
|
||||
}
|
||||
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, false);
|
||||
Number upper = to == null ? maxValue() : numberType.parse(to, false);
|
||||
Object lower = from == null ? minValue() : parse(from, false);
|
||||
Object upper = to == null ? maxValue() : parse(to, false);
|
||||
if (relation == ShapeRelation.WITHIN) {
|
||||
return withinQuery(field, lower, upper, includeFrom, includeTo);
|
||||
} else if (relation == ShapeRelation.CONTAINS) {
|
||||
|
@ -747,12 +785,12 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
/** Class defining a range */
|
||||
public static class Range {
|
||||
RangeType type;
|
||||
private Number from;
|
||||
private Number to;
|
||||
private Object from;
|
||||
private Object to;
|
||||
private boolean includeFrom;
|
||||
private boolean includeTo;
|
||||
|
||||
public Range(RangeType type, Number from, Number to, boolean includeFrom, boolean includeTo) {
|
||||
public Range(RangeType type, Object from, Object to, boolean includeFrom, boolean includeTo) {
|
||||
this.type = type;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
|
@ -764,9 +802,11 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(includeFrom ? '[' : '(');
|
||||
sb.append(includeFrom || from.equals(type.minValue()) ? from : type.nextDown(from));
|
||||
sb.append(':');
|
||||
sb.append(includeTo || to.equals(type.maxValue()) ? to : type.nextUp(to));
|
||||
Object f = includeFrom || from.equals(type.minValue()) ? from : type.nextDown(from);
|
||||
Object t = includeTo || to.equals(type.maxValue()) ? to : type.nextUp(to);
|
||||
sb.append(type == RangeType.IP ? InetAddresses.toAddrString((InetAddress)f) : f.toString());
|
||||
sb.append(" : ");
|
||||
sb.append(type == RangeType.IP ? InetAddresses.toAddrString((InetAddress)t) : t.toString());
|
||||
sb.append(includeTo ? ']' : ')');
|
||||
return sb.toString();
|
||||
}
|
||||
|
|
|
@ -18,14 +18,17 @@
|
|||
*/
|
||||
package org.elasticsearch.index.mapper;
|
||||
|
||||
import org.apache.lucene.document.InetAddressPoint;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.network.InetAddresses;
|
||||
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 java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
@ -40,6 +43,8 @@ import static org.hamcrest.Matchers.containsString;
|
|||
public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
|
||||
private static String FROM_DATE = "2016-10-31";
|
||||
private static String TO_DATE = "2016-11-01 20:00:00";
|
||||
private static String FROM_IP = "::ffff:c0a8:107";
|
||||
private static String TO_IP = "2001:db8::";
|
||||
private static int FROM = 5;
|
||||
private static String FROM_STR = FROM + "";
|
||||
private static int TO = 10;
|
||||
|
@ -48,12 +53,14 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
|
|||
|
||||
@Override
|
||||
protected void setTypeList() {
|
||||
TYPES = new HashSet<>(Arrays.asList("date_range", "float_range", "double_range", "integer_range", "long_range"));
|
||||
TYPES = new HashSet<>(Arrays.asList("date_range", "ip_range", "float_range", "double_range", "integer_range", "long_range"));
|
||||
}
|
||||
|
||||
private Object getFrom(String type) {
|
||||
if (type.equals("date_range")) {
|
||||
return FROM_DATE;
|
||||
} else if (type.equals("ip_range")) {
|
||||
return FROM_IP;
|
||||
}
|
||||
return random().nextBoolean() ? FROM : FROM_STR;
|
||||
}
|
||||
|
@ -69,13 +76,17 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
|
|||
private Object getTo(String type) {
|
||||
if (type.equals("date_range")) {
|
||||
return TO_DATE;
|
||||
} else if (type.equals("ip_range")) {
|
||||
return TO_IP;
|
||||
}
|
||||
return random().nextBoolean() ? TO : TO_STR;
|
||||
}
|
||||
|
||||
private Number getMax(String type) {
|
||||
private Object getMax(String type) {
|
||||
if (type.equals("date_range") || type.equals("long_range")) {
|
||||
return Long.MAX_VALUE;
|
||||
} else if (type.equals("ip_range")) {
|
||||
return InetAddressPoint.MAX_VALUE;
|
||||
} else if (type.equals("integer_range")) {
|
||||
return Integer.MAX_VALUE;
|
||||
} else if (type.equals("float_range")) {
|
||||
|
@ -189,7 +200,14 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
|
|||
assertEquals(2, pointField.fieldType().pointDimensionCount());
|
||||
IndexableField storedField = fields[1];
|
||||
assertTrue(storedField.fieldType().stored());
|
||||
assertThat(storedField.stringValue(), containsString(type.equals("date_range") ? "1477872000000" : "5"));
|
||||
String strVal = "5";
|
||||
if (type.equals("date_range")) {
|
||||
strVal = "1477872000000";
|
||||
} else if (type.equals("ip_range")) {
|
||||
strVal = InetAddresses.toAddrString(InetAddresses.forString("192.168.1.7")) + " : "
|
||||
+ InetAddresses.toAddrString(InetAddresses.forString("2001:db8:0:0:0:0:0:0"));
|
||||
}
|
||||
assertThat(storedField.stringValue(), containsString(strVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -234,7 +252,8 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
|
|||
.endObject().bytes(),
|
||||
XContentType.JSON));
|
||||
MapperParsingException e = expectThrows(MapperParsingException.class, runnable);
|
||||
assertThat(e.getCause().getMessage(), anyOf(containsString("passed as String"), containsString("failed to parse date")));
|
||||
assertThat(e.getCause().getMessage(), anyOf(containsString("passed as String"),
|
||||
containsString("failed to parse date"), containsString("is not an IP string literal")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -261,7 +280,8 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
|
|||
assertEquals(2, doc.rootDoc().getFields("field").length);
|
||||
IndexableField[] fields = doc.rootDoc().getFields("field");
|
||||
IndexableField storedField = fields[1];
|
||||
assertThat(storedField.stringValue(), containsString(type.equals("date_range") ? Long.MAX_VALUE+"" : getMax(type)+""));
|
||||
String expected = type.equals("ip_range") ? InetAddresses.toAddrString((InetAddress)getMax(type)) : getMax(type) +"";
|
||||
assertThat(storedField.stringValue(), containsString(expected));
|
||||
|
||||
// test null max value
|
||||
doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder()
|
||||
|
@ -280,8 +300,14 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
|
|||
assertFalse(pointField.fieldType().stored());
|
||||
storedField = fields[1];
|
||||
assertTrue(storedField.fieldType().stored());
|
||||
assertThat(storedField.stringValue(), containsString(type.equals("date_range") ? "1477872000000" : "5"));
|
||||
assertThat(storedField.stringValue(), containsString(getMax(type) + ""));
|
||||
String strVal = "5";
|
||||
if (type.equals("date_range")) {
|
||||
strVal = "1477872000000";
|
||||
} else if (type.equals("ip_range")) {
|
||||
strVal = InetAddresses.toAddrString(InetAddresses.forString("192.168.1.7")) + " : "
|
||||
+ InetAddresses.toAddrString(InetAddresses.forString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
|
||||
}
|
||||
assertThat(storedField.stringValue(), containsString(strVal));
|
||||
}
|
||||
|
||||
public void testNoBounds() throws Exception {
|
||||
|
@ -316,8 +342,8 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
|
|||
assertFalse(pointField.fieldType().stored());
|
||||
IndexableField storedField = fields[1];
|
||||
assertTrue(storedField.fieldType().stored());
|
||||
assertThat(storedField.stringValue(), containsString(type.equals("date_range") ? Long.MAX_VALUE+"" : getMax(type)+""));
|
||||
assertThat(storedField.stringValue(), containsString(getMax(type) + ""));
|
||||
String expected = type.equals("ip_range") ? InetAddresses.toAddrString((InetAddress)getMax(type)) : getMax(type) +"";
|
||||
assertThat(storedField.stringValue(), containsString(expected));
|
||||
}
|
||||
|
||||
public void testIllegalArguments() throws Exception {
|
||||
|
|
|
@ -21,6 +21,8 @@ package org.elasticsearch.index.mapper;
|
|||
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||
import org.apache.lucene.document.DoubleRange;
|
||||
import org.apache.lucene.document.FloatRange;
|
||||
import org.apache.lucene.document.InetAddressPoint;
|
||||
import org.apache.lucene.document.InetAddressRange;
|
||||
import org.apache.lucene.document.IntRange;
|
||||
import org.apache.lucene.document.LongRange;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
|
@ -37,6 +39,7 @@ import org.elasticsearch.test.IndexSettingsModule;
|
|||
import org.joda.time.DateTime;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Locale;
|
||||
|
||||
public class RangeFieldTypeTests extends FieldTypeTestCase {
|
||||
|
@ -100,6 +103,8 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
|
|||
return getLongRangeQuery(relation, (long)from, (long)to, includeLower, includeUpper);
|
||||
case DOUBLE:
|
||||
return getDoubleRangeQuery(relation, (double)from, (double)to, includeLower, includeUpper);
|
||||
case IP:
|
||||
return getInetAddressRangeQuery(relation, (InetAddress)from, (InetAddress)to, includeLower, includeUpper);
|
||||
default:
|
||||
return getFloatRangeQuery(relation, (float)from, (float)to, includeLower, includeUpper);
|
||||
}
|
||||
|
@ -142,7 +147,8 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
|
|||
return FloatRange.newIntersectsQuery(FIELDNAME, lower, upper);
|
||||
}
|
||||
|
||||
private Query getDoubleRangeQuery(ShapeRelation relation, double from, double to, boolean includeLower, boolean includeUpper) {
|
||||
private Query getDoubleRangeQuery(ShapeRelation relation, double from, double to, boolean includeLower,
|
||||
boolean includeUpper) {
|
||||
double[] lower = new double[] {includeLower ? from : Math.nextUp(from)};
|
||||
double[] upper = new double[] {includeUpper ? to : Math.nextDown(to)};
|
||||
if (relation == ShapeRelation.WITHIN) {
|
||||
|
@ -153,7 +159,19 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
|
|||
return DoubleRange.newIntersectsQuery(FIELDNAME, lower, upper);
|
||||
}
|
||||
|
||||
private Object nextFrom() {
|
||||
private Query getInetAddressRangeQuery(ShapeRelation relation, InetAddress from, InetAddress to, boolean includeLower,
|
||||
boolean includeUpper) {
|
||||
InetAddress lower = includeLower ? from : InetAddressPoint.nextUp(from);
|
||||
InetAddress upper = includeUpper ? to : InetAddressPoint.nextDown(to);
|
||||
if (relation == ShapeRelation.WITHIN) {
|
||||
return InetAddressRange.newWithinQuery(FIELDNAME, lower, upper);
|
||||
} else if (relation == ShapeRelation.CONTAINS) {
|
||||
return InetAddressRange.newContainsQuery(FIELDNAME, lower, upper);
|
||||
}
|
||||
return InetAddressRange.newIntersectsQuery(FIELDNAME, lower, upper);
|
||||
}
|
||||
|
||||
private Object nextFrom() throws Exception {
|
||||
switch (type) {
|
||||
case INTEGER:
|
||||
return (int)(random().nextInt() * 0.5 - DISTANCE);
|
||||
|
@ -163,12 +181,14 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
|
|||
return (long)(random().nextLong() * 0.5 - DISTANCE);
|
||||
case FLOAT:
|
||||
return (float)(random().nextFloat() * 0.5 - DISTANCE);
|
||||
case IP:
|
||||
return InetAddress.getByName("::ffff:c0a8:107");
|
||||
default:
|
||||
return random().nextDouble() * 0.5 - DISTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
private Object nextTo(Object from) {
|
||||
private Object nextTo(Object from) throws Exception {
|
||||
switch (type) {
|
||||
case INTEGER:
|
||||
return (Integer)from + DISTANCE;
|
||||
|
@ -178,6 +198,8 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
|
|||
return (Long)from + DISTANCE;
|
||||
case DOUBLE:
|
||||
return (Double)from + DISTANCE;
|
||||
case IP:
|
||||
return InetAddress.getByName("2001:db8::");
|
||||
default:
|
||||
return (Float)from + DISTANCE;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ The following range types are supported:
|
|||
`long_range`:: A range of signed 64-bit integers with a minimum value of +-2^63^+ and maximum of +2^63^-1+.
|
||||
`double_range`:: A range of double-precision 64-bit IEEE 754 floating point values.
|
||||
`date_range`:: A range of date values represented as unsigned 64-bit integer milliseconds elapsed since system epoch.
|
||||
`ip_range` :: A range of ip values supporting either https://en.wikipedia.org/wiki/IPv4[IPv4] or
|
||||
https://en.wikipedia.org/wiki/IPv6[IPv6] (or mixed) addresses.
|
||||
|
||||
Below is an example of configuring a mapping with various range fields followed by an example that indexes several range types.
|
||||
|
||||
|
|
|
@ -147,6 +147,7 @@ Internal::
|
|||
|
||||
Core::
|
||||
* Enable index-time sorting {pull}24055[#24055] (issue: {issue}6720[#6720])
|
||||
* Add new ip_range field type {pull}24433[#24433]
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue