From 04714e146918d91223c219284f776e7db49b5eb0 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 29 May 2020 17:00:19 -0400 Subject: [PATCH] Fix casting of scaled_float in sorts (#57207) Previously we'd get a `ClassCastException` when you tried to use `numeric_type` on `scaled_float`. Oops! This cleans up the CCE and moves some code around so the casting actually works. --- .../index/mapper/ScaledFloatFieldMapper.java | 31 ++- .../test/scaled_float/10_basic.yml | 26 ++- .../fielddata/IndexNumericFieldData.java | 183 ++++++++++++++++-- .../plain/SortedNumericIndexFieldData.java | 159 ++------------- .../search/sort/FieldSortBuilder.java | 11 +- .../functionscore/FunctionScoreTests.java | 17 +- 6 files changed, 233 insertions(+), 194 deletions(-) diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapper.java index e95b03d56d738..89efba9d72031 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapper.java @@ -29,14 +29,11 @@ import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.DocValuesFieldExistsQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.SortField; import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Explicit; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser.Token; @@ -45,7 +42,6 @@ import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fielddata.FieldData; import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.LeafNumericFieldData; @@ -53,17 +49,13 @@ import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; -import org.elasticsearch.index.fielddata.fieldcomparator.DoubleValuesComparatorSource; import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData; import org.elasticsearch.index.mapper.NumberFieldMapper.Defaults; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.MultiValueMode; import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; import org.elasticsearch.search.aggregations.support.ValuesSourceType; -import org.elasticsearch.search.sort.BucketedSort; -import org.elasticsearch.search.sort.SortOrder; import java.io.IOException; import java.math.BigDecimal; @@ -499,7 +491,7 @@ private static double objectToDouble(Object value) { return doubleValue; } - private static class ScaledFloatIndexFieldData implements IndexNumericFieldData { + private static class ScaledFloatIndexFieldData extends IndexNumericFieldData { private final IndexNumericFieldData scaledFieldData; private final double scalingFactor; @@ -525,16 +517,15 @@ public LeafNumericFieldData loadDirect(LeafReaderContext context) throws Excepti } @Override - public SortField sortField(@Nullable Object missingValue, MultiValueMode sortMode, Nested nested, boolean reverse) { - final XFieldComparatorSource source = new DoubleValuesComparatorSource(this, missingValue, sortMode, nested); - return new SortField(getFieldName(), source, reverse); - } - - @Override - public BucketedSort newBucketedSort(BigArrays bigArrays, Object missingValue, MultiValueMode sortMode, Nested nested, - SortOrder sortOrder, DocValueFormat format, int bucketSize, BucketedSort.ExtraData extra) { - return new DoubleValuesComparatorSource(this, missingValue, sortMode, nested) - .newBucketedSort(bigArrays, sortOrder, format, bucketSize, extra); + protected boolean sortRequiresCustomComparator() { + /* + * We need to use a custom comparator because the non-custom + * comparator wouldn't properly decode the long bits into the + * double. Sorting on the long representation *would* put the + * docs in order. We just don't have a way to convert the long + * into a double the right way afterwords. + */ + return true; } @Override @@ -549,7 +540,7 @@ public Index index() { @Override public NumericType getNumericType() { - /** + /* * {@link ScaledFloatLeafFieldData#getDoubleValues()} transforms the raw long values in `scaled` floats. */ return NumericType.DOUBLE; diff --git a/modules/mapper-extras/src/test/resources/rest-api-spec/test/scaled_float/10_basic.yml b/modules/mapper-extras/src/test/resources/rest-api-spec/test/scaled_float/10_basic.yml index 0298a1c00ce90..800944e3a5d68 100644 --- a/modules/mapper-extras/src/test/resources/rest-api-spec/test/scaled_float/10_basic.yml +++ b/modules/mapper-extras/src/test/resources/rest-api-spec/test/scaled_float/10_basic.yml @@ -97,8 +97,28 @@ setup: - do: search: - rest_total_hits_as_int: true - body: { "size" : 1, "sort" : { "number" : { "order" : "asc" } } } + body: + size: 1 + sort: + number: + order: asc - - match: { hits.total: 4 } + - match: { hits.total.value: 4 } + - match: { hits.hits.0._id: "3" } + - match: { hits.hits.0.sort.0: -2.1 } + +--- +"Sort with numeric_type": + + - do: + search: + body: + size: 1 + sort: + number: + order: asc + numeric_type: long + + - match: { hits.total.value: 4 } - match: { hits.hits.0._id: "3" } + - match: { hits.hits.0.sort.0: -2 } diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/IndexNumericFieldData.java b/server/src/main/java/org/elasticsearch/index/fielddata/IndexNumericFieldData.java index b881c09c94984..9f13013eff6c1 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/IndexNumericFieldData.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/IndexNumericFieldData.java @@ -19,31 +19,184 @@ package org.elasticsearch.index.fielddata; -public interface IndexNumericFieldData extends IndexFieldData { - - enum NumericType { - BOOLEAN(false), - BYTE(false), - SHORT(false), - INT(false), - LONG(false), - DATE(false), - DATE_NANOSECONDS(false), - HALF_FLOAT(true), - FLOAT(true), - DOUBLE(true); +import org.apache.lucene.index.SortedNumericDocValues; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.SortedNumericSelector; +import org.apache.lucene.search.SortedNumericSortField; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.time.DateUtils; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; +import org.elasticsearch.index.fielddata.fieldcomparator.DoubleValuesComparatorSource; +import org.elasticsearch.index.fielddata.fieldcomparator.FloatValuesComparatorSource; +import org.elasticsearch.index.fielddata.fieldcomparator.LongValuesComparatorSource; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.MultiValueMode; +import org.elasticsearch.search.sort.BucketedSort; +import org.elasticsearch.search.sort.SortOrder; + +import java.io.IOException; +import java.util.function.LongUnaryOperator; + +/** + * Base class for numeric field data. + */ +public abstract class IndexNumericFieldData implements IndexFieldData { + /** + * The type of number. + */ + public enum NumericType { + BOOLEAN(false, SortField.Type.LONG), + BYTE(false, SortField.Type.LONG), + SHORT(false, SortField.Type.LONG), + INT(false, SortField.Type.LONG), + LONG(false, SortField.Type.LONG), + DATE(false, SortField.Type.LONG), + DATE_NANOSECONDS(false, SortField.Type.LONG), + HALF_FLOAT(true, SortField.Type.LONG), + FLOAT(true, SortField.Type.FLOAT), + DOUBLE(true, SortField.Type.DOUBLE); private final boolean floatingPoint; + private final SortField.Type sortFieldType; - NumericType(boolean floatingPoint) { + NumericType(boolean floatingPoint, SortField.Type sortFieldType) { this.floatingPoint = floatingPoint; + this.sortFieldType = sortFieldType; } public final boolean isFloatingPoint() { return floatingPoint; } + } + + /** + * The numeric type of this number. + */ + public abstract NumericType getNumericType(); + + /** + * Returns the {@link SortField} to used for sorting. + * Values are casted to the provided targetNumericType type if it doesn't + * match the field's numericType. + */ + public final SortField sortField( + NumericType targetNumericType, + Object missingValue, + MultiValueMode sortMode, + Nested nested, + boolean reverse + ) { + XFieldComparatorSource source = comparatorSource(targetNumericType, missingValue, sortMode, nested); + + /* + * Use a SortField with the custom comparator logic if required because + * 1. The underlying data source needs it. + * 2. We need to read the value from a nested field. + * 3. We Aren't using max or min to resolve the duplicates. + * 4. We have to cast the results to another type. + */ + if (sortRequiresCustomComparator() + || nested != null + || (sortMode != MultiValueMode.MAX && sortMode != MultiValueMode.MIN) + || targetNumericType != getNumericType()) { + return new SortField(getFieldName(), source, reverse); + } + + SortedNumericSelector.Type selectorType = sortMode == MultiValueMode.MAX ? + SortedNumericSelector.Type.MAX : SortedNumericSelector.Type.MIN; + SortField sortField = new SortedNumericSortField(getFieldName(), getNumericType().sortFieldType, reverse, selectorType); + sortField.setMissingValue(source.missingObject(missingValue, reverse)); + return sortField; + } + + /** + * Does {@link #sortField} require a custom comparator because of the way + * the data is stored in doc values ({@code true}) or are the docs values + * stored such that they can be sorted without decoding ({@code false}). + */ + protected abstract boolean sortRequiresCustomComparator(); + @Override + public final SortField sortField(Object missingValue, MultiValueMode sortMode, Nested nested, boolean reverse) { + return sortField(getNumericType(), missingValue, sortMode, nested, reverse); } - NumericType getNumericType(); + /** + * Builds a {@linkplain BucketedSort} for the {@code targetNumericType}, + * casting the values if their native type doesn't match. + */ + public final BucketedSort newBucketedSort(NumericType targetNumericType, BigArrays bigArrays, @Nullable Object missingValue, + MultiValueMode sortMode, Nested nested, SortOrder sortOrder, DocValueFormat format, + int bucketSize, BucketedSort.ExtraData extra) { + return comparatorSource(targetNumericType, missingValue, sortMode, nested) + .newBucketedSort(bigArrays, sortOrder, format, bucketSize, extra); + } + + @Override + public final BucketedSort newBucketedSort(BigArrays bigArrays, @Nullable Object missingValue, MultiValueMode sortMode, Nested nested, + SortOrder sortOrder, DocValueFormat format, int bucketSize, BucketedSort.ExtraData extra) { + return newBucketedSort(getNumericType(), bigArrays, missingValue, sortMode, nested, sortOrder, format, bucketSize, extra); + } + + /** + * Build a {@link XFieldComparatorSource} matching the parameters. + */ + private XFieldComparatorSource comparatorSource( + NumericType targetNumericType, + @Nullable Object missingValue, + MultiValueMode sortMode, + Nested nested + ) { + switch (targetNumericType) { + case HALF_FLOAT: + case FLOAT: + return new FloatValuesComparatorSource(this, missingValue, sortMode, nested); + case DOUBLE: + return new DoubleValuesComparatorSource(this, missingValue, sortMode, nested); + case DATE: + return dateComparatorSource(missingValue, sortMode, nested); + case DATE_NANOSECONDS: + return dateNanosComparatorSource(missingValue, sortMode, nested); + default: + assert !targetNumericType.isFloatingPoint(); + return new LongValuesComparatorSource(this, missingValue, sortMode, nested); + } + } + + protected XFieldComparatorSource dateComparatorSource(@Nullable Object missingValue, MultiValueMode sortMode, Nested nested) { + return new LongValuesComparatorSource(this, missingValue, sortMode, nested); + } + + protected XFieldComparatorSource dateNanosComparatorSource(@Nullable Object missingValue, MultiValueMode sortMode, Nested nested) { + return new LongValuesComparatorSource(this, missingValue, sortMode, nested, dvs -> convertNumeric(dvs, DateUtils::toNanoSeconds)); + } + + /** + * Convert the values in dvs using the provided converter. + */ + protected static SortedNumericDocValues convertNumeric(SortedNumericDocValues values, LongUnaryOperator converter) { + return new AbstractSortedNumericDocValues() { + + @Override + public boolean advanceExact(int target) throws IOException { + return values.advanceExact(target); + } + + @Override + public long nextValue() throws IOException { + return converter.applyAsLong(values.nextValue()); + } + + @Override + public int docValueCount() { + return values.docValueCount(); + } + + @Override + public int nextDoc() throws IOException { + return values.nextDoc(); + } + }; + } } diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/plain/SortedNumericIndexFieldData.java b/server/src/main/java/org/elasticsearch/index/fielddata/plain/SortedNumericIndexFieldData.java index ed252e4411474..b7d44e1d86801 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/plain/SortedNumericIndexFieldData.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/plain/SortedNumericIndexFieldData.java @@ -26,17 +26,11 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.NumericDocValues; import org.apache.lucene.index.SortedNumericDocValues; -import org.apache.lucene.search.SortField; -import org.apache.lucene.search.SortedNumericSelector; -import org.apache.lucene.search.SortedNumericSortField; import org.apache.lucene.util.Accountable; import org.apache.lucene.util.NumericUtils; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.time.DateUtils; -import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.index.fielddata.AbstractSortedNumericDocValues; import org.elasticsearch.index.fielddata.FieldData; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; @@ -45,28 +39,22 @@ import org.elasticsearch.index.fielddata.LeafNumericFieldData; import org.elasticsearch.index.fielddata.NumericDoubleValues; import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; -import org.elasticsearch.index.fielddata.fieldcomparator.DoubleValuesComparatorSource; -import org.elasticsearch.index.fielddata.fieldcomparator.FloatValuesComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.LongValuesComparatorSource; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.indices.breaker.CircuitBreakerService; -import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.MultiValueMode; -import org.elasticsearch.search.sort.BucketedSort; -import org.elasticsearch.search.sort.SortOrder; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Objects; -import java.util.function.LongUnaryOperator; /** * FieldData backed by {@link LeafReader#getSortedNumericDocValues(String)} * @see DocValuesType#SORTED_NUMERIC */ -public class SortedNumericIndexFieldData implements IndexNumericFieldData { +public class SortedNumericIndexFieldData extends IndexNumericFieldData { public static class Builder implements IndexFieldData.Builder { private final NumericType numericType; @@ -113,95 +101,29 @@ public final Index index() { return index; } - /** - * Returns the {@link SortField} to used for sorting. - * Values are casted to the provided targetNumericType type if it doesn't - * match the field's numericType. - */ - public SortField sortField(NumericType targetNumericType, Object missingValue, MultiValueMode sortMode, - Nested nested, boolean reverse) { - final XFieldComparatorSource source = comparatorSource(targetNumericType, missingValue, sortMode, nested); - - /** - * Check if we can use a simple {@link SortedNumericSortField} compatible with index sorting and - * returns a custom sort field otherwise. - */ - if (nested != null - || (sortMode != MultiValueMode.MAX && sortMode != MultiValueMode.MIN) - || numericType == NumericType.HALF_FLOAT - || targetNumericType != numericType) { - return new SortField(fieldName, source, reverse); - } - - final SortField sortField; - final SortedNumericSelector.Type selectorType = sortMode == MultiValueMode.MAX ? - SortedNumericSelector.Type.MAX : SortedNumericSelector.Type.MIN; - switch (numericType) { - case FLOAT: - sortField = new SortedNumericSortField(fieldName, SortField.Type.FLOAT, reverse, selectorType); - break; - - case DOUBLE: - sortField = new SortedNumericSortField(fieldName, SortField.Type.DOUBLE, reverse, selectorType); - break; - - default: - assert !numericType.isFloatingPoint(); - sortField = new SortedNumericSortField(fieldName, SortField.Type.LONG, reverse, selectorType); - break; - } - sortField.setMissingValue(source.missingObject(missingValue, reverse)); - return sortField; - } - @Override - public SortField sortField(Object missingValue, MultiValueMode sortMode, Nested nested, boolean reverse) { - return sortField(numericType, missingValue, sortMode, nested, reverse); - } - - /** - * Builds a {@linkplain BucketedSort} for the {@code targetNumericType}, - * casting the values if their native type doesn't match. - */ - public BucketedSort newBucketedSort(NumericType targetNumericType, BigArrays bigArrays, @Nullable Object missingValue, - MultiValueMode sortMode, Nested nested, SortOrder sortOrder, DocValueFormat format, - int bucketSize, BucketedSort.ExtraData extra) { - return comparatorSource(targetNumericType, missingValue, sortMode, nested) - .newBucketedSort(bigArrays, sortOrder, format, bucketSize, extra); + protected boolean sortRequiresCustomComparator() { + return numericType == NumericType.HALF_FLOAT; } @Override - public BucketedSort newBucketedSort(BigArrays bigArrays, @Nullable Object missingValue, MultiValueMode sortMode, Nested nested, - SortOrder sortOrder, DocValueFormat format, int bucketSize, BucketedSort.ExtraData extra) { - return newBucketedSort(numericType, bigArrays, missingValue, sortMode, nested, sortOrder, format, bucketSize, extra); + protected XFieldComparatorSource dateComparatorSource(Object missingValue, MultiValueMode sortMode, Nested nested) { + if (numericType == NumericType.DATE_NANOSECONDS) { + // converts date values to nanosecond resolution + return new LongValuesComparatorSource(this, missingValue, + sortMode, nested, dvs -> convertNumeric(dvs, DateUtils::toMilliSeconds)); + } + return new LongValuesComparatorSource(this, missingValue, sortMode, nested); } - - private XFieldComparatorSource comparatorSource(NumericType targetNumericType, @Nullable Object missingValue, MultiValueMode sortMode, - Nested nested) { - switch (targetNumericType) { - case HALF_FLOAT: - case FLOAT: - return new FloatValuesComparatorSource(this, missingValue, sortMode, nested); - case DOUBLE: - return new DoubleValuesComparatorSource(this, missingValue, sortMode, nested); - case DATE: - if (numericType == NumericType.DATE_NANOSECONDS) { - // converts date values to nanosecond resolution - return new LongValuesComparatorSource(this, missingValue, - sortMode, nested, dvs -> convertNanosToMillis(dvs)); - } - return new LongValuesComparatorSource(this, missingValue, sortMode, nested); - case DATE_NANOSECONDS: - if (numericType == NumericType.DATE) { - // converts date_nanos values to millisecond resolution - return new LongValuesComparatorSource(this, missingValue, - sortMode, nested, dvs -> convertMillisToNanos(dvs)); - } - return new LongValuesComparatorSource(this, missingValue, sortMode, nested); - default: - assert !targetNumericType.isFloatingPoint(); - return new LongValuesComparatorSource(this, missingValue, sortMode, nested); + + @Override + protected XFieldComparatorSource dateNanosComparatorSource(Object missingValue, MultiValueMode sortMode, Nested nested) { + if (numericType == NumericType.DATE) { + // converts date_nanos values to millisecond resolution + return new LongValuesComparatorSource(this, missingValue, + sortMode, nested, dvs -> convertNumeric(dvs, DateUtils::toNanoSeconds)); } + return new LongValuesComparatorSource(this, missingValue, sortMode, nested); } @Override @@ -250,7 +172,7 @@ public final class NanoSecondFieldData extends LeafLongFieldData { @Override public SortedNumericDocValues getLongValues() { - return convertNanosToMillis(getLongValuesAsNanos()); + return convertNumeric(getLongValuesAsNanos(), DateUtils::toMilliSeconds); } public SortedNumericDocValues getLongValuesAsNanos() { @@ -520,47 +442,4 @@ public Collection getChildResources() { return Collections.emptyList(); } } - - /** - * Convert the values in dvs from nanosecond to millisecond resolution. - */ - static SortedNumericDocValues convertNanosToMillis(SortedNumericDocValues dvs) { - return convertNumeric(dvs, DateUtils::toMilliSeconds); - } - - /** - * Convert the values in dvs from millisecond to nanosecond resolution. - */ - static SortedNumericDocValues convertMillisToNanos(SortedNumericDocValues values) { - return convertNumeric(values, DateUtils::toNanoSeconds); - } - - /** - * Convert the values in dvs using the provided converter. - */ - private static SortedNumericDocValues convertNumeric(SortedNumericDocValues values, LongUnaryOperator converter) { - return new AbstractSortedNumericDocValues() { - - @Override - public boolean advanceExact(int target) throws IOException { - return values.advanceExact(target); - } - - @Override - public long nextValue() throws IOException { - return converter.applyAsLong(values.nextValue()); - } - - @Override - public int docValueCount() { - return values.docValueCount(); - } - - @Override - public int nextDoc() throws IOException { - return values.nextDoc(); - } - }; - } - } diff --git a/server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java b/server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java index 161e6064022e2..c2d4df94f8c8c 100644 --- a/server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java @@ -44,7 +44,6 @@ import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType; -import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData; import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType; import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; @@ -53,9 +52,9 @@ import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardException; -import org.elasticsearch.search.SearchSortValuesAndFormats; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.MultiValueMode; +import org.elasticsearch.search.SearchSortValuesAndFormats; import org.elasticsearch.search.builder.SearchSourceBuilder; import java.io.IOException; @@ -411,7 +410,7 @@ public SortFieldAndFormat build(QueryShardContext context) throws IOException { throw new QueryShardException(context, "[numeric_type] option cannot be set on a non-numeric field, got " + fieldType.typeName()); } - SortedNumericIndexFieldData numericFieldData = (SortedNumericIndexFieldData) fieldData; + IndexNumericFieldData numericFieldData = (IndexNumericFieldData) fieldData; NumericType resolvedType = resolveNumericType(numericType); field = numericFieldData.sortField(resolvedType, missing, localSortMode(), nested, reverse); } else { @@ -487,7 +486,11 @@ public BucketedSort buildBucketedSort(QueryShardContext context, int bucketSize, throw new QueryShardException(context, "we only support AVG, MEDIAN and SUM on number based fields"); } if (numericType != null) { - SortedNumericIndexFieldData numericFieldData = (SortedNumericIndexFieldData) fieldData; + if (fieldData instanceof IndexNumericFieldData == false) { + throw new QueryShardException(context, + "[numeric_type] option cannot be set on a non-numeric field, got " + fieldType.typeName()); + } + IndexNumericFieldData numericFieldData = (IndexNumericFieldData) fieldData; NumericType resolvedType = resolveNumericType(numericType); return numericFieldData.newBucketedSort(resolvedType, context.bigArrays(), missing, localSortMode(), nested, order, fieldType.docValueFormat(null, null), bucketSize, extra); diff --git a/server/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreTests.java b/server/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreTests.java index 60c95758bc904..b93bf308e75d1 100644 --- a/server/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreTests.java @@ -54,11 +54,11 @@ import org.elasticsearch.common.lucene.search.function.WeightFactorFunction; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.index.Index; -import org.elasticsearch.index.fielddata.LeafFieldData; -import org.elasticsearch.index.fielddata.LeafNumericFieldData; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; import org.elasticsearch.index.fielddata.IndexNumericFieldData; +import org.elasticsearch.index.fielddata.LeafFieldData; +import org.elasticsearch.index.fielddata.LeafNumericFieldData; import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; @@ -169,7 +169,7 @@ public Index index() { /** * Stub for IndexNumericFieldData needed by some score functions. Returns 1 as value always. */ - private static class IndexNumericFieldDataStub implements IndexNumericFieldData { + private static class IndexNumericFieldDataStub extends IndexNumericFieldData { @Override public NumericType getNumericType() { @@ -241,15 +241,8 @@ public LeafNumericFieldData loadDirect(LeafReaderContext context) throws Excepti } @Override - public SortField sortField(@Nullable Object missingValue, MultiValueMode sortMode, - XFieldComparatorSource.Nested nested, boolean reverse) { - throw new UnsupportedOperationException(UNSUPPORTED); - } - - @Override - public BucketedSort newBucketedSort(BigArrays bigArrays, Object missingValue, MultiValueMode sortMode, Nested nested, - SortOrder sortOrder, DocValueFormat format, int bucketSize, BucketedSort.ExtraData extra) { - throw new UnsupportedOperationException(UNSUPPORTED); + protected boolean sortRequiresCustomComparator() { + return false; } @Override