Skip to content

Commit c658c0e

Browse files
committed
[BUGFIX] Render id/pk-only unexpected indices instead of raising (fixes #11933)
When a COMPLETE result provides unexpected_index_column_names and the unexpected index records carry only the id/pk columns (Spark/SQL paths, and when exclude_unexpected_values=True), _convert_unexpected_indices_to_df computed an empty domain_column_name_list and called unexpected_index_df.groupby([]), which raises ValueError: No group keys passed! and aborted Data Docs rendering. Aggregate all records into a single row in that case so the count/index table renders. The domain-column path is unchanged.
1 parent d8813fe commit c658c0e

2 files changed

Lines changed: 69 additions & 3 deletions

File tree

great_expectations/render/util.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,16 @@ def _convert_unexpected_indices_to_df(
445445
def _agg_func(y: pd.Series[T]) -> list[T]: # type: ignore[type-var] # not yet supported by pandas-stubs
446446
return list(y)
447447

448-
all_unexpected_indices: pd.DataFrame = unexpected_index_df.groupby(domain_column_name_list).agg(
449-
_agg_func
450-
)
448+
if domain_column_name_list:
449+
all_unexpected_indices: pd.DataFrame = unexpected_index_df.groupby(
450+
domain_column_name_list
451+
).agg(_agg_func)
452+
else:
453+
# The unexpected-index records carry only the ID/PK columns (no domain
454+
# column to group on); this happens for Spark/SQL paths and when
455+
# exclude_unexpected_values=True. Grouping on an empty list raises
456+
# "No group keys passed!", so aggregate every record into a single row.
457+
all_unexpected_indices = unexpected_index_df.groupby(by=lambda _: "").agg(_agg_func)
451458

452459
# 2. add count
453460
col_to_count: str = unexpected_index_column_names[0]

tests/render/test_util.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,34 @@ def test_convert_unexpected_indices_to_df_actual_values():
355355
assert res.iloc[2].tolist() == ["5", 1]
356356

357357

358+
def test_convert_unexpected_indices_to_df_only_id_pk_columns():
359+
"""
360+
Records carry only the ID/PK columns (no domain column to group on), which
361+
happens for Spark/SQL paths and when exclude_unexpected_values=True. This used
362+
to raise "No group keys passed!"; instead the records are aggregated into a
363+
single row. See https://github.com/great-expectations/great_expectations/issues/11933
364+
"""
365+
unexpected_index_list = [
366+
{"pk_1": 3, "pk_2": "three"},
367+
{"pk_1": 4, "pk_2": "four"},
368+
{"pk_1": 5, "pk_2": "five"},
369+
]
370+
unexpected_index_column_names = ["pk_1", "pk_2"]
371+
partial_unexpected_counts = [
372+
{"count": 1, "value": "giraffe"},
373+
{"count": 1, "value": "lion"},
374+
{"count": 1, "value": "zebra"},
375+
]
376+
377+
res = _convert_unexpected_indices_to_df(
378+
unexpected_index_list=unexpected_index_list,
379+
unexpected_index_column_names=unexpected_index_column_names,
380+
partial_unexpected_counts=partial_unexpected_counts,
381+
)
382+
assert list(res) == ["pk_1", "pk_2", "Count"]
383+
assert res.iloc[0].tolist() == ["3, 4, 5", "three, four, five", 3]
384+
385+
358386
def test_truncate_list_of_indices():
359387
int_indices: List[Union[int, str]] = [4, 5, 6, 7]
360388
result: str = truncate_list_of_indices(indices=int_indices)
@@ -525,6 +553,37 @@ def test_build_count_and_index_table_with_null():
525553
]
526554

527555

556+
def test_build_count_and_index_table_only_id_pk_columns():
557+
"""
558+
Records carry only the ID/PK columns (no domain column), so they are
559+
aggregated into a single row instead of raising "No group keys passed!".
560+
See https://github.com/great-expectations/great_expectations/issues/11933
561+
"""
562+
partial_unexpected_counts = [
563+
{"count": 1, "value": "giraffe"},
564+
{"count": 1, "value": "lion"},
565+
{"count": 1, "value": "zebra"},
566+
]
567+
unexpected_index_list = [
568+
{"pk_1": 3, "pk_2": "three"},
569+
{"pk_1": 4, "pk_2": "four"},
570+
{"pk_1": 5, "pk_2": "five"},
571+
]
572+
unexpected_count = 3
573+
unexpected_index_column_names = ["pk_1", "pk_2"]
574+
575+
header_row, table_rows = build_count_and_index_table(
576+
partial_unexpected_counts=partial_unexpected_counts,
577+
unexpected_index_list=unexpected_index_list,
578+
unexpected_count=unexpected_count,
579+
unexpected_index_column_names=unexpected_index_column_names,
580+
)
581+
assert header_row == ["Unexpected Value", "Count", "pk_1", "pk_2"]
582+
assert table_rows == [
583+
["EMPTY", 3, "3, 4, 5", "three, four, five"],
584+
]
585+
586+
528587
def test_build_count_table():
529588
partial_unexpected_counts = [
530589
{"count": 3, "value": "giraffe"},

0 commit comments

Comments
 (0)