Skip to content

Core: Refactor ContentStats and FieldStats#17159

Open
rdblue wants to merge 6 commits into
apache:mainfrom
rdblue:fix-content-stats
Open

Core: Refactor ContentStats and FieldStats#17159
rdblue wants to merge 6 commits into
apache:mainfrom
rdblue:fix-content-stats

Conversation

@rdblue

@rdblue rdblue commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

This updates the content stats API and implementations used for columnar stats in v4.

API changes:

  • Removes StructLike used for serialization from the interfaces
  • Adds copy methods to support copying TrackedFile with stats
  • Aligns interface conventions (use Iterable, expose full struct type as type, etc.)

StatsUtil changes:

  • Reduce complexity of ID methods, reduce number of constants
  • Remove FieldStatistic enum; not all offsets were represented as FieldStatistic and it also embedded util methods. All utils are now in StatsUtil.
  • Avoid double switch statement to resolve position to enum and enum to field in FieldStatsStruct get and set
  • Simplified field stats struct generation
  • Replaced content stats schema visitor with a loop
  • Added stats schema generation for read path based on field IDs
  • Added stats schema generation for write path based on MetricsConfig

ContentStats implementation changes:

  • Rename to ContentStatsStruct to align with other v4 types
  • Implement StructLike methods without copying data to allow object reuse, do not rebuild field stats
  • Use setCustomType instead of copying via builder in set
  • Keep field stats in a map rather than syncing between list and map
  • Remove unnecessary builder
  • Fix raw types suppressions

FieldStats implementation changes:

  • Rename to FieldStatsStruct to align with other v4 types
  • Remove SupportsIndexProjection that is bypassed
  • Implement get and set projection directly based on offset, do not rely on mapping passed to constructor
  • Use internal get/set to avoid duplicated javaClass.cast
  • Remove equals and hashCode
  • Remove unnecessary builder

Test changes:

  • Separated StatsUtil test values from production constants
  • Aligned implementation tests with other v4 object tests (test accessors, get, set, projection, serialization, and copy)
  • Updated tests that depend on stats classes

@github-actions github-actions Bot added the core label Jul 10, 2026
Types.StructType type();

/** Returns a copy of these {@link ContentStats}, deep-copying the contained field stats. */
ContentStats copy();

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.

Needed to implement copy in TrackedFile correctly. We also need to follow up with methods to project by field ID.

* <p>Note: This type may be a projection of the stats stored in manifest files.
*/
Type type();
Types.StructType type();

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 changed to the actual schema. The lower and upper bounds types do not always match (for geo types) so this is the best way to expose types. It is also more consistent with existing conventions.


/** The total NaN value count */
Long nanValueCount();
long nanValueCount();

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.

These changes are conservative. We may want to widen later, but I want to make a conscious choice.


private Object getOffset(int offset) {
return switch (offset) {
case StatsUtil.LOWER_BOUND_OFFSET -> lowerBound();

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.

Uses offsets to avoid a switch by position immediately followed by a switch by enum symbol.


private static final Types.StructType CONTENT_STATS_TYPE =
StatsUtil.contentStatsFor(TABLE_SCHEMA).type().asStructType();
StatsUtil.statsReadSchema(TABLE_SCHEMA, ImmutableList.of(1, 2));

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.

The read schema method is easiest to call for testing since it creates full stats structs for each field ID. The write schema method will omit lower and upper bounds for MetricsMode.Counts.

10L,
3L,
null);
private static final ContentStatsStruct CONTENT_STATS =

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 left this in to continue testing the MetricsUtil conversion, but I suspect that those conversions will be done lazily.

Those are the only place where fieldId is needed so I think we can avoid needing to recover field ID from base ID if we rewrite them.

assertThat(dataFile.equalityFieldIds()).isNull();
assertThat(dataFile.columnSizes()).isNull();
assertThat(dataFile.valueCounts()).containsOnly(Map.entry(1, 100L), Map.entry(2, 200L));
assertThat(dataFile.valueCounts()).containsOnly(Map.entry(1, 100L), Map.entry(2, 100L));

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.

Updated to align with what stats would actually look like.

@rdblue rdblue force-pushed the fix-content-stats branch from b35b148 to f996392 Compare July 10, 2026 21:56
Comment thread core/src/main/java/org/apache/iceberg/TrackedFileStruct.java Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant