Skip to content

add ability to customize json (and auto) column physical storage details on per column basis#17762

Merged
clintropolis merged 34 commits into
apache:masterfrom
clintropolis:json-column-format-spec
Oct 1, 2025
Merged

add ability to customize json (and auto) column physical storage details on per column basis#17762
clintropolis merged 34 commits into
apache:masterfrom
clintropolis:json-column-format-spec

Conversation

@clintropolis

@clintropolis clintropolis commented Feb 27, 2025

Copy link
Copy Markdown
Member

Description

changes:

  • NestedDataColumnSchema and AutoTypeColumnSchema now accept a NestedCommonFormatColumnFormatSpec as a new 'columnFormatSpec' property, which allows customizing how json and auto columns are compressed, how strings are encoded in dictionaries, etc, on a per column level. The nested column format spec has several properties: objectKeysEncoding, objectStorageEncoding, objectStorageCompression, stringDictionaryEncoding, dictionaryEncodedColumnCompression, longColumnEncoding, longColumnCompression, doubleColumnCompression
  • JSON field names can now be stored with front-coding with columnFormatSpec property objectKeysEncoding
  • IndexSpec has a new autoColumnFormatSpec to set default NestedCommonFormatColumnFormatSpec
  • IndexSpec fields are now all nullable and NOT populated by default. A new getEffectiveSpec() method has been added to combine an IndexSpec with the system default IndexSpec and finally the hard-coded defaults if no override values have been specified at any level
  • Adds 'druid.indexing.formats.indexSpec' to define a system default IndexSpec. This system default will be blended with the job IndexSpec at segment building time, before NestedCommonFormatColumn blends in segment level config from the computed IndexSpec to populate any values not explicitly specified with segment level

…lumn physical storage details on per column basis

changes:
* NestedDataColumnSchema and AutoTypeColumnSchema now accept a NestedCommonFormatColumn as a new 'columnFormatSpec' property, which allows customizing how json and auto columns are compressed, how strings are encoded in dictionaries, etc, on a per column level. The nested column format spec has several properties: objectKeysEncoding, objectStorageEncoding, objectStorageCompression, stringDictionaryEncoding, dictionaryEncodedColumnCompression, longColumnEncoding, longColumnCompression, doubleColumnCompression NestedCommonFormatColumn blends in segment level config from IndexSpec to populate any values not explicitly specified with segment level.
* JSON field names can now be stored with front-coding with columnFormatSpec property objectKeysEncoding
* Adds 'druid.indexing.formats.nestedColumnFormatSpec' to define a default NestedCommonFormatColumn
@github-actions github-actions Bot added Area - Batch Ingestion Area - Querying Area - Segment Format and Ser/De Area - Ingestion Area - MSQ For multi stage queries - https://github.com/apache/druid/issues/12262 labels Feb 27, 2025
@github-actions

Copy link
Copy Markdown

This pull request has been marked as stale due to 60 days of inactivity.
It will be closed in 4 weeks if no further activity occurs. If you think
that's incorrect or this pull request should instead be reviewed, please simply
write any comment. Even if closed, you can still revive the PR at any time or
discuss it on the [email protected] list.
Thank you for your contributions.

@github-actions

github-actions Bot commented Jul 6, 2025

Copy link
Copy Markdown

This pull request has been marked as stale due to 60 days of inactivity.
It will be closed in 4 weeks if no further activity occurs. If you think
that's incorrect or this pull request should instead be reviewed, please simply
write any comment. Even if closed, you can still revive the PR at any time or
discuss it on the [email protected] list.
Thank you for your contributions.

@github-actions github-actions Bot added the stale label Jul 6, 2025
@clintropolis
clintropolis marked this pull request as ready for review July 29, 2025 17:10
@capistrant
capistrant requested a review from Copilot September 5, 2025 17:31

Copilot AI left a comment

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.

Pull Request Overview

