|
19 | 19 |
|
20 | 20 | package org.elasticsearch.index.fielddata; |
21 | 21 |
|
22 | | -public interface IndexNumericFieldData extends IndexFieldData<LeafNumericFieldData> { |
23 | | - |
24 | | - enum NumericType { |
25 | | - BOOLEAN(false), |
26 | | - BYTE(false), |
27 | | - SHORT(false), |
28 | | - INT(false), |
29 | | - LONG(false), |
30 | | - DATE(false), |
31 | | - DATE_NANOSECONDS(false), |
32 | | - HALF_FLOAT(true), |
33 | | - FLOAT(true), |
34 | | - DOUBLE(true); |
| 22 | +import org.apache.lucene.index.SortedNumericDocValues; |
| 23 | +import org.apache.lucene.search.SortField; |
| 24 | +import org.apache.lucene.search.SortedNumericSelector; |
| 25 | +import org.apache.lucene.search.SortedNumericSortField; |
| 26 | +import org.elasticsearch.common.Nullable; |
| 27 | +import org.elasticsearch.common.time.DateUtils; |
| 28 | +import org.elasticsearch.common.util.BigArrays; |
| 29 | +import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; |
| 30 | +import org.elasticsearch.index.fielddata.fieldcomparator.DoubleValuesComparatorSource; |
| 31 | +import org.elasticsearch.index.fielddata.fieldcomparator.FloatValuesComparatorSource; |
| 32 | +import org.elasticsearch.index.fielddata.fieldcomparator.LongValuesComparatorSource; |
| 33 | +import org.elasticsearch.search.DocValueFormat; |
| 34 | +import org.elasticsearch.search.MultiValueMode; |
| 35 | +import org.elasticsearch.search.sort.BucketedSort; |
| 36 | +import org.elasticsearch.search.sort.SortOrder; |
| 37 | + |
| 38 | +import java.io.IOException; |
| 39 | +import java.util.function.LongUnaryOperator; |
| 40 | + |
| 41 | +/** |
| 42 | + * Base class for numeric field data. |
| 43 | + */ |
| 44 | +public abstract class IndexNumericFieldData implements IndexFieldData<LeafNumericFieldData> { |
| 45 | + /** |
| 46 | + * The type of number. |
| 47 | + */ |
| 48 | + public enum NumericType { |
| 49 | + BOOLEAN(false, SortField.Type.LONG), |
| 50 | + BYTE(false, SortField.Type.LONG), |
| 51 | + SHORT(false, SortField.Type.LONG), |
| 52 | + INT(false, SortField.Type.LONG), |
| 53 | + LONG(false, SortField.Type.LONG), |
| 54 | + DATE(false, SortField.Type.LONG), |
| 55 | + DATE_NANOSECONDS(false, SortField.Type.LONG), |
| 56 | + HALF_FLOAT(true, SortField.Type.LONG), |
| 57 | + FLOAT(true, SortField.Type.FLOAT), |
| 58 | + DOUBLE(true, SortField.Type.DOUBLE); |
35 | 59 |
|
36 | 60 | private final boolean floatingPoint; |
| 61 | + private final SortField.Type sortFieldType; |
37 | 62 |
|
38 | | - NumericType(boolean floatingPoint) { |
| 63 | + NumericType(boolean floatingPoint, SortField.Type sortFieldType) { |
39 | 64 | this.floatingPoint = floatingPoint; |
| 65 | + this.sortFieldType = sortFieldType; |
40 | 66 | } |
41 | 67 |
|
42 | 68 | public final boolean isFloatingPoint() { |
43 | 69 | return floatingPoint; |
44 | 70 | } |
| 71 | + } |
| 72 | + |
| 73 | + /** |
| 74 | + * The numeric type of this number. |
| 75 | + */ |
| 76 | + public abstract NumericType getNumericType(); |
| 77 | + |
| 78 | + /** |
| 79 | + * Returns the {@link SortField} to used for sorting. |
| 80 | + * Values are casted to the provided <code>targetNumericType</code> type if it doesn't |
| 81 | + * match the field's <code>numericType</code>. |
| 82 | + */ |
| 83 | + public final SortField sortField( |
| 84 | + NumericType targetNumericType, |
| 85 | + Object missingValue, |
| 86 | + MultiValueMode sortMode, |
| 87 | + Nested nested, |
| 88 | + boolean reverse |
| 89 | + ) { |
| 90 | + XFieldComparatorSource source = comparatorSource(targetNumericType, missingValue, sortMode, nested); |
| 91 | + |
| 92 | + /* |
| 93 | + * Use a SortField with the custom comparator logic if required because |
| 94 | + * 1. The underlying data source needs it. |
| 95 | + * 2. We need to read the value from a nested field. |
| 96 | + * 3. We Aren't using max or min to resolve the duplicates. |
| 97 | + * 4. We have to cast the results to another type. |
| 98 | + */ |
| 99 | + if (sortRequiresCustomComparator() |
| 100 | + || nested != null |
| 101 | + || (sortMode != MultiValueMode.MAX && sortMode != MultiValueMode.MIN) |
| 102 | + || targetNumericType != getNumericType()) { |
| 103 | + return new SortField(getFieldName(), source, reverse); |
| 104 | + } |
| 105 | + |
| 106 | + SortedNumericSelector.Type selectorType = sortMode == MultiValueMode.MAX ? |
| 107 | + SortedNumericSelector.Type.MAX : SortedNumericSelector.Type.MIN; |
| 108 | + SortField sortField = new SortedNumericSortField(getFieldName(), getNumericType().sortFieldType, reverse, selectorType); |
| 109 | + sortField.setMissingValue(source.missingObject(missingValue, reverse)); |
| 110 | + return sortField; |
| 111 | + } |
| 112 | + |
| 113 | + /** |
| 114 | + * Does {@link #sortField} require a custom comparator because of the way |
| 115 | + * the data is stored in doc values ({@code true}) or are the docs values |
| 116 | + * stored such that they can be sorted without decoding ({@code false}). |
| 117 | + */ |
| 118 | + protected abstract boolean sortRequiresCustomComparator(); |
45 | 119 |
|
| 120 | + @Override |
| 121 | + public final SortField sortField(Object missingValue, MultiValueMode sortMode, Nested nested, boolean reverse) { |
| 122 | + return sortField(getNumericType(), missingValue, sortMode, nested, reverse); |
46 | 123 | } |
47 | 124 |
|
48 | | - NumericType getNumericType(); |
| 125 | + /** |
| 126 | + * Builds a {@linkplain BucketedSort} for the {@code targetNumericType}, |
| 127 | + * casting the values if their native type doesn't match. |
| 128 | + */ |
| 129 | + public final BucketedSort newBucketedSort(NumericType targetNumericType, BigArrays bigArrays, @Nullable Object missingValue, |
| 130 | + MultiValueMode sortMode, Nested nested, SortOrder sortOrder, DocValueFormat format, |
| 131 | + int bucketSize, BucketedSort.ExtraData extra) { |
| 132 | + return comparatorSource(targetNumericType, missingValue, sortMode, nested) |
| 133 | + .newBucketedSort(bigArrays, sortOrder, format, bucketSize, extra); |
| 134 | + } |
| 135 | + |
| 136 | + @Override |
| 137 | + public final BucketedSort newBucketedSort(BigArrays bigArrays, @Nullable Object missingValue, MultiValueMode sortMode, Nested nested, |
| 138 | + SortOrder sortOrder, DocValueFormat format, int bucketSize, BucketedSort.ExtraData extra) { |
| 139 | + return newBucketedSort(getNumericType(), bigArrays, missingValue, sortMode, nested, sortOrder, format, bucketSize, extra); |
| 140 | + } |
| 141 | + |
| 142 | + /** |
| 143 | + * Build a {@link XFieldComparatorSource} matching the parameters. |
| 144 | + */ |
| 145 | + private XFieldComparatorSource comparatorSource( |
| 146 | + NumericType targetNumericType, |
| 147 | + @Nullable Object missingValue, |
| 148 | + MultiValueMode sortMode, |
| 149 | + Nested nested |
| 150 | + ) { |
| 151 | + switch (targetNumericType) { |
| 152 | + case HALF_FLOAT: |
| 153 | + case FLOAT: |
| 154 | + return new FloatValuesComparatorSource(this, missingValue, sortMode, nested); |
| 155 | + case DOUBLE: |
| 156 | + return new DoubleValuesComparatorSource(this, missingValue, sortMode, nested); |
| 157 | + case DATE: |
| 158 | + return dateComparatorSource(missingValue, sortMode, nested); |
| 159 | + case DATE_NANOSECONDS: |
| 160 | + return dateNanosComparatorSource(missingValue, sortMode, nested); |
| 161 | + default: |
| 162 | + assert !targetNumericType.isFloatingPoint(); |
| 163 | + return new LongValuesComparatorSource(this, missingValue, sortMode, nested); |
| 164 | + } |
| 165 | + } |
| 166 | + |
| 167 | + protected XFieldComparatorSource dateComparatorSource(@Nullable Object missingValue, MultiValueMode sortMode, Nested nested) { |
| 168 | + return new LongValuesComparatorSource(this, missingValue, sortMode, nested); |
| 169 | + } |
| 170 | + |
| 171 | + protected XFieldComparatorSource dateNanosComparatorSource(@Nullable Object missingValue, MultiValueMode sortMode, Nested nested) { |
| 172 | + return new LongValuesComparatorSource(this, missingValue, sortMode, nested, dvs -> convertNumeric(dvs, DateUtils::toNanoSeconds)); |
| 173 | + } |
| 174 | + |
| 175 | + /** |
| 176 | + * Convert the values in <code>dvs</code> using the provided <code>converter</code>. |
| 177 | + */ |
| 178 | + protected static SortedNumericDocValues convertNumeric(SortedNumericDocValues values, LongUnaryOperator converter) { |
| 179 | + return new AbstractSortedNumericDocValues() { |
| 180 | + |
| 181 | + @Override |
| 182 | + public boolean advanceExact(int target) throws IOException { |
| 183 | + return values.advanceExact(target); |
| 184 | + } |
| 185 | + |
| 186 | + @Override |
| 187 | + public long nextValue() throws IOException { |
| 188 | + return converter.applyAsLong(values.nextValue()); |
| 189 | + } |
| 190 | + |
| 191 | + @Override |
| 192 | + public int docValueCount() { |
| 193 | + return values.docValueCount(); |
| 194 | + } |
| 195 | + |
| 196 | + @Override |
| 197 | + public int nextDoc() throws IOException { |
| 198 | + return values.nextDoc(); |
| 199 | + } |
| 200 | + }; |
| 201 | + } |
49 | 202 | } |
0 commit comments