mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-29 01:44:36 -04:00
Format default values of IP ranges to match other range bound
This commit is contained in:
parent
a2fdc53053
commit
acb513945b
5 changed files with 129 additions and 59 deletions
|
@ -323,7 +323,7 @@ setup:
|
||||||
index:
|
index:
|
||||||
index: synthetic_source_test
|
index: synthetic_source_test
|
||||||
id: "4"
|
id: "4"
|
||||||
body: { "ip_range" : { "gt": "192.168.0.4", "lt": "192.168.0.8" } }
|
body: { "ip_range" : { "gt": "2001:db8::", "lt": "200a:100::" } }
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
index:
|
index:
|
||||||
|
@ -347,7 +347,7 @@ setup:
|
||||||
index:
|
index:
|
||||||
index: synthetic_source_test
|
index: synthetic_source_test
|
||||||
id: "8"
|
id: "8"
|
||||||
body: { "ip_range": { "gte": "10.10.10.10" } }
|
body: { "ip_range": { "gte": "2001:db8::" } }
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
indices.refresh: {}
|
indices.refresh: {}
|
||||||
|
@ -367,7 +367,7 @@ setup:
|
||||||
ip_range: { "gte": "192.168.0.4", "lte": "192.168.0.4" }
|
ip_range: { "gte": "192.168.0.4", "lte": "192.168.0.4" }
|
||||||
- match:
|
- match:
|
||||||
hits.hits.3._source:
|
hits.hits.3._source:
|
||||||
ip_range: { "gte": "192.168.0.5", "lte": "192.168.0.7" }
|
ip_range: { "gte": "2001:db8::1", "lte": "200a:ff:ffff:ffff:ffff:ffff:ffff:ffff" }
|
||||||
- match:
|
- match:
|
||||||
hits.hits.4._source:
|
hits.hits.4._source:
|
||||||
ip_range: { "gte": "74.125.227.0", "lte": "74.125.227.127" }
|
ip_range: { "gte": "74.125.227.0", "lte": "74.125.227.127" }
|
||||||
|
@ -375,10 +375,10 @@ setup:
|
||||||
hits.hits.5._source: {}
|
hits.hits.5._source: {}
|
||||||
- match:
|
- match:
|
||||||
hits.hits.6._source:
|
hits.hits.6._source:
|
||||||
ip_range: { "gte": "::", "lte": "10.10.10.10" }
|
ip_range: { "gte": "0.0.0.0", "lte": "10.10.10.10" }
|
||||||
- match:
|
- match:
|
||||||
hits.hits.7._source:
|
hits.hits.7._source:
|
||||||
ip_range: { "gte": "10.10.10.10", "lte": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" }
|
ip_range: { "gte": "2001:db8::", "lte": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" }
|
||||||
|
|
||||||
---
|
---
|
||||||
"Date range":
|
"Date range":
|
||||||
|
|
|
@ -381,17 +381,37 @@ public class RangeFieldMapper extends FieldMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseCreateField(DocumentParserContext context) throws IOException {
|
protected void parseCreateField(DocumentParserContext context) throws IOException {
|
||||||
Range range;
|
|
||||||
XContentParser parser = context.parser();
|
XContentParser parser = context.parser();
|
||||||
final XContentParser.Token start = parser.currentToken();
|
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
|
||||||
if (start == XContentParser.Token.VALUE_NULL) {
|
|
||||||
return;
|
return;
|
||||||
} else if (start == XContentParser.Token.START_OBJECT) {
|
}
|
||||||
|
|
||||||
|
Range range = parseRange(parser);
|
||||||
|
context.doc().addAll(fieldType().rangeType.createFields(context, name(), range, index, hasDocValues, store));
|
||||||
|
|
||||||
|
if (hasDocValues == false && (index || store)) {
|
||||||
|
context.addToFieldNames(fieldType().name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Range parseRange(XContentParser parser) throws IOException {
|
||||||
|
final XContentParser.Token start = parser.currentToken();
|
||||||
|
if (fieldType().rangeType == RangeType.IP && start == XContentParser.Token.VALUE_STRING) {
|
||||||
|
return parseIpRangeFromCidr(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start != XContentParser.Token.START_OBJECT) {
|
||||||
|
throw new DocumentParsingException(
|
||||||
|
parser.getTokenLocation(),
|
||||||
|
"error parsing field [" + name() + "], expected an object but got " + parser.currentName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
RangeFieldType fieldType = fieldType();
|
RangeFieldType fieldType = fieldType();
|
||||||
RangeType rangeType = fieldType.rangeType;
|
RangeType rangeType = fieldType.rangeType;
|
||||||
String fieldName = null;
|
String fieldName = null;
|
||||||
Object from = rangeType.minValue();
|
Object parsedFrom = null;
|
||||||
Object to = rangeType.maxValue();
|
Object parsedTo = null;
|
||||||
boolean includeFrom = DEFAULT_INCLUDE_LOWER;
|
boolean includeFrom = DEFAULT_INCLUDE_LOWER;
|
||||||
boolean includeTo = DEFAULT_INCLUDE_UPPER;
|
boolean includeTo = DEFAULT_INCLUDE_UPPER;
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
|
@ -402,22 +422,22 @@ public class RangeFieldMapper extends FieldMapper {
|
||||||
if (fieldName.equals(GT_FIELD.getPreferredName())) {
|
if (fieldName.equals(GT_FIELD.getPreferredName())) {
|
||||||
includeFrom = false;
|
includeFrom = false;
|
||||||
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
|
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
|
||||||
from = rangeType.parseFrom(fieldType, parser, coerce.value(), includeFrom);
|
parsedFrom = rangeType.parseFrom(fieldType, parser, coerce.value(), includeFrom);
|
||||||
}
|
}
|
||||||
} else if (fieldName.equals(GTE_FIELD.getPreferredName())) {
|
} else if (fieldName.equals(GTE_FIELD.getPreferredName())) {
|
||||||
includeFrom = true;
|
includeFrom = true;
|
||||||
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
|
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
|
||||||
from = rangeType.parseFrom(fieldType, parser, coerce.value(), includeFrom);
|
parsedFrom = rangeType.parseFrom(fieldType, parser, coerce.value(), includeFrom);
|
||||||
}
|
}
|
||||||
} else if (fieldName.equals(LT_FIELD.getPreferredName())) {
|
} else if (fieldName.equals(LT_FIELD.getPreferredName())) {
|
||||||
includeTo = false;
|
includeTo = false;
|
||||||
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
|
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
|
||||||
to = rangeType.parseTo(fieldType, parser, coerce.value(), includeTo);
|
parsedTo = rangeType.parseTo(fieldType, parser, coerce.value(), includeTo);
|
||||||
}
|
}
|
||||||
} else if (fieldName.equals(LTE_FIELD.getPreferredName())) {
|
} else if (fieldName.equals(LTE_FIELD.getPreferredName())) {
|
||||||
includeTo = true;
|
includeTo = true;
|
||||||
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
|
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
|
||||||
to = rangeType.parseTo(fieldType, parser, coerce.value(), includeTo);
|
parsedTo = rangeType.parseTo(fieldType, parser, coerce.value(), includeTo);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new DocumentParsingException(
|
throw new DocumentParsingException(
|
||||||
|
@ -427,20 +447,10 @@ public class RangeFieldMapper extends FieldMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
range = new Range(rangeType, from, to, includeFrom, includeTo);
|
Object from = parsedFrom != null ? parsedFrom : rangeType.defaultFrom(parsedTo);
|
||||||
} else if (fieldType().rangeType == RangeType.IP && start == XContentParser.Token.VALUE_STRING) {
|
Object to = parsedTo != null ? parsedTo : rangeType.defaultTo(parsedFrom);
|
||||||
range = parseIpRangeFromCidr(parser);
|
|
||||||
} else {
|
|
||||||
throw new DocumentParsingException(
|
|
||||||
parser.getTokenLocation(),
|
|
||||||
"error parsing field [" + name() + "], expected an object but got " + parser.currentName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
context.doc().addAll(fieldType().rangeType.createFields(context, name(), range, index, hasDocValues, store));
|
|
||||||
|
|
||||||
if (hasDocValues == false && (index || store)) {
|
return new Range(rangeType, from, to, includeFrom, includeTo);
|
||||||
context.addToFieldNames(fieldType().name());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Range parseIpRangeFromCidr(final XContentParser parser) throws IOException {
|
private static Range parseIpRangeFromCidr(final XContentParser parser) throws IOException {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.elasticsearch.lucene.queries.BinaryDocValuesRangeQuery;
|
||||||
import org.elasticsearch.xcontent.XContentParser;
|
import org.elasticsearch.xcontent.XContentParser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.Inet4Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
|
@ -64,6 +65,26 @@ public enum RangeType {
|
||||||
return included ? address : nextDown(address);
|
return included ? address : nextDown(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object defaultFrom(Object parsedTo) {
|
||||||
|
if (parsedTo == null) {
|
||||||
|
return minValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that we keep the range inside the same address family.
|
||||||
|
// `minValue()` is always IPv6 so we need to adjust it.
|
||||||
|
return parsedTo instanceof Inet4Address ? InetAddressPoint.decode(new byte[4]) : minValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object defaultTo(Object parsedFrom) {
|
||||||
|
if (parsedFrom == null) {
|
||||||
|
return maxValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that we keep the range inside the same address family.
|
||||||
|
// `maxValue()` is always IPv6 so we need to adjust it.
|
||||||
|
return parsedFrom instanceof Inet4Address ? InetAddressPoint.decode(new byte[] { -1, -1, -1, -1 }) : maxValue();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetAddress parseValue(Object value, boolean coerce, @Nullable DateMathParser dateMathParser) {
|
public InetAddress parseValue(Object value, boolean coerce, @Nullable DateMathParser dateMathParser) {
|
||||||
if (value instanceof InetAddress) {
|
if (value instanceof InetAddress) {
|
||||||
|
@ -844,6 +865,14 @@ public enum RangeType {
|
||||||
return included ? value : (Number) nextDown(value);
|
return included ? value : (Number) nextDown(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object defaultFrom(Object parsedTo) {
|
||||||
|
return minValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object defaultTo(Object parsedFrom) {
|
||||||
|
return maxValue();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract Object minValue();
|
public abstract Object minValue();
|
||||||
|
|
||||||
public abstract Object maxValue();
|
public abstract Object maxValue();
|
||||||
|
|
|
@ -87,6 +87,37 @@ public class IpRangeFieldMapperTests extends RangeFieldMapperTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testNullBounds() throws IOException {
|
||||||
|
DocumentMapper mapper = createDocumentMapper(fieldMapping(b -> {
|
||||||
|
minimalMapping(b);
|
||||||
|
b.field("store", true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
ParsedDocument bothNull = mapper.parse(source(b -> b.startObject("field").nullField("gte").nullField("lte").endObject()));
|
||||||
|
assertThat(storedValue(bothNull), equalTo("[:: : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]"));
|
||||||
|
|
||||||
|
ParsedDocument onlyFromIPv4 = mapper.parse(
|
||||||
|
source(b -> b.startObject("field").field("gte", rangeValue()).nullField("lte").endObject())
|
||||||
|
);
|
||||||
|
assertThat(storedValue(onlyFromIPv4), equalTo("[192.168.1.7 : 255.255.255.255]"));
|
||||||
|
|
||||||
|
ParsedDocument onlyToIPv4 = mapper.parse(
|
||||||
|
source(b -> b.startObject("field").nullField("gte").field("lte", rangeValue()).endObject())
|
||||||
|
);
|
||||||
|
assertThat(storedValue(onlyToIPv4), equalTo("[0.0.0.0 : 192.168.1.7]"));
|
||||||
|
|
||||||
|
ParsedDocument onlyFromIPv6 = mapper.parse(
|
||||||
|
source(b -> b.startObject("field").field("gte", "2001:db8::").nullField("lte").endObject())
|
||||||
|
);
|
||||||
|
assertThat(storedValue(onlyFromIPv6), equalTo("[2001:db8:: : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]"));
|
||||||
|
|
||||||
|
ParsedDocument onlyToIPv6 = mapper.parse(
|
||||||
|
source(b -> b.startObject("field").nullField("gte").field("lte", "2001:db8::").endObject())
|
||||||
|
);
|
||||||
|
assertThat(storedValue(onlyToIPv6), equalTo("[:: : 2001:db8::]"));
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void testValidSyntheticSource() throws IOException {
|
public void testValidSyntheticSource() throws IOException {
|
||||||
CheckedConsumer<XContentBuilder, IOException> mapping = b -> {
|
CheckedConsumer<XContentBuilder, IOException> mapping = b -> {
|
||||||
|
|
|
@ -236,14 +236,14 @@ public abstract class RangeFieldMapperTests extends MapperTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String storedValue(ParsedDocument doc) {
|
protected static String storedValue(ParsedDocument doc) {
|
||||||
assertEquals(3, doc.rootDoc().getFields("field").size());
|
assertEquals(3, doc.rootDoc().getFields("field").size());
|
||||||
List<IndexableField> fields = doc.rootDoc().getFields("field");
|
List<IndexableField> fields = doc.rootDoc().getFields("field");
|
||||||
IndexableField storedField = fields.get(2);
|
IndexableField storedField = fields.get(2);
|
||||||
return storedField.stringValue();
|
return storedField.stringValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void testNullBounds() throws IOException {
|
public void testNullBounds() throws IOException {
|
||||||
|
|
||||||
// null, null => min, max
|
// null, null => min, max
|
||||||
assertNullBounds(b -> b.startObject("field").nullField("gte").nullField("lte").endObject(), true, true);
|
assertNullBounds(b -> b.startObject("field").nullField("gte").nullField("lte").endObject(), true, true);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue