Skip to content

Commit 1a0dddf

Browse files
authored
Range Field support for Histogram and Date Histogram aggregations(#45395)
* Add support for a Range field ValuesSource, including decode logic for range doc values and exposing RangeType as a first class enum * Provide hooks in ValuesSourceConfig for aggregations to control ValuesSource class selection on missing & script values * Branch aggregator creation in Histogram and DateHistogram based on ValuesSource class, to enable specialization based on type. This is similar to how Terms aggregator works. * Prioritize field type when available for selecting the ValuesSource class type to use for an aggregation
1 parent cf651ec commit 1a0dddf

File tree

51 files changed

+3176
-823
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+3176
-823
lines changed

modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
import org.elasticsearch.index.mapper.NumberFieldMapper;
6767
import org.elasticsearch.index.mapper.ParseContext;
6868
import org.elasticsearch.index.mapper.RangeFieldMapper;
69-
import org.elasticsearch.index.mapper.RangeFieldMapper.RangeType;
69+
import org.elasticsearch.index.mapper.RangeType;
7070
import org.elasticsearch.index.query.BoolQueryBuilder;
7171
import org.elasticsearch.index.query.BoostingQueryBuilder;
7272
import org.elasticsearch.index.query.ConstantScoreQueryBuilder;

server/src/main/java/org/apache/lucene/queries/BinaryDocValuesRangeQuery.java

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.lucene.search.Weight;
3333
import org.apache.lucene.store.ByteArrayDataInput;
3434
import org.apache.lucene.util.BytesRef;
35+
import org.elasticsearch.index.mapper.RangeType;
3536

3637
import java.io.IOException;
3738
import java.util.Objects;
@@ -40,13 +41,13 @@ public final class BinaryDocValuesRangeQuery extends Query {
4041

4142
private final String fieldName;
4243
private final QueryType queryType;
43-
private final LengthType lengthType;
44+
private final RangeType.LengthType lengthType;
4445
private final BytesRef from;
4546
private final BytesRef to;
4647
private final Object originalFrom;
4748
private final Object originalTo;
4849

49-
public BinaryDocValuesRangeQuery(String fieldName, QueryType queryType, LengthType lengthType,
50+
public BinaryDocValuesRangeQuery(String fieldName, QueryType queryType, RangeType.LengthType lengthType,
5051
BytesRef from, BytesRef to,
5152
Object originalFrom, Object originalTo) {
5253
this.fieldName = fieldName;
@@ -178,42 +179,4 @@ boolean matches(BytesRef from, BytesRef to, BytesRef otherFrom, BytesRef otherTo
178179

179180
}
180181

181-
public enum LengthType {
182-
FIXED_4 {
183-
@Override
184-
int readLength(byte[] bytes, int offset) {
185-
return 4;
186-
}
187-
},
188-
FIXED_8 {
189-
@Override
190-
int readLength(byte[] bytes, int offset) {
191-
return 8;
192-
}
193-
},
194-
FIXED_16 {
195-
@Override
196-
int readLength(byte[] bytes, int offset) {
197-
return 16;
198-
}
199-
},
200-
VARIABLE {
201-
@Override
202-
int readLength(byte[] bytes, int offset) {
203-
// the first bit encodes the sign and the next 4 bits encode the number
204-
// of additional bytes
205-
int token = Byte.toUnsignedInt(bytes[offset]);
206-
int length = (token >>> 3) & 0x0f;
207-
if ((token & 0x80) == 0) {
208-
length = 0x0f - length;
209-
}
210-
return 1 + length;
211-
}
212-
};
213-
214-
/**
215-
* Return the length of the value that starts at {@code offset} in {@code bytes}.
216-
*/
217-
abstract int readLength(byte[] bytes, int offset);
218-
}
219182
}

server/src/main/java/org/elasticsearch/index/fielddata/plain/DocValuesIndexFieldData.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.elasticsearch.index.mapper.IdFieldMapper;
3131
import org.elasticsearch.index.mapper.MappedFieldType;
3232
import org.elasticsearch.index.mapper.MapperService;
33+
import org.elasticsearch.index.mapper.RangeType;
3334
import org.elasticsearch.indices.breaker.CircuitBreakerService;
3435

3536
import java.util.Set;
@@ -71,6 +72,7 @@ public static class Builder implements IndexFieldData.Builder {
7172

7273
private NumericType numericType;
7374
private Function<SortedSetDocValues, ScriptDocValues<?>> scriptFunction = AbstractAtomicOrdinalsFieldData.DEFAULT_SCRIPT_FUNCTION;
75+
private RangeType rangeType;
7476

7577
public Builder numericType(NumericType type) {
7678
this.numericType = type;
@@ -82,12 +84,17 @@ public Builder scriptFunction(Function<SortedSetDocValues, ScriptDocValues<?>> s
8284
return this;
8385
}
8486

87+
public Builder setRangeType(RangeType rangeType) {
88+
this.rangeType = rangeType;
89+
return this;
90+
}
91+
8592
@Override
8693
public IndexFieldData<?> build(IndexSettings indexSettings, MappedFieldType fieldType, IndexFieldDataCache cache,
8794
CircuitBreakerService breakerService, MapperService mapperService) {
8895
// Ignore Circuit Breaker
8996
final String fieldName = fieldType.name();
90-
if (BINARY_INDEX_FIELD_NAMES.contains(fieldName)) {
97+
if (BINARY_INDEX_FIELD_NAMES.contains(fieldName) || rangeType != null) {
9198
assert numericType == null;
9299
return new BinaryDVIndexFieldData(indexSettings.getIndex(), fieldName);
93100
} else if (numericType != null) {

server/src/main/java/org/elasticsearch/index/mapper/BinaryRangeUtil.java

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@
1919

2020
package org.elasticsearch.index.mapper;
2121

22+
import org.apache.lucene.document.InetAddressPoint;
23+
import org.apache.lucene.store.ByteArrayDataInput;
2224
import org.apache.lucene.store.ByteArrayDataOutput;
2325
import org.apache.lucene.util.BytesRef;
2426
import org.apache.lucene.util.NumericUtils;
27+
import org.elasticsearch.common.TriFunction;
2528

2629
import java.io.IOException;
30+
import java.net.InetAddress;
2731
import java.util.ArrayList;
32+
import java.util.Arrays;
2833
import java.util.Comparator;
2934
import java.util.List;
3035
import java.util.Set;
@@ -33,6 +38,32 @@ enum BinaryRangeUtil {
3338

3439
;
3540

41+
static BytesRef encodeIPRanges(Set<RangeFieldMapper.Range> ranges) throws IOException {
42+
final byte[] encoded = new byte[5 + (16 * 2) * ranges.size()];
43+
ByteArrayDataOutput out = new ByteArrayDataOutput(encoded);
44+
out.writeVInt(ranges.size());
45+
for (RangeFieldMapper.Range range : ranges) {
46+
InetAddress fromValue = (InetAddress) range.from;
47+
byte[] encodedFromValue = InetAddressPoint.encode(fromValue);
48+
out.writeBytes(encodedFromValue, 0, encodedFromValue.length);
49+
50+
InetAddress toValue = (InetAddress) range.to;
51+
byte[] encodedToValue = InetAddressPoint.encode(toValue);
52+
out.writeBytes(encodedToValue, 0, encodedToValue.length);
53+
}
54+
return new BytesRef(encoded, 0, out.getPosition());
55+
}
56+
57+
static List<RangeFieldMapper.Range> decodeIPRanges(BytesRef encodedRanges) {
58+
return decodeRanges(encodedRanges, RangeType.IP, BinaryRangeUtil::decodeIP);
59+
}
60+
61+
private static InetAddress decodeIP(byte[] bytes, int offset, int length) {
62+
// offset + length because copyOfRange wants a from and a to, not an offset & length
63+
byte[] slice = Arrays.copyOfRange(bytes, offset, offset + length);
64+
return InetAddressPoint.decode(slice);
65+
}
66+
3667
static BytesRef encodeLongRanges(Set<RangeFieldMapper.Range> ranges) throws IOException {
3768
List<RangeFieldMapper.Range> sortedRanges = new ArrayList<>(ranges);
3869
Comparator<RangeFieldMapper.Range> fromComparator = Comparator.comparingLong(range -> ((Number) range.from).longValue());
@@ -51,6 +82,11 @@ static BytesRef encodeLongRanges(Set<RangeFieldMapper.Range> ranges) throws IOEx
5182
return new BytesRef(encoded, 0, out.getPosition());
5283
}
5384

85+
static List<RangeFieldMapper.Range> decodeLongRanges(BytesRef encodedRanges) {
86+
return decodeRanges(encodedRanges, RangeType.LONG,
87+
BinaryRangeUtil::decodeLong);
88+
}
89+
5490
static BytesRef encodeDoubleRanges(Set<RangeFieldMapper.Range> ranges) throws IOException {
5591
List<RangeFieldMapper.Range> sortedRanges = new ArrayList<>(ranges);
5692
Comparator<RangeFieldMapper.Range> fromComparator = Comparator.comparingDouble(range -> ((Number) range.from).doubleValue());
@@ -69,6 +105,43 @@ static BytesRef encodeDoubleRanges(Set<RangeFieldMapper.Range> ranges) throws IO
69105
return new BytesRef(encoded, 0, out.getPosition());
70106
}
71107

108+
static List<RangeFieldMapper.Range> decodeDoubleRanges(BytesRef encodedRanges) {
109+
return decodeRanges(encodedRanges, RangeType.DOUBLE,
110+
BinaryRangeUtil::decodeDouble);
111+
}
112+
113+
static List<RangeFieldMapper.Range> decodeFloatRanges(BytesRef encodedRanges) {
114+
return decodeRanges(encodedRanges, RangeType.FLOAT,
115+
BinaryRangeUtil::decodeFloat);
116+
}
117+
118+
static List<RangeFieldMapper.Range> decodeRanges(BytesRef encodedRanges, RangeType rangeType,
119+
TriFunction<byte[], Integer, Integer, Object> decodeBytes) {
120+
121+
RangeType.LengthType lengthType = rangeType.lengthType;
122+
ByteArrayDataInput in = new ByteArrayDataInput();
123+
in.reset(encodedRanges.bytes, encodedRanges.offset, encodedRanges.length);
124+
int numRanges = in.readVInt();
125+
126+
List<RangeFieldMapper.Range> ranges = new ArrayList<>(numRanges);
127+
128+
final byte[] bytes = encodedRanges.bytes;
129+
int offset = in.getPosition();
130+
for (int i = 0; i < numRanges; i++) {
131+
int length = lengthType.readLength(bytes, offset);
132+
Object from = decodeBytes.apply(bytes, offset, length);
133+
offset += length;
134+
135+
length = lengthType.readLength(bytes, offset);
136+
Object to = decodeBytes.apply(bytes, offset, length);
137+
offset += length;
138+
// TODO: Support for exclusive ranges, pending resolution of #40601
139+
RangeFieldMapper.Range decodedRange = new RangeFieldMapper.Range(rangeType, from, to, true, true);
140+
ranges.add(decodedRange);
141+
}
142+
return ranges;
143+
}
144+
72145
static BytesRef encodeFloatRanges(Set<RangeFieldMapper.Range> ranges) throws IOException {
73146
List<RangeFieldMapper.Range> sortedRanges = new ArrayList<>(ranges);
74147
Comparator<RangeFieldMapper.Range> fromComparator = Comparator.comparingDouble(range -> ((Number) range.from).floatValue());
@@ -93,12 +166,20 @@ static byte[] encodeDouble(double number) {
93166
return encoded;
94167
}
95168

169+
static double decodeDouble(byte[] bytes, int offset, int length){
170+
return NumericUtils.sortableLongToDouble(NumericUtils.sortableBytesToLong(bytes, offset));
171+
}
172+
96173
static byte[] encodeFloat(float number) {
97174
byte[] encoded = new byte[4];
98175
NumericUtils.intToSortableBytes(NumericUtils.floatToSortableInt(number), encoded, 0);
99176
return encoded;
100177
}
101178

179+
static float decodeFloat(byte[] bytes, int offset, int length) {
180+
return NumericUtils.sortableIntToFloat(NumericUtils.sortableBytesToInt(bytes, offset));
181+
}
182+
102183
/**
103184
* Encodes the specified number of type long in a variable-length byte format.
104185
* The byte format preserves ordering, which means the returned byte array can be used for comparing as is.
@@ -114,6 +195,23 @@ static byte[] encodeLong(long number) {
114195
return encode(number, sign);
115196
}
116197

198+
static long decodeLong(byte[] bytes, int offset, int length) {
199+
boolean isNegative = (bytes[offset] & 128) == 0;
200+
// Start by masking off the last three bits of the first byte - that's the start of our number
201+
long decoded;
202+
if (isNegative) {
203+
decoded = -8 | bytes[offset];
204+
} else {
205+
decoded = bytes[offset] & 7;
206+
}
207+
for (int i = 1; i < length; i++) {
208+
decoded <<= 8;
209+
decoded += Byte.toUnsignedInt(bytes[offset + i]);
210+
}
211+
212+
return decoded;
213+
}
214+
117215
private static byte[] encode(long l, int sign) {
118216
assert l >= 0;
119217

@@ -158,4 +256,5 @@ private static byte[] encode(long l, int sign) {
158256
}
159257
return encoded;
160258
}
259+
161260
}

server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ protected DateMathParser dateMathParser() {
328328
return dateMathParser;
329329
}
330330

331-
long parse(String value) {
331+
public long parse(String value) {
332332
return resolution.convert(DateFormatters.from(dateTimeFormatter().parse(value)).toInstant());
333333
}
334334

0 commit comments

Comments
 (0)