Skip to content

Commit d1826f1

Browse files
geruhAniketsy
andauthored
feat: Make and expression JSON serializable (#2784)
Related to: #2518, #2775 # Rationale for this change This work was done by @Aniketsy, I just opened this to get the tests passing, and we can merge for scan planning. But, this PR allows `And` expressions to be deserialized from JSON through Pydantic. This PR aligns the `And` expression with the `Or`/`Not` pattern by adding `IcebergBaseModel` as an inherited class. This gets teh And expression into a proven serializable state, preparing it for the full expression tree [de]serializability work in #2783. ## Are these changes tested? Yes added a test and ensure that they align with EpressionParser in Iceberg Java ## Are there any user-facing changes? No this is just serialization cc: @kevinjqliu @Fokko --------- Co-authored-by: Aniket Singh Yadav <[email protected]>
1 parent 9610b41 commit d1826f1

File tree

2 files changed

+17
-3
lines changed

2 files changed

+17
-3
lines changed

pyiceberg/expressions/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,19 @@ def as_bound(self) -> type[BoundReference]:
237237
return BoundReference
238238

239239

240-
class And(BooleanExpression):
240+
class And(IcebergBaseModel, BooleanExpression):
241241
"""AND operation expression - logical conjunction."""
242242

243+
model_config = ConfigDict(arbitrary_types_allowed=True)
244+
245+
type: TypingLiteral["and"] = Field(default="and", alias="type")
243246
left: BooleanExpression
244247
right: BooleanExpression
245248

249+
def __init__(self, left: BooleanExpression, right: BooleanExpression, *rest: BooleanExpression) -> None:
250+
if isinstance(self, And) and not hasattr(self, "left") and not hasattr(self, "right"):
251+
super().__init__(left=left, right=right)
252+
246253
def __new__(cls, left: BooleanExpression, right: BooleanExpression, *rest: BooleanExpression) -> BooleanExpression: # type: ignore
247254
if rest:
248255
return _build_balanced_tree(And, (left, right, *rest))
@@ -254,8 +261,6 @@ def __new__(cls, left: BooleanExpression, right: BooleanExpression, *rest: Boole
254261
return left
255262
else:
256263
obj = super().__new__(cls)
257-
obj.left = left
258-
obj.right = right
259264
return obj
260265

261266
def __eq__(self, other: Any) -> bool:

tests/expressions/test_expressions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,15 @@ def test_and() -> None:
725725
null & "abc"
726726

727727

728+
def test_and_serialization() -> None:
729+
expr = And(EqualTo("x", 1), GreaterThan("y", 2))
730+
731+
assert (
732+
expr.model_dump_json()
733+
== '{"type":"and","left":{"term":"x","type":"eq","value":1},"right":{"term":"y","type":"gt","value":2}}'
734+
)
735+
736+
728737
def test_or() -> None:
729738
null = IsNull(Reference("a"))
730739
nan = IsNaN(Reference("b"))

0 commit comments

Comments
 (0)