This PR adds the ability to customize JSON (and auto) column physical storage details on a per-column basis by introducing a new configuration mechanism. It allows fine-grained control over compression, encoding, and storage settings for nested data columns.

  • Introduces NestedCommonFormatColumnFormatSpec for per-column configuration of JSON/auto column storage
  • Updates NestedDataColumnSchema and AutoTypeColumnSchema to accept a new columnFormatSpec property
  • Adds global default configuration via druid.indexing.formats.nestedColumnFormatSpec

Reviewed Changes

Copilot reviewed 76 out of 76 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
sql/src/test/java/org/apache/druid/sql/calcite/util/TestDataBuilder.java Updated to use new AutoTypeColumnSchema.of() factory method
sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java Replaced explicit constructor calls with factory method for cleaner API
services/src/test/java/org/apache/druid/cli/DumpSegmentTest.java Updated DefaultColumnFormatConfig constructor calls to include new nested format spec parameter
services/src/main/java/org/apache/druid/cli/DumpSegment.java Fixed generic type declaration for better type safety
server/src/test/java/org/apache/druid/server/compaction/CompactionStatusTest.java Added test cases for auto schema compaction matching/mismatching
server/src/main/java/org/apache/druid/server/compaction/CompactionStatus.java Enhanced dimension comparison logic to handle effective schemas and format specs

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +462 to +463
final CompressedNestedDataComplexColumn<?, ?> nestedDataColumn =
(CompressedNestedDataComplexColumn<?, ?>) baseColumn;

Copilot AI Sep 5, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The cast from wildcard generic type <?> to <?, ?> is redundant and adds no type safety. Consider using a single wildcard CompressedNestedDataComplexColumn<?> or removing the explicit type parameters altogether.

Suggested change
final CompressedNestedDataComplexColumn<?, ?> nestedDataColumn =
(CompressedNestedDataComplexColumn<?, ?>) baseColumn;
final CompressedNestedDataComplexColumn<?> nestedDataColumn =
(CompressedNestedDataComplexColumn<?>) baseColumn;

Copilot uses AI. Check for mistakes.
Comment on lines +212 to 215
return CHECKS.stream()
.map(f -> f.apply(evaluator))
.filter(status -> !status.isComplete())
.findFirst().orElse(COMPLETE);

Copilot AI Sep 5, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The formatting change here appears to be a style-only modification without functional impact. Consider maintaining consistency with the existing codebase formatting patterns unless there's a specific reason for this change.

Suggested change
return CHECKS.stream()
.map(f -> f.apply(evaluator))
.filter(status -> !status.isComplete())
.findFirst().orElse(COMPLETE);
return CHECKS.stream().map(f -> f.apply(evaluator))
.filter(status -> !status.isComplete())
.findFirst().orElse(COMPLETE);

Copilot uses AI. Check for mistakes.

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.

oh no, the bots are nitpicking now? 😆 we are doomed

Comment on lines +245 to +256
return dimensionSchemas.stream()
.map(dim -> dim.getEffectiveSchema(indexSpec))
.collect(Collectors.toList());
}
return null;
}

final List<String> partitionsDimensions = ((DimensionRangePartitionsSpec) partitionsSpec).getPartitionDimensions();
return dimensionSchemas.stream()
.filter(dim -> !partitionsDimensions.contains(dim.getName()))
.collect(Collectors.toList());
.filter(dim -> !partitionsDimensions.contains(dim.getName()))
.map(dim -> dim.getEffectiveSchema(indexSpec))
.collect(Collectors.toList());

Copilot AI Sep 5, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The logic for transforming dimension schemas is duplicated below. Consider extracting this transformation into a private helper method to reduce code duplication and improve maintainability.

Suggested change
return dimensionSchemas.stream()
.map(dim -> dim.getEffectiveSchema(indexSpec))
.collect(Collectors.toList());
}
return null;
}
final List<String> partitionsDimensions = ((DimensionRangePartitionsSpec) partitionsSpec).getPartitionDimensions();
return dimensionSchemas.stream()
.filter(dim -> !partitionsDimensions.contains(dim.getName()))
.collect(Collectors.toList());
.filter(dim -> !partitionsDimensions.contains(dim.getName()))
.map(dim -> dim.getEffectiveSchema(indexSpec))
.collect(Collectors.toList());
return transformDimensionSchemas(dimensionSchemas, indexSpec);
}
return null;
}
final List<String> partitionsDimensions = ((DimensionRangePartitionsSpec) partitionsSpec).getPartitionDimensions();
return transformDimensionSchemas(
dimensionSchemas.stream()
.filter(dim -> !partitionsDimensions.contains(dim.getName()))
.collect(Collectors.toList()),
indexSpec
);
}
private static List<DimensionSchema> transformDimensionSchemas(
final List<DimensionSchema> dimensionSchemas,
final IndexSpec indexSpec
)
{
return dimensionSchemas.stream()
.map(dim -> dim.getEffectiveSchema(indexSpec))
.collect(Collectors.toList());

Copilot uses AI. Check for mistakes.
…roperly blend job IndexSpec, system default IndexSpec, and hard-coded defaults with new IndexSpec.getEffectiveSpec method
Comment thread processing/src/main/java/org/apache/druid/segment/IndexSpec.java Dismissed
Comment thread processing/src/main/java/org/apache/druid/segment/IndexSpec.java Dismissed
throw new IAE("Trying to persist an empty index!");
}

indexSpec = indexSpec.getEffectiveSpec();

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.

It would be good, at this spot or even closer to the actual usage site, to log the spec that is actually used. Ideally just once per segment, if the multiple-entry-point design of this class allows it.

@clintropolis
clintropolis merged commit db240b9 into apache:master Oct 1, 2025
102 of 104 checks passed
@clintropolis
clintropolis deleted the json-column-format-spec branch October 1, 2025 00:33
@cecemei cecemei added this to the 35.0.0 milestone Oct 21, 2025
@cecemei

cecemei commented Nov 5, 2025

Copy link
Copy Markdown
Contributor

Hey this is a new feature, I drafted some release note:

Users can now specify a format specification for each JSON column individually, which will override the IndexSpec defined in the ingestion job. Additionally, a system-wide default IndexSpec can be set using the druid.indexing.formats.indexSpec property.

WDYT?

riovic918data pushed a commit to riovic918data/druid that referenced this pull request Jun 12, 2026
…ils on per column basis (apache#17762)

changes:
* `NestedDataColumnSchema` and `AutoTypeColumnSchema` now accept a `NestedCommonFormatColumnFormatSpec` as a new 'columnFormatSpec' property, which allows customizing how json and auto columns are compressed, how strings are encoded in dictionaries, etc, on a per column level. The nested column format spec has several properties: `objectKeysEncoding`, `objectStorageEncoding`, `objectStorageCompression`, `stringDictionaryEncoding`, `dictionaryEncodedColumnCompression`, `longColumnEncoding`, `longColumnCompression`, `doubleColumnCompression`
* JSON field names can now be stored with front-coding with `columnFormatSpec` property `objectKeysEncoding`
*  `IndexSpec` has a new `autoColumnFormatSpec` to set default `NestedCommonFormatColumnFormatSpec`
* `IndexSpec` fields are now all nullable and NOT populated by default. A new `getEffectiveSpec()` method has been added to combine an `IndexSpec` with the system default `IndexSpec` and finally the hard-coded defaults if no override values have been specified at any level
* Adds 'druid.indexing.formats.indexSpec' to define a system default `IndexSpec`. This system default will be blended with the job `IndexSpec` at segment building time, before `NestedCommonFormatColumn` blends in segment level config from the computed `IndexSpec` to populate any values not explicitly specified with segment level
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area - Batch Ingestion Area - Ingestion Area - MSQ For multi stage queries - https://github.com/apache/druid/issues/12262 Area - Querying Area - Segment Format and Ser/De Area - Streaming Ingestion Kubernetes Release Notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants