TGooch44 commented on a change in pull request #2399:
URL: https://github.com/apache/iceberg/pull/2399#discussion_r606005443



##########
File path: python/iceberg/api/expressions/predicate.py
##########
@@ -70,72 +86,152 @@ def __str__(self):
 
 class BoundPredicate(Predicate):
 
-    def __init__(self, op, ref, lit=None):
-        super(BoundPredicate, self).__init__(op, ref, lit)
+    def __init__(self, op: Operation, term: BoundTerm, lit: Literal = None, 
literals: List[Literal] = None,
+                 is_unary_predicate: bool = False, is_literal_predicate: bool 
= False,
+                 is_set_predicate: bool = False):
+        super(BoundPredicate, self).__init__(op, term)
+        ValidationException.check(sum([is_unary_predicate, 
is_literal_predicate, is_set_predicate]) == 1,
+                                  "Only a single predicate type may be set: 
%s=%s, %s=%s, %s=%s",
+                                  ("is_unary_predicate", is_unary_predicate,
+                                   "is_literal_predicate", 
is_literal_predicate,
+                                   "is_set_predicate", is_set_predicate))
+
+        self._literals: Union[None, List[Literal]] = None
+        if is_unary_predicate:
+            ValidationException.check(lit is None, "Unary Predicates may not 
have a literal", ())
+
+        elif is_literal_predicate:
+            ValidationException.check(lit is not None, "Literal Predicates 
must have a literal set", ())
+            self._literals = [lit]  # type: ignore
+
+        elif is_set_predicate:
+            ValidationException.check(literals is not None, "Set Predicates 
must have literals set", ())
+            self._literals = literals
+        else:
+            raise ValueError(f"Unable to instantiate {op} -> (lit={lit}, 
literal={literals}")
 
-    def negate(self):
-        return BoundPredicate(self.op.negate(), self.ref, self.lit)
+    @property
+    def lit(self) -> Union[None, Literal]:
+        if self._literals is None or len(self._literals) == 0:
+            return None
+        return self._literals[0]
+
+    def eval(self, struct: StructLike) -> bool:
+        raise NotImplementedError("eval not implemented for BoundPredicate")
+
+    def test(self, struct) -> bool:
+        raise NotImplementedError("TO:DO implement test")
 
 
 class UnboundPredicate(Predicate):
 
-    def __init__(self, op, named_ref, value=None, lit=None):
-        if value is None and lit is None:
-            super(UnboundPredicate, self).__init__(op, named_ref, None)
+    def __init__(self, op, term, value=None, lit=None, values=None, 
literals=None):
+        self._literals = None
+        num_set_args = sum([1 for x in [value, lit, values, literals] if x is 
not None])
+
+        if num_set_args > 1:
+            raise ValueError(f"Only one of value={value}, lit={lit}, 
values={values}, literals={literals} may be set")
+        super(UnboundPredicate, self).__init__(op, term)
         if isinstance(value, Literal):
             lit = value
             value = None
         if value is not None:
-            super(UnboundPredicate, self).__init__(op, named_ref, 
Literals.from_(value))
+            self._literals = [Literals.from_(value)]
         elif lit is not None:
-            super(UnboundPredicate, self).__init__(op, named_ref, lit)
+            self._literals = [lit]
+        elif values is not None:
+            self._literals = map(Literals.from_, values)
+        elif literals is not None:
+            self._literals = literals
+
+    @property
+    def literals(self):
+        return self._literals
+
+    @property
+    def lit(self):
+        if self.op in [Operation.IN, Operation.NOT_IN]:
+            raise ValueError(f"{self.op} predicate cannot return a literal")
+
+        return None if self.literals is None else self.literals[0]
 
     def negate(self):
-        return UnboundPredicate(self.op.negate(), self.ref, self.lit)
+        return UnboundPredicate(self.op.negate(), self.term, 
literals=self.literals)
 
-    def bind(self, struct, case_sensitive=True):  # noqa: C901
-        if case_sensitive:
-            field = struct.field(self.ref.name)
-        else:
-            field = struct.case_insensitive_field(self.ref.name.lower())
-
-        ValidationException.check(field is not None,
-                                  "Cannot find field '%s' in struct %s", 
(self.ref.name, struct))
-
-        if self.lit is None:
-            if self.op == Operation.IS_NULL:
-                if field.is_required:
-                    return FALSE
-                return BoundPredicate(Operation.IS_NULL, 
BoundReference(struct, field.field_id))
-            elif self.op == Operation.NOT_NULL:
-                if field.is_required:
-                    return TRUE
-                return BoundPredicate(Operation.NOT_NULL, 
BoundReference(struct, field.field_id))
+    def bind(self, struct, case_sensitive=True):
+        bound = self.term.bind(struct, case_sensitive=case_sensitive)
+
+        if self.literals is None:
+            return self.bind_unary_operation(bound)
+        elif self.op in [Operation.IN, Operation.NOT_IN]:
+            return self.bind_in_operation(bound)
+
+        return self.bind_literal_operation(bound)
+
+    def bind_unary_operation(self, bound_term: BoundTerm) -> BoundPredicate:
+        from .expressions import Expressions

Review comment:
       If it's at the top it introduces a circular import because of all the 
static methods in Expressions that expose shorthand's for the various predicate 
type, eg. `Expressions.equal`,  `Expressions.not_equal`, etc.  These could 
probably be moved into the predicate file itself, but leaving it in the 
expressions keeps it a bit more consistent with the Java API. We could also 
move it to the predicates file and then expose it in the expressions module 
init file.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to