@@ -661,61 +661,61 @@ def get_combinator_queries(self):
661661 combinator_pipeline .append ({"$unset" : "_id" })
662662 return combinator_pipeline
663663
664- def _get_pushable_conditions (self ):
665- """
666- Return a dict mapping each alias to a WhereNode holding its pushable
667- condition.
668- """
669-
670- def collect_pushable (expr , negated = False ):
671- if expr is None or isinstance (expr , NothingNode ):
664+ @classmethod
665+ def _collect_pushable (cls , expr , negated = False ):
666+ if expr is None or isinstance (expr , NothingNode ):
667+ return {}
668+ if isinstance (expr , WhereNode ):
669+ # Apply De Morgan: track negation so connectors are flipped
670+ # when needed.
671+ negated ^= expr .negated
672+ pushable_expressions = [
673+ cls ._collect_pushable (sub_expr , negated = negated )
674+ for sub_expr in expr .children
675+ if sub_expr is not None
676+ ]
677+ operator = expr .connector
678+ if operator == XOR :
672679 return {}
673- if isinstance (expr , WhereNode ):
674- # Apply De Morgan: track negation so connectors are flipped
675- # when needed.
676- negated ^= expr .negated
677- pushable_expressions = [
678- collect_pushable (sub_expr , negated = negated )
679- for sub_expr in expr .children
680- if sub_expr is not None
681- ]
682- operator = expr .connector
683- if operator == XOR :
684- return {}
685- if negated :
686- operator = OR if operator == AND else AND
687- alias_children = defaultdict (list )
688- for pe in pushable_expressions :
689- for alias , expressions in pe .items ():
690- alias_children [alias ].append (expressions )
691- # Build per-alias pushable condition nodes.
692- if operator == AND :
693- return {
694- alias : WhereNode (children = children , negated = False , connector = operator )
695- for alias , children in alias_children .items ()
696- }
697- # Only aliases shared across all branches are pushable for OR.
698- shared_alias = (
699- set .intersection (* (set (pe ) for pe in pushable_expressions ))
700- if pushable_expressions
701- else set ()
702- )
680+ if negated :
681+ operator = OR if operator == AND else AND
682+ alias_children = defaultdict (list )
683+ for pe in pushable_expressions :
684+ for alias , expressions in pe .items ():
685+ alias_children [alias ].append (expressions )
686+ # Build per-alias pushable condition nodes.
687+ if operator == AND :
703688 return {
704689 alias : WhereNode (children = children , negated = False , connector = operator )
705690 for alias , children in alias_children .items ()
706- if alias in shared_alias
707691 }
708- # A leaf is pushable only when comparing a field to a constant or
709- # simple value.
710- if isinstance (expr .lhs , Col ) and (
711- is_constant_value (expr .rhs ) or getattr (expr .rhs , "is_simple_column" , False )
712- ):
713- alias = expr .lhs .alias
714- expr = WhereNode (children = [expr ], negated = negated )
715- return {alias : expr }
716- return {}
692+ # Only aliases shared across all branches are pushable for OR.
693+ shared_alias = (
694+ set .intersection (* (set (pe ) for pe in pushable_expressions ))
695+ if pushable_expressions
696+ else set ()
697+ )
698+ return {
699+ alias : WhereNode (children = children , negated = False , connector = operator )
700+ for alias , children in alias_children .items ()
701+ if alias in shared_alias
702+ }
703+ # A leaf is pushable only when comparing a field to a constant or
704+ # simple value.
705+ if isinstance (expr .lhs , Col ) and (
706+ is_constant_value (expr .rhs ) or getattr (expr .rhs , "is_simple_column" , False )
707+ ):
708+ alias = expr .lhs .alias
709+ expr = WhereNode (children = [expr ], negated = negated )
710+ return {alias : expr }
711+ return {}
717712
718- return collect_pushable (self .get_where ())
713+ def _get_pushable_conditions (self ):
714+ """
715+ Return a dict mapping each alias to a WhereNode holding its pushable
716+ condition.
717+ """
718+ return self ._collect_pushable (self .get_where ())
719719
720720 def get_lookup_pipeline (self ):
721721 result = []
0 commit comments