Skip to content

Core: Implement stats filtering in V4ManifestReader - #18147

Merged
rdblue merged 1 commit into
apache:mainfrom
rdblue:add-v4-stats-filtering
Sep 16, 2026
Merged

rdblue merged 1 commit into
apache:mainfrom
rdblue:add-v4-stats-filtering

Conversation

@rdblue

@rdblue rdblue commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

This implements stats filtering in V4ManifestReader using an InclusiveStatsFilter.

This required updating CloseableIterable.filter to be able to pass a callback for updating metrics based on a skipped item. A reasonable follow up would be to rewrite the partition filtering to use this, but #18108 is changing partition handling so I thought it would be better to wait and avoid conflicts.

Test plan:

  • InclusiveStatsFilter is thoroughly tested, so it should not be exhaustively tested here
  • Ensure that the inputs to InclusiveStatsFilter.eval are passed correctly (content stats and record count)
  • Test content stats using bounds filter
  • Test record count filters files with 0 records
  • Test record count filter does not filter files with -1 records (old Avro bug)
  • Test filtering data files and manifest files
  • Test that files with null stats are not filtered
  • Test filtering with forScanPlanning that projects no stats by default
  • Test filtering with project and select that do not request stats columns
  • Test filter binding correctly applies case sensitivity setting
  • Test file skipping updates scan metrics

I also added a test for bucket partition skipping, which will eventually move to stats-based skipping. This currently passes based on partition filtering.

protected boolean shouldKeep(E item) {
boolean matches = pred.test(item);
if (!matches) {
skipCallback.accept(item);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variant of filter uses a callback that is passed the item that is skipped so that we can update the right counter depending on the item. In this case, update the manifest or data file counter based on the content type.

This iterator is identical to the one removed below, except that it calls skipCallback.accept instead of counter.increment.

@rdblue
rdblue force-pushed the add-v4-stats-filtering branch from c7a450a to 1641c82 Compare September 16, 2026 22:26

if (!includeAll) {
entries = CloseableIterable.filter(entries, entry -> entry.tracking().isLive());
files = CloseableIterable.filter(files, file -> file.tracking().isLive());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reordered this so that the cheapest filter is evaluated first. That way more expensive stats or partition filters are not run for files that are skipped because of status.

} else {
files =
CloseableIterable.filter(
this::incrementSkipCount, files, file -> file.recordCount() != 0L);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch skips files based on record count when there is no row filter.

}

private static TrackedFile unpartitionedDataWithDVFile(String location, String dvLocation) {
return new TrackedFileStruct(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored to share an implementation with other helpers.


@ParameterizedTest
@FieldSource("MANIFEST_FORMATS")
public void statsFilterCaseSensitivity(FileFormat format) throws IOException {

@stevenzwu stevenzwu Sep 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should we call this statsFilterCaseInsensitive?

should we also add a statsFilterCaseSensitive where the upper case filter doesn't skip FILE_C

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tests both case sensitive and case insensitive. First it verifies that a filter that requires case sensitivity fails and then it turns on case sensitivity and verifies the filter works.

I think the confusion is that when case doesn't match, the result is ValidationException because the filter cannot be bound to the schema, rather than just ignoring the filter.

@rdblue
rdblue merged commit b8b0805 into apache:main Sep 16, 2026
43 checks passed
@rdblue

rdblue commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Merged. Thanks, @stevenzwu!

anoopj added a commit to anoopj/iceberg that referenced this pull request Sep 17, 2026
Reconcile the partition projection with main's V4ManifestReader rework
(apache#18109 content stats, apache#18147 stats filtering, apache#18138 ManifestBitmap MDV)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants