Format default values of IP ranges to match other range bound

This commit is contained in:
Oleksandr Kolomiiets 2024-04-23 15:01:51 -07:00
parent a2fdc53053
commit acb513945b
5 changed files with 129 additions and 59 deletions

View file

@ -323,7 +323,7 @@ setup:
index:
index: synthetic_source_test
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:
index:
@ -347,7 +347,7 @@ setup:
index:
index: synthetic_source_test
id: "8"
body: { "ip_range": { "gte": "10.10.10.10" } }
body: { "ip_range": { "gte": "2001:db8::" } }
- do:
indices.refresh: {}
@ -367,7 +367,7 @@ setup:
ip_range: { "gte": "192.168.0.4", "lte": "192.168.0.4" }
- match:
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:
hits.hits.4._source:
ip_range: { "gte": "74.125.227.0", "lte": "74.125.227.127" }
@ -375,10 +375,10 @@ setup:
hits.hits.5._source: {}
- match:
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:
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":

View file

@ -381,17 +381,37 @@ public class RangeFieldMapper extends FieldMapper {
@Override
protected void parseCreateField(DocumentParserContext context) throws IOException {
Range range;
XContentParser parser = context.parser();
final XContentParser.Token start = parser.currentToken();
if (start == XContentParser.Token.VALUE_NULL) {
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
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();
RangeType rangeType = fieldType.rangeType;
String fieldName = null;
Object from = rangeType.minValue();
Object to = rangeType.maxValue();
Object parsedFrom = null;
Object parsedTo = null;
boolean includeFrom = DEFAULT_INCLUDE_LOWER;
boolean includeTo = DEFAULT_INCLUDE_UPPER;
XContentParser.Token token;
@ -402,22 +422,22 @@ public class RangeFieldMapper extends FieldMapper {
if (fieldName.equals(GT_FIELD.getPreferredName())) {
includeFrom = false;
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())) {
includeFrom = true;
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())) {
includeTo = false;
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())) {
includeTo = true;
if (parser.currentToken() != XContentParser.Token.VALUE_NULL) {
to = rangeType.parseTo(fieldType, parser, coerce.value(), includeTo);
parsedTo = rangeType.parseTo(fieldType, parser, coerce.value(), includeTo);
}
} else {
throw new DocumentParsingException(
@ -427,20 +447,10 @@ public class RangeFieldMapper extends FieldMapper {
}
}
}
range = new Range(rangeType, from, to, includeFrom, includeTo);
} else if (fieldType().rangeType == RangeType.IP && start == XContentParser.Token.VALUE_STRING) {
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));
Object from = parsedFrom != null ? parsedFrom : rangeType.defaultFrom(parsedTo);
Object to = parsedTo != null ? parsedTo : rangeType.defaultTo(parsedFrom);
if (hasDocValues == false && (index || store)) {
context.addToFieldNames(fieldType().name());
}
return new Range(rangeType, from, to, includeFrom, includeTo);
}
private static Range parseIpRangeFromCidr(final XContentParser parser) throws IOException {

View file

@ -31,6 +31,7 @@ import org.elasticsearch.lucene.queries.BinaryDocValuesRangeQuery;
import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.time.Instant;
import java.time.ZoneId;
@ -64,6 +65,26 @@ public enum RangeType {
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
public InetAddress parseValue(Object value, boolean coerce, @Nullable DateMathParser dateMathParser) {
if (value instanceof InetAddress) {
@ -844,6 +865,14 @@ public enum RangeType {
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 maxValue();

View file

@ -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")
public void testValidSyntheticSource() throws IOException {
CheckedConsumer<XContentBuilder, IOException> mapping = b -> {

View file

@ -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());
List<IndexableField> fields = doc.rootDoc().getFields("field");
IndexableField storedField = fields.get(2);
return storedField.stringValue();
}
public final void testNullBounds() throws IOException {
public void testNullBounds() throws IOException {
// null, null => min, max
assertNullBounds(b -> b.startObject("field").nullField("gte").nullField("lte").endObject(), true, true);