Skip to content

Commit 942e66c

Browse files
feat: Add size op support in local engine (#1865)
1 parent 25684ff commit 942e66c

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-1
lines changed

bigframes/session/polars_executor.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from bigframes.core import array_value, bigframe_node, expression, local_data, nodes
2222
import bigframes.operations
23+
from bigframes.operations import aggregations as agg_ops
2324
from bigframes.session import executor, semi_executor
2425

2526
if TYPE_CHECKING:
@@ -32,9 +33,11 @@
3233
nodes.ReversedNode,
3334
nodes.SelectionNode,
3435
nodes.SliceNode,
36+
nodes.AggregateNode,
3537
)
3638

3739
_COMPATIBLE_SCALAR_OPS = ()
40+
_COMPATIBLE_AGG_OPS = (agg_ops.SizeOp, agg_ops.SizeUnaryOp)
3841

3942

4043
def _get_expr_ops(expr: expression.Expression) -> set[bigframes.operations.ScalarOp]:
@@ -48,7 +51,8 @@ def _is_node_polars_executable(node: nodes.BigFrameNode):
4851
return False
4952
for expr in node._node_expressions:
5053
if isinstance(expr, expression.Aggregation):
51-
return False
54+
if not type(expr.op) in _COMPATIBLE_AGG_OPS:
55+
return False
5256
if isinstance(expr, expression.Expression):
5357
if not _get_expr_ops(expr).issubset(_COMPATIBLE_SCALAR_OPS):
5458
return False
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pytest
16+
17+
from bigframes.core import array_value, expression, identifiers, nodes
18+
import bigframes.operations.aggregations as agg_ops
19+
from bigframes.session import polars_executor
20+
from bigframes.testing.engine_utils import assert_equivalence_execution
21+
22+
pytest.importorskip("polars")
23+
24+
# Polars used as reference as its fast and local. Generally though, prefer gbq engine where they disagree.
25+
REFERENCE_ENGINE = polars_executor.PolarsExecutor()
26+
27+
28+
@pytest.mark.parametrize("engine", ["polars", "bq"], indirect=True)
29+
def test_engines_aggregate_size(
30+
scalars_array_value: array_value.ArrayValue,
31+
engine,
32+
):
33+
node = nodes.AggregateNode(
34+
scalars_array_value.node,
35+
aggregations=(
36+
(
37+
expression.NullaryAggregation(agg_ops.SizeOp()),
38+
identifiers.ColumnId("size_op"),
39+
),
40+
(
41+
expression.UnaryAggregation(
42+
agg_ops.SizeUnaryOp(), expression.deref("string_col")
43+
),
44+
identifiers.ColumnId("unary_size_op"),
45+
),
46+
),
47+
)
48+
assert_equivalence_execution(node, REFERENCE_ENGINE, engine)
49+
50+
51+
@pytest.mark.parametrize("engine", ["polars", "bq"], indirect=True)
52+
@pytest.mark.parametrize(
53+
"grouping_cols",
54+
[
55+
["bool_col"],
56+
["string_col", "int64_col"],
57+
["date_col"],
58+
["datetime_col"],
59+
["timestamp_col"],
60+
["bytes_col"],
61+
],
62+
)
63+
def test_engines_grouped_aggregate(
64+
scalars_array_value: array_value.ArrayValue, engine, grouping_cols
65+
):
66+
node = nodes.AggregateNode(
67+
scalars_array_value.node,
68+
aggregations=(
69+
(
70+
expression.NullaryAggregation(agg_ops.SizeOp()),
71+
identifiers.ColumnId("size_op"),
72+
),
73+
(
74+
expression.UnaryAggregation(
75+
agg_ops.SizeUnaryOp(), expression.deref("string_col")
76+
),
77+
identifiers.ColumnId("unary_size_op"),
78+
),
79+
),
80+
by_column_ids=tuple(expression.deref(id) for id in grouping_cols),
81+
)
82+
assert_equivalence_execution(node, REFERENCE_ENGINE, engine)

0 commit comments

Comments
 (0)