From 737ce42c1c0a8697009b97d72386f2c165d07add Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Mon, 4 Apr 2022 15:02:00 -0400 Subject: [PATCH] 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. --- lucene/CHANGES.txt | 4 +- ...xSortSortedNumericDocValuesRangeQuery.java | 47 ++++++++++++++----- ...xSortSortedNumericDocValuesRangeQuery.java | 29 ++++++++++-- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 814df4024a7..d5a4c14f69f 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -39,7 +39,9 @@ Optimizations Bug Fixes --------------------- -(No changes) + +* LUCENE-10466: Ensure IndexSortSortedNumericDocValuesRangeQuery handles sort field + types besides LONG (Andriy Redko) Other --------------------- diff --git a/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/IndexSortSortedNumericDocValuesRangeQuery.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/IndexSortSortedNumericDocValuesRangeQuery.java index 38d9314fefd..ccaf0766f73 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/IndexSortSortedNumericDocValuesRangeQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/IndexSortSortedNumericDocValuesRangeQuery.java @@ -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; * @@ -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 fieldComparator = - (FieldComparator) sortField.getComparator(1, false); - fieldComparator.setTopValue(topValue); + FieldComparator fieldComparator = + (FieldComparator) 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). diff --git a/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestIndexSortSortedNumericDocValuesRangeQuery.java b/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestIndexSortSortedNumericDocValuesRangeQuery.java index d52fadec5be..08f220755d0 100644 --- a/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestIndexSortSortedNumericDocValuesRangeQuery.java +++ b/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestIndexSortSortedNumericDocValuesRangeQuery.java @@ -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.