Jackie-Jiang opened a new pull request, #19684:
URL: https://github.com/apache/pinot/pull/19684

   ## Summary
   
   `BlockDocIdSet#getOptimizedDocIdSet` let a parent ask whether a child was 
empty, but only `AND` / `OR` `getTrues`, `CombinedFilterOperator` and 
`deriveNulls` asked. The not-false / false paths used by `NOT` and null 
handling checked `instanceof EmptyDocIdSet` directly and never saw an empty 
bitmap as empty. This PR normalizes emptiness when a doc id set is built 
instead, so an empty result is always an `EmptyDocIdSet` and every parent can 
recognize it by type:
   - `BitmapDocIdSet`, `RangelessBitmapDocIdSet` and `SortedDocIdSet` get 
`create` factories that return an `EmptyDocIdSet` for an empty input. The 
index-based filter operators and `BlockDocIdSet#toNonScanDocIdSet` build their 
results through them.
   - `ShortCircuitingDocIdSet` is merged into `EmptyDocIdSet`, which now keeps 
the number of entries scanned before the result was found empty. 
`EmptyDocIdSet.unscanned()` (was `getInstance()`) is the shared instance for a 
result found without scanning. It is no longer the only instance, so emptiness 
is tested with `instanceof`, not `==`.
   - `BitmapDocIdSet` takes an optional scan count, replacing the anonymous 
subclasses in `H3IndexFilterOperator`, `H3InclusionIndexFilterOperator` and the 
legacy path of `RangeIndexBasedFilterOperator`.
   - `BaseColumnFilterOperator` returns an empty match as is from 
`excludeNulls`, and returns the null bitmap directly from `getNotFalses` when 
nothing matches. The result stays recognizable as empty, and `AndDocIdSet` / 
`OrDocIdSet` keep merging their bitmap children instead of falling back to 
iterator intersection.
   - `TestFilterOperator` moves from main to test sources.
   
   ## Behavior
   
   Query results do not change: an `EmptyDocIdSet` iterates exactly like an 
empty bitmap. Two things do:
   - More short-circuits fire. `NOT` over an index-based predicate that matches 
nothing becomes a match-all instead of iterating the complement of an empty 
set, and with null handling enabled an empty column predicate no longer hides 
inside an `AndDocIdSet`, so a parent `AND` stops early.
   - `numEntriesScannedInFilter` can be lower on the `NOT` path. When a filter 
that scans eagerly (H3, legacy range index) matches nothing, `NOT` 
short-circuits to a match-all, which carries no scan count. This matches how 
`OR` already drops the count when it short-circuits on a match-all child.
   
   ## API changes
   
   For code outside Pinot that implements or builds these classes:
   - `BlockDocIdSet#getOptimizedDocIdSet` is removed.
   - `ShortCircuitingDocIdSet` is removed; use `new 
EmptyDocIdSet(numEntriesScannedInFilter)`.
   - `EmptyDocIdSet.getInstance()` is renamed to `EmptyDocIdSet.unscanned()`.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to