mirror of https://github.com/apache/lucene.git
LUCENE-10466: Ensure IndexSortSortedNumericDocValuesRangeQuery handles sort types besides LONG
IndexSortSortedNumericDocValuesRangeQuery unconditionally assumes the usage of the LONG-encoded SortField. Using the numeric range query (in case of sorted index) with anything but LONG ends up with class cast exception. Now the query consults the numeric type of the `SortField` and perform appropriate checks.
This commit is contained in:
parent
0a525ce2ab
commit
737ce42c1c
|
@ -39,7 +39,9 @@ Optimizations
|
|||
|
||||
Bug Fixes
|
||||
---------------------
|
||||
(No changes)
|
||||
|
||||
* LUCENE-10466: Ensure IndexSortSortedNumericDocValuesRangeQuery handles sort field
|
||||
types besides LONG (Andriy Redko)
|
||||
|
||||
Other
|
||||
---------------------
|
||||
|
|
|
@ -40,6 +40,8 @@ import org.apache.lucene.search.Scorer;
|
|||
import org.apache.lucene.search.ScorerSupplier;
|
||||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.lucene.search.SortField.Type;
|
||||
import org.apache.lucene.search.SortedNumericSortField;
|
||||
import org.apache.lucene.search.Weight;
|
||||
|
||||
/**
|
||||
|
@ -52,6 +54,7 @@ import org.apache.lucene.search.Weight;
|
|||
* <ul>
|
||||
* <li>The index is sorted, and its primary sort is on the same field as the query.
|
||||
* <li>The query field has either {@link SortedNumericDocValues} or {@link NumericDocValues}.
|
||||
* <li>The sort field is of type {@code SortField.Type.LONG} or {@code SortField.Type.INT}.
|
||||
* <li>The segments must have at most one field value per document (otherwise we cannot easily
|
||||
* determine the matching document IDs through a binary search).
|
||||
* </ul>
|
||||
|
@ -222,8 +225,12 @@ public class IndexSortSortedNumericDocValuesRangeQuery extends Query {
|
|||
&& indexSort.getSort().length > 0
|
||||
&& indexSort.getSort()[0].getField().equals(field)) {
|
||||
|
||||
SortField sortField = indexSort.getSort()[0];
|
||||
return getDocIdSetIterator(sortField, context, numericValues);
|
||||
final SortField sortField = indexSort.getSort()[0];
|
||||
final SortField.Type sortFieldType = getSortFieldType(sortField);
|
||||
// The index sort optimization is only supported for Type.INT and Type.LONG
|
||||
if (sortFieldType == Type.INT || sortFieldType == Type.LONG) {
|
||||
return getDocIdSetIterator(sortField, sortFieldType, context, numericValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -242,14 +249,17 @@ public class IndexSortSortedNumericDocValuesRangeQuery extends Query {
|
|||
* no value.
|
||||
*/
|
||||
private BoundedDocIdSetIterator getDocIdSetIterator(
|
||||
SortField sortField, LeafReaderContext context, DocIdSetIterator delegate)
|
||||
SortField sortField,
|
||||
SortField.Type sortFieldType,
|
||||
LeafReaderContext context,
|
||||
DocIdSetIterator delegate)
|
||||
throws IOException {
|
||||
long lower = sortField.getReverse() ? upperValue : lowerValue;
|
||||
long upper = sortField.getReverse() ? lowerValue : upperValue;
|
||||
int maxDoc = context.reader().maxDoc();
|
||||
|
||||
// Perform a binary search to find the first document with value >= lower.
|
||||
ValueComparator comparator = loadComparator(sortField, lower, context);
|
||||
ValueComparator comparator = loadComparator(sortField, sortFieldType, lower, context);
|
||||
int low = 0;
|
||||
int high = maxDoc - 1;
|
||||
|
||||
|
@ -257,7 +267,7 @@ public class IndexSortSortedNumericDocValuesRangeQuery extends Query {
|
|||
int mid = (low + high) >>> 1;
|
||||
if (comparator.compare(mid) <= 0) {
|
||||
high = mid - 1;
|
||||
comparator = loadComparator(sortField, lower, context);
|
||||
comparator = loadComparator(sortField, sortFieldType, lower, context);
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
|
@ -267,7 +277,7 @@ public class IndexSortSortedNumericDocValuesRangeQuery extends Query {
|
|||
// Perform a binary search to find the first document with value > upper.
|
||||
// Since we know that upper >= lower, we can initialize the lower bound
|
||||
// of the binary search to the result of the previous search.
|
||||
comparator = loadComparator(sortField, upper, context);
|
||||
comparator = loadComparator(sortField, sortFieldType, upper, context);
|
||||
low = firstDocIdInclusive;
|
||||
high = maxDoc - 1;
|
||||
|
||||
|
@ -275,7 +285,7 @@ public class IndexSortSortedNumericDocValuesRangeQuery extends Query {
|
|||
int mid = (low + high) >>> 1;
|
||||
if (comparator.compare(mid) < 0) {
|
||||
high = mid - 1;
|
||||
comparator = loadComparator(sortField, upper, context);
|
||||
comparator = loadComparator(sortField, sortFieldType, upper, context);
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
|
@ -303,11 +313,17 @@ public class IndexSortSortedNumericDocValuesRangeQuery extends Query {
|
|||
}
|
||||
|
||||
private static ValueComparator loadComparator(
|
||||
SortField sortField, long topValue, LeafReaderContext context) throws IOException {
|
||||
SortField sortField, SortField.Type type, long topValue, LeafReaderContext context)
|
||||
throws IOException {
|
||||
@SuppressWarnings("unchecked")
|
||||
FieldComparator<Long> fieldComparator =
|
||||
(FieldComparator<Long>) sortField.getComparator(1, false);
|
||||
fieldComparator.setTopValue(topValue);
|
||||
FieldComparator<Number> fieldComparator =
|
||||
(FieldComparator<Number>) sortField.getComparator(1, false);
|
||||
if (type == Type.INT) {
|
||||
fieldComparator.setTopValue((int) topValue);
|
||||
} else {
|
||||
// Since we support only Type.INT and Type.LONG, assuming LONG for all other cases
|
||||
fieldComparator.setTopValue(topValue);
|
||||
}
|
||||
|
||||
LeafFieldComparator leafFieldComparator = fieldComparator.getLeafComparator(context);
|
||||
int direction = sortField.getReverse() ? -1 : 1;
|
||||
|
@ -318,6 +334,15 @@ public class IndexSortSortedNumericDocValuesRangeQuery extends Query {
|
|||
};
|
||||
}
|
||||
|
||||
private static SortField.Type getSortFieldType(SortField sortField) {
|
||||
// We expect the sortField to be SortedNumericSortField
|
||||
if (sortField instanceof SortedNumericSortField) {
|
||||
return ((SortedNumericSortField) sortField).getNumericType();
|
||||
} else {
|
||||
return sortField.getType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A doc ID set iterator that wraps a delegate iterator and only returns doc IDs in the range
|
||||
* [firstDocInclusive, lastDoc).
|
||||
|
|
|
@ -155,15 +155,18 @@ public class TestIndexSortSortedNumericDocValuesRangeQuery extends LuceneTestCas
|
|||
}
|
||||
|
||||
public void testIndexSortDocValuesWithEvenLength() throws Exception {
|
||||
testIndexSortDocValuesWithEvenLength(false);
|
||||
testIndexSortDocValuesWithEvenLength(true);
|
||||
for (SortField.Type type : new SortField.Type[] {SortField.Type.INT, SortField.Type.LONG}) {
|
||||
testIndexSortDocValuesWithEvenLength(true, type);
|
||||
testIndexSortDocValuesWithEvenLength(false, type);
|
||||
}
|
||||
}
|
||||
|
||||
public void testIndexSortDocValuesWithEvenLength(boolean reverse) throws Exception {
|
||||
public void testIndexSortDocValuesWithEvenLength(boolean reverse, SortField.Type type)
|
||||
throws Exception {
|
||||
Directory dir = newDirectory();
|
||||
|
||||
IndexWriterConfig iwc = new IndexWriterConfig(new MockAnalyzer(random()));
|
||||
Sort indexSort = new Sort(new SortedNumericSortField("field", SortField.Type.LONG, reverse));
|
||||
Sort indexSort = new Sort(new SortedNumericSortField("field", type, reverse));
|
||||
iwc.setIndexSort(indexSort);
|
||||
RandomIndexWriter writer = new RandomIndexWriter(random(), dir, iwc);
|
||||
|
||||
|
@ -428,6 +431,24 @@ public class TestIndexSortSortedNumericDocValuesRangeQuery extends LuceneTestCas
|
|||
dir.close();
|
||||
}
|
||||
|
||||
public void testOtherSortTypes() throws Exception {
|
||||
for (SortField.Type type : new SortField.Type[] {SortField.Type.FLOAT, SortField.Type.DOUBLE}) {
|
||||
Directory dir = newDirectory();
|
||||
|
||||
IndexWriterConfig iwc = new IndexWriterConfig(new MockAnalyzer(random()));
|
||||
Sort indexSort = new Sort(new SortedNumericSortField("field", type));
|
||||
iwc.setIndexSort(indexSort);
|
||||
|
||||
RandomIndexWriter writer = new RandomIndexWriter(random(), dir, iwc);
|
||||
writer.addDocument(createDocument("field", 0));
|
||||
|
||||
testIndexSortOptimizationDeactivated(writer);
|
||||
|
||||
writer.close();
|
||||
dir.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the index sort optimization is not activated when some documents have multiple
|
||||
* values.
|
||||
|
|
Loading…
Reference in New Issue