2222import java .util .function .Function ;
2323import org .apache .lucene .index .PointValues ;
2424import org .apache .lucene .internal .hppc .LongIntHashMap ;
25+ import org .apache .lucene .search .CollectionTerminatedException ;
2526import org .apache .lucene .search .DocIdSetIterator ;
27+ import org .apache .lucene .search .PointRangeQuery ;
2628import org .apache .lucene .util .NumericUtils ;
2729
2830/**
@@ -76,15 +78,23 @@ static boolean canCollectEfficiently(final PointValues pointValues, final long b
7678
7779 static void collect (
7880 final PointValues pointValues ,
81+ final PointRangeQuery prq ,
7982 final long bucketWidth ,
8083 final LongIntHashMap collectorCounts ,
8184 final int maxBuckets )
8285 throws IOException {
8386 final Function <byte [], Long > byteToLong = bytesToLong (pointValues .getBytesPerDimension ());
87+ long leafMin = byteToLong .apply (pointValues .getMinPackedValue ());
88+ long leafMax = byteToLong .apply (pointValues .getMaxPackedValue ());
89+ if (prq != null ) {
90+ leafMin = Math .max (leafMin , byteToLong .apply (prq .getLowerPoint ()));
91+ leafMax = Math .min (leafMax , byteToLong .apply (prq .getUpperPoint ()));
92+ }
8493 BucketManager collector =
8594 new BucketManager (
8695 collectorCounts ,
87- byteToLong .apply (pointValues .getMinPackedValue ()),
96+ leafMin ,
97+ leafMax + 1 , // the max value is exclusive for collector
8898 bucketWidth ,
8999 byteToLong ,
90100 maxBuckets );
@@ -135,6 +145,11 @@ public void visit(int docID) {
135145 public void visit (int docID , byte [] packedValue ) throws IOException {
136146 if (!collector .withinUpperBound (packedValue )) {
137147 collector .finalizePreviousBucket (packedValue );
148+ // If the packedValue is not within upper bound even after updating upper bound,
149+ // we have exhausted the max value and should throw early termination error
150+ if (!collector .withinUpperBound (packedValue )) {
151+ throw new CollectionTerminatedException ();
152+ }
138153 }
139154
140155 if (collector .withinRange (packedValue )) {
@@ -146,6 +161,11 @@ public void visit(int docID, byte[] packedValue) throws IOException {
146161 public void visit (DocIdSetIterator iterator , byte [] packedValue ) throws IOException {
147162 if (!collector .withinUpperBound (packedValue )) {
148163 collector .finalizePreviousBucket (packedValue );
164+ // If the packedValue is not within upper bound even after updating upper bound,
165+ // we have exhausted the max value and should throw early termination error
166+ if (!collector .withinUpperBound (packedValue )) {
167+ throw new CollectionTerminatedException ();
168+ }
149169 }
150170
151171 if (collector .withinRange (packedValue )) {
@@ -157,9 +177,14 @@ public void visit(DocIdSetIterator iterator, byte[] packedValue) throws IOExcept
157177
158178 @ Override
159179 public PointValues .Relation compare (byte [] minPackedValue , byte [] maxPackedValue ) {
160- // try to find the first range that may collect values from this cell
180+ // Try to find the first range that may collect values from this cell
161181 if (!collector .withinUpperBound (minPackedValue )) {
162182 collector .finalizePreviousBucket (minPackedValue );
183+ // If the minPackedValue is not within upper bound even after updating upper bound,
184+ // we have exhausted the max value and should throw early termination error
185+ if (!collector .withinUpperBound (minPackedValue )) {
186+ throw new CollectionTerminatedException ();
187+ }
163188 }
164189
165190 // Not possible to have the CELL_OUTSIDE_QUERY, as bucket lower bound is updated
@@ -176,6 +201,7 @@ private static class BucketManager {
176201 private final LongIntHashMap collectorCounts ;
177202 private int counter = 0 ;
178203 private long startValue ;
204+ private long maxValue ;
179205 private long endValue ;
180206 private int nonZeroBuckets = 0 ;
181207 private int maxBuckets ;
@@ -185,13 +211,16 @@ private static class BucketManager {
185211 public BucketManager (
186212 LongIntHashMap collectorCounts ,
187213 long minValue ,
214+ long maxValue ,
188215 long bucketWidth ,
189216 Function <byte [], Long > byteToLong ,
190217 int maxBuckets ) {
191218 this .collectorCounts = collectorCounts ;
192219 this .bucketWidth = bucketWidth ;
193- this .startValue = Math .floorDiv (minValue , bucketWidth ) * bucketWidth ;
194- this .endValue = startValue + bucketWidth ;
220+ this .startValue = minValue ;
221+ this .endValue =
222+ Math .min ((Math .floorDiv (startValue , bucketWidth ) + 1 ) * bucketWidth , maxValue );
223+ this .maxValue = maxValue ;
195224 this .byteToLong = byteToLong ;
196225 this .maxBuckets = maxBuckets ;
197226 }
@@ -205,19 +234,19 @@ private void countNode(int count) {
205234 }
206235
207236 private void finalizePreviousBucket (byte [] packedValue ) {
208- // TODO: Can counter ever be 0?
237+ // counter can be 0 for first bucket in case
238+ // of Point Range Query
209239 if (counter > 0 ) {
210240 collectorCounts .addTo (Math .floorDiv (startValue , bucketWidth ), counter );
211- if (packedValue != null ) {
212- startValue = byteToLong .apply (packedValue );
213- // Align the start value with bucket width
214- startValue = Math .floorDiv (startValue , bucketWidth ) * bucketWidth ;
215- endValue = startValue + bucketWidth ;
216- }
217241 nonZeroBuckets ++;
218242 counter = 0 ;
219243 HistogramCollector .checkMaxBuckets (nonZeroBuckets , maxBuckets );
220244 }
245+
246+ if (packedValue != null ) {
247+ startValue = byteToLong .apply (packedValue );
248+ endValue = Math .min ((Math .floorDiv (startValue , bucketWidth ) + 1 ) * bucketWidth , maxValue );
249+ }
221250 }
222251
223252 private boolean withinLowerBound (byte [] value ) {
0 commit comments