Example #1
0
    def compare_expr(self, expr: Expression):
        assert expr.operator in COMPARISONS
        (left, right) = expr.args
        left_path = dot_path(left)
        right_path = dot_path(right)

        # Normalize partial to LHS.
        if right_path:
            expr = reflect_expr(expr)
            left, right = right, left
            left_path, right_path = right_path, left_path

        left_field = "__".join(left_path[1:]) if left_path[1:] else "pk"

        if left_path and right_path:
            raise UnsupportedError(f"Unsupported partial expression: {expr}")
            # compare partials
            # self.filter &= COMPARISONS[expr.operator](
            #     left_field, self.translate_path_to_field(right_path)
            # )
        else:
            # partial cmp grounded
            assert left_path
            if isinstance(right, Model):
                right = right.pk
            self.filter &= COMPARISONS[expr.operator](left_field, right)
Example #2
0
def translate_in(expression, session, model, get_model):
    assert expression.operator == "In"
    left = expression.args[0]
    right = expression.args[1]

    # IN means at least something must be contained in the property.

    # There are two possible types of in operations. In both, the right hand side
    # should be a dot op.

    # Partial In: LHS is an expression
    if isinstance(left, Expression):
        path = dot_path(right)
        assert path

        return translate_dot(
            path, session, model, functools.partial(emit_subexpression, left, get_model)
        )
    else:
        # Contains: LHS is not an expression.
        # TODO (dhatch) Missing check, left type must match type of the target?
        path = dot_path(right)
        assert path
        path, field_name = path[:-1], path[-1]
        return translate_dot(
            path, session, model, functools.partial(emit_contains, field_name, left)
        )
Example #3
0
def test_dot_path():
    single = Expression("Dot", [Variable("_this"), "created_by"])
    assert dot_path(single) == ("created_by", )

    double = Expression("Dot", [single, "username"])
    assert dot_path(double) == ("created_by", "username")

    triple = Expression("Dot", [double, "first"])
    assert dot_path(triple) == ("created_by", "username", "first")
Example #4
0
def translate_isa(expression: Expression, session: Session, model, get_model):
    assert expression.operator == "Isa"
    left, right = expression.args
    if dot_path(left) == ():
        assert left == Variable("_this")
    else:
        for field_name in dot_path(left):
            _, model, __ = get_relationship(model, field_name)

    assert not right.fields, "Unexpected fields in isa expression"
    constraint_type = get_model(right.tag)
    return sql.true() if issubclass(model, constraint_type) else sql.false()
Example #5
0
def translate_compare(expression: Expression, session: Session, model, get_model):
    (left, right) = expression.args
    left_path = dot_path(left)
    if left_path:
        path, field_name = left_path[:-1], left_path[-1]
        return translate_dot(
            path,
            session,
            model,
            functools.partial(emit_compare, field_name, right, expression.operator),
        )
    else:
        assert left == Variable("_this")
        if not isinstance(right, model):
            return sql.false()

        if expression.operator not in ("Eq", "Unify"):
            raise UnsupportedError(
                f"Unsupported comparison: {expression}. Models can only be compared"
                " with `=` or `==`"
            )

        primary_keys = [pk.name for pk in inspect(model).primary_key]
        pk_filter = sql.true()
        for key in primary_keys:
            pk_filter &= getattr(model, key) == getattr(right, key)
        return pk_filter
Example #6
0
    def in_expr(self, expr: Expression):
        assert expr.operator == "In"
        (left, right) = expr.args
        right_path = dot_path(right)

        if left == "_this" and right_path:
            if right_path[1:]:
                # _this in _this.foo.bar
                # _this in _some_var.foo.bar
                path = self.translate_path_to_field(right_path)
                # path = "__".join(right_path[1:])
                self.filter &= COMPARISONS["Unify"]("pk", path)
            else:
                # _this in _this
                # _this in _some_var
                raise UnsupportedError(
                    f"Unsupported partial expression: {expr}")
        elif isinstance(left, Variable) and right_path:
            if right_path[1:]:
                # var in _this.foo.bar
                # var in other_var.foo.bar

                # get the base query for the RHS of the `in`
                root = self
                while root.parent:
                    root = root.parent
                base_query = root.get_query_from_var(right_path[0]) or root

                # Left is a variable => apply constraints to the subquery.
                if left not in base_query.variables:
                    subquery_path = right_path[1:]
                    model = get_model_by_path(base_query.model, subquery_path)
                    base_query.variables[left] = FilterBuilder(
                        model, parent=base_query, name=left)
                else:
                    # This means we have two paths for the same variable
                    # the subquery will handle the intersection
                    pass

                # Get the model for the subfield

                subquery = base_query.variables[left]
                # <var> in <partial>
                # => set up <var> as a new filtered query over the model
                # filtered to the entries of right_path
                path = base_query.translate_path_to_field(right_path)
                field = OuterRef(path.name) if isinstance(
                    path, F) else OuterRef(path)
                subquery.filter &= COMPARISONS["Unify"]("pk", field)
                # Maybe redundant, but want to be sure
                base_query.variables[left] = subquery
            else:
                # var in _this
                # var in other_var
                raise UnsupportedError(
                    f"Unsupported partial expression: {expr}")
        else:
            # <value> in <partial>
            self.filter &= COMPARISONS["Unify"]("__".join(right_path[1:]),
                                                left)
