diff --git a/lucene/core/src/java/org/apache/lucene/search/WANDScorer.java b/lucene/core/src/java/org/apache/lucene/search/WANDScorer.java index c9a6746b052..a633f0859a0 100644 --- a/lucene/core/src/java/org/apache/lucene/search/WANDScorer.java +++ b/lucene/core/src/java/org/apache/lucene/search/WANDScorer.java @@ -52,7 +52,7 @@ final class WANDScorer extends Scorer { */ static int scalingFactor(float f) { if (f < 0) { - throw new IllegalArgumentException(""); + throw new IllegalArgumentException("Scores must be positive or null"); } else if (f == 0) { return scalingFactor(Float.MIN_VALUE) - 1; } else if (Float.isInfinite(f)) { @@ -76,14 +76,18 @@ final class WANDScorer extends Scorer { assert Float.isNaN(maxScore) == false; assert maxScore >= 0; - if (Float.isInfinite(maxScore)) { - return (1L << 32) - 1; // means +Infinity in practice for this scorer - } - // NOTE: because doubles have more amplitude than floats for the // exponent, the scalb call produces an accurate value. double scaled = Math.scalb((double) maxScore, scalingFactor); - assert scaled <= 1 << 16 : scaled + " " + maxScore; // regular values of max_score go into 0..2^16 + + if (scaled > 1 << 16) { + // This happens if either maxScore is +Infty, or we have a scorer that + // returned +Infty as its maximum score over the whole range of doc IDs + // when computing the scaling factor in the constructor, and now returned + // a finite maximum score for a smaller range of doc IDs. + return (1L << 32) - 1; // means +Infinity in practice for this scorer + } + return (long) Math.ceil(scaled); // round up, cast is accurate since value is <= 2^16 } diff --git a/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java index 03285bd6ae0..7d1b2cb7396 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java @@ -270,7 +270,7 @@ public class TestWANDScorer extends LuceneTestCase { for (int i = 0; i < numClauses; ++i) { Query query = new TermQuery(new Term("foo", Integer.toString(start + i))); if (random().nextBoolean()) { - query = new InfiniteMaxScoreWrapperQuery(query); + query = new InfiniteMaxScoreWrapperQuery(query, numDocs / TestUtil.nextInt(random(), 1, 100)); } builder.add(query, Occur.SHOULD); } @@ -292,13 +292,26 @@ public class TestWANDScorer extends LuceneTestCase { private static class InfiniteMaxScoreWrapperScorer extends FilterScorer { - InfiniteMaxScoreWrapperScorer(Scorer scorer) { + private final int maxRange; + private int lastShallowTarget = -1; + + InfiniteMaxScoreWrapperScorer(Scorer scorer, int maxRange) { super(scorer); + this.maxRange = maxRange; + } + + @Override + public int advanceShallow(int target) throws IOException { + lastShallowTarget = target; + return in.advanceShallow(target); } @Override public float getMaxScore(int upTo) throws IOException { - return Float.POSITIVE_INFINITY; + if (upTo - Math.max(docID(), lastShallowTarget) >= maxRange) { + return Float.POSITIVE_INFINITY; + } + return in.getMaxScore(upTo); } } @@ -306,9 +319,15 @@ public class TestWANDScorer extends LuceneTestCase { private static class InfiniteMaxScoreWrapperQuery extends Query { private final Query query; + private final int maxRange; - InfiniteMaxScoreWrapperQuery(Query query) { + /** + * If asked for the maximum score over a range of doc IDs that is greater + * than or equal to maxRange, this query will return a maximum score of +Infty + */ + InfiniteMaxScoreWrapperQuery(Query query, int maxRange) { this.query = query; + this.maxRange = maxRange; } @Override @@ -330,7 +349,7 @@ public class TestWANDScorer extends LuceneTestCase { public Query rewrite(IndexReader reader) throws IOException { Query rewritten = query.rewrite(reader); if (rewritten != query) { - return new InfiniteMaxScoreWrapperQuery(rewritten); + return new InfiniteMaxScoreWrapperQuery(rewritten, maxRange); } return super.rewrite(reader); } @@ -344,7 +363,7 @@ public class TestWANDScorer extends LuceneTestCase { if (scorer == null) { return null; } else { - return new InfiniteMaxScoreWrapperScorer(scorer); + return new InfiniteMaxScoreWrapperScorer(scorer, maxRange); } } @@ -358,7 +377,7 @@ public class TestWANDScorer extends LuceneTestCase { @Override public Scorer get(long leadCost) throws IOException { - return new InfiniteMaxScoreWrapperScorer(supplier.get(leadCost)); + return new InfiniteMaxScoreWrapperScorer(supplier.get(leadCost), maxRange); } @Override