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:
Nicholas Knize 2017-05-02 09:56:52 -05:00
parent e3766d2828
commit 0c4eb0a029
5 changed files with 201 additions and 110 deletions

View File

@ -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();
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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.

View File

@ -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]