Example #7
0
def isa_expr(expr: Expression, model: Model, **kwargs):
    assert expr.operator == "Isa"
    (left, right) = expr.args
    for attr in dot_path(left):
        model = getattr(model, attr).field.related_model
    constraint_type = apps.get_model(django_model_name(right.tag))
    assert not right.fields, "Unexpected fields in matches expression"
    return TRUE_FILTER if issubclass(model, constraint_type) else FALSE_FILTER
Example #8
0
 def isa_expr(self, expr: Expression):
     assert expr.operator == "Isa"
     (left, right) = expr.args
     left_path = dot_path(left)
     assert left_path[0] == self.name
     root = self.get_query_from_var(left_path[0])
     model = get_model_by_path(root.model, left_path[1:])
     ty = apps.get_model(django_model_name(right.tag))
     assert not right.fields, "Unexpected fields in matches expression"
     assert issubclass(
         model, ty), "Inapplicable rule should have been filtered out"
Example #9
0
    def translate_expr(self, expr: Expression):
        """Translate a Polar expression to a Django Q object."""
        assert isinstance(expr, Expression), "expected a Polar expression"

        # Check if either side of the expression starts with a lookup on
        # a variable. In which case, enter the subquery for that variable
        # instead and proceed as usual.
        if len(expr.args) == 2:
            left, right = expr.args
            left_path = dot_path(left)
            right_path = dot_path(right)

            if left_path:
                query = self.get_query_from_var(left_path[0])
                if query and query != self:
                    query.translate_expr(expr)
                    return self
            if right_path:
                query = self.get_query_from_var(right_path[0])
                if query and query != self:
                    query.translate_expr(expr)
                    return self

        if expr.operator in COMPARISONS:
            self.compare_expr(expr)
        elif expr.operator == "And":
            self.and_expr(expr)
        elif expr.operator == "Isa":
            self.isa_expr(expr)
        elif expr.operator == "In":
            self.in_expr(expr)
        elif expr.operator == "Not":
            self.not_expr(expr)
        else:
            raise UnsupportedError(f"Unsupported partial expression: {expr}")
        return self
Example #10
0
def translate_isa(expression: Expression, session: Session, model, get_model):
    assert expression.operator == "Isa"
    left, right = expression.args
    left_path = dot_path(left)
    assert left_path[0] == Variable("_this")
    left_path = left_path[1:]  # Drop _this.
    if left_path:
        for field_name in left_path:
            _, model, __ = get_relationship(model, field_name)

    assert not right.fields, "Unexpected fields in isa expression"
    constraint_type = get_model(right.tag)
    model_type = inspect(model, raiseerr=True).class_
    return sql.true() if issubclass(model_type,
                                    constraint_type) else sql.false()
Example #11
0
def compare_expr(expr: Expression, model: Model, path=(), **kwargs):
    assert expr.operator in COMPARISONS
    (left, right) = expr.args
    left_path = dot_path(left)
    if left_path:
        return COMPARISONS[expr.operator]("__".join(path + left_path), right)
    else:
        assert left == Variable("_this")
        if not isinstance(right, model):
            return FALSE_FILTER

        if expr.operator not in ("Eq", "Unify"):
            raise UnsupportedError(
                f"Unsupported comparison: {expr}. Models can only be compared"
                " with `=` or `==`")

        return COMPARISONS[expr.operator]("__".join(path + ("pk", )), right.pk)
Example #12
0
def in_expr(expr: Expression, model: Model, path=(), **kwargs):
    assert expr.operator == "In"
    (left, right) = expr.args
    right_path = dot_path(right)
    assert right_path, "RHS of in must be a dot lookup"
    right_path = path + right_path

    if isinstance(left, Expression):
        if left.operator == "And" and not left.args:
            # An unconstrained partial is in a list if the list is non-empty.
            count = Count("__".join(right_path))
            filter = COMPARISONS["Gt"]("__".join(right_path + ("count", )), 0)
            subquery = Subquery(
                model.objects.annotate(count).filter(filter).values("pk"))

            return contained_in("pk", subquery)
        else:
            return translate_expr(left, model, path=right_path, **kwargs)
    else:
        return COMPARISONS["Unify"]("__".join(right_path), left)
Example #13
0
def test_dot_path():
    non_dot = Expression("And", [])
    assert dot_path(non_dot) == ()

    this = Variable("_this")
    assert dot_path(this) == (this, )

    var = Variable("x")
    assert dot_path(var) == (var, )

    single_dot = Expression("Dot", [this, "created_by"])
    assert dot_path(single_dot) == (this, "created_by")

    double_dot = Expression("Dot", [single_dot, "username"])
    assert dot_path(double_dot) == (this, "created_by", "username")

    triple_dot = Expression("Dot", [double_dot, "first"])
    assert dot_path(triple_dot) == (this, "created_by", "username", "first")