Skip to content

[FLINK-40421][python] Add sorting APIs to DataFrame API - #29078

Closed
Milesian111 wants to merge 1 commit into
apache:masterfrom
Milesian111:pull/40421
Closed

[FLINK-40421][python] Add sorting APIs to DataFrame API#29078
Milesian111 wants to merge 1 commit into
apache:masterfrom
Milesian111:pull/40421

Conversation

@Milesian111

Copy link
Copy Markdown
Contributor

What is the purpose of the change

This pull request adds sorting support to the PyFlink DataFrame API. It introduces a lazy DataFrame.sort() transformation backed by the existing Table API order_by() operation, allowing users to sort by one or more columns or expressions without triggering execution.

Brief change log

  • Added DataFrame.sort() with support for single-column and multi-column sorting.
  • Added scalar and per-column descending options.
  • Added scalar and per-column nulls_first options.
  • Added validation to reject expressions that already contain .asc or .desc.
  • Kept sorting lazy and ensured the original DataFrame remains unchanged.
  • Added public API documentation.

Verifying this change

This change added focused tests covering:

  • Single-column and multi-column sorting.
  • Expression-based sorting.
  • Scalar and per-column sort directions.
  • Explicit NULL ordering.
  • Invalid inputs and mismatched option lengths.
  • Lazy plan construction and DataFrame immutability.
  • Batch execution with deterministic ordering and NULL handling.

The focused sorting tests and complete DataFrame batch integration tests pass locally. The full DataFrame test module has one unrelated Windows-only failure involving pre-epoch timestamp conversion in time.mktime.

Does this pull request potentially affect one of the following parts:

  • Dependencies: no
  • The public API: yes
  • The serializers: no
  • The runtime per-record code paths: no
  • Anything that affects deployment or recovery: no
  • The S3 file system connector: no

Documentation

  • Does this pull request introduce a new feature? yes
  • If yes, how is the feature documented? docs

Was generative AI tooling used to co-author this PR?
  • Yes

Generated-by: Codex GPT-5

@flinkbot

flinkbot commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

CI report:

Bot commands The @flinkbot bot supports the following commands:
  • @flinkbot run azure re-run the last Azure build

@Milesian111
Milesian111 force-pushed the pull/40421 branch 2 times, most recently from 3c5cf58 to f71e1d1 Compare September 3, 2026 09:35
@Milesian111

Copy link
Copy Markdown
Contributor Author

@flinkbot run azure

@dianfu dianfu 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.

@Milesian111 Good work! Have left a few comments.

"sort() expressions must not specify asc or desc; use descending instead"
)

descending_values = _normalize_sort_flags(descending, len(order_keys), "descending")

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.

PR #29105 has introduced a method _normalize_descending which is similar to _normalize_sort_flags. Could you rebase the PR and check if we could use that method? Besides, there is code conflict with master, should be caused by the above PR.

order_keys, descending_values, nulls_values
):
expression = table_col(key) if isinstance(key, str) else key
if is_nulls_first is not None:

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.

Adding expression.is_null as the leading sort key prevents the streaming planner from recognizing a temporal sort.

For example, df.sort(\"ts\") on a rowtime column produces TemporalSort, but df.sort(\"ts\", nulls_first=True) becomes ORDER BY ts IS NULL DESC, ts ASC and fails with Sort on a non-time-attribute field is not supported.

Please represent null placement as part of the original key, e.g. ORDER BY ts ASC NULLS FIRST

[Row(1, 20, "B"), Row(1, 10, "A"), Row(2, 5, "C")],
)

def test_sort_supports_explicit_null_ordering(self):

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.

This test calls collect() five times, launching five separate Flink jobs, while the default ascending and descending cases are already covered elsewhere. Please keep only the essential non-default combinations, for example descending=True, nulls_first=False and one expression-key case, to reduce the integration-test runtime.

@Milesian111
Milesian111 force-pushed the pull/40421 branch 2 times, most recently from 133b640 to b9af998 Compare September 10, 2026 02:56
self.assertIsNot(result, self.dataframe)
self.assertIsNot(result.to_table(), self.dataframe.to_table())
self.assertEqual(
result.to_table()._j_table.getQueryOperation().getOrder().toString(),

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.

These assertions still assume that sort() calls Table.order_by() and returns a query operation with getOrder(). After switching to sql_query(), the root operation does not expose that method, so four tests will fail with Method getOrder([]) does not exist.

We can update the tests to verify the generated SQL or observable behavior. For example, sort("id", nulls_first=True) should generate ORDER BY id ASC NULLS FIRST.

"sort() expressions must not specify asc or desc; use descending instead"
)

descending_values = _normalize_descending(descending, len(order_keys))

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.

These helpers hard-code order_by in their length-mismatch errors, but sort() exposes the parameter as by.

We can take the same way as _normalize_order_by by introducing parameter_name or use a generic term such as sort keys. The same issue applies to nulls_first.

@Milesian111 Milesian111 Sep 10, 2026

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.

Thanks for the review and applying the remaining adjustments. Sorry for the delayed reply.

I planned to address all 5 suggestions before responding, but you’ve gone ahead, closed this PR and committed the last two fixes haha.

I thought I should reply first to sync my progress with reviewer next time.

Appreciate you taking care of these again!

Add lazy DataFrame.sort backed by Table.order_by with per-key direction and explicit null ordering. Reject ordered expressions and cover validation, planning, batch behavior, and mypy-compatible typing with focused tests.

Generated-by: Codex GPT-5
@dianfu dianfu closed this in f83a73c Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants