@@ -123,7 +123,13 @@ def extra_where(self, compiler, connection): # noqa: ARG001
123
123
raise NotSupportedError ("QuerySet.extra() is not supported on MongoDB." )
124
124
125
125
126
- def join (self , compiler , connection , pushed_expressions = None ):
126
+ def join (self , compiler , connection , pushed_expression = None ):
127
+ """
128
+ Generate a MongoDB $lookup stage for a join. Optionally accepts a pushed_expression,
129
+ which is a filter expression involving fields from the joined collection, and
130
+ can be pushed into the lookup pipeline for optimization.
131
+ """
132
+
127
133
parent_template = "parent__field__"
128
134
129
135
def _get_reroot_replacements (expressions ):
@@ -182,12 +188,18 @@ def _get_reroot_replacements(expressions):
182
188
extra .replace_expressions (replacements ).as_mql (compiler , connection )
183
189
)
184
190
185
- if pushed_expressions and self .join_type == INNER :
186
- rerooted_replacement = _get_reroot_replacements (pushed_expressions )
191
+ # pushed_expression is a filter expression from the outer WHERE clause
192
+ # that involves fields from the joined (right-hand) table and possibly the
193
+ # outer (left-hand) table.
194
+ # If it can be safely evaluated within the $lookup pipeline
195
+ # (e.g., field comparisons like right.status = left.id), it is
196
+ # "pushed down" into the join's $match stage to reduce the volume of
197
+ # joined documents. This only applies to inner joins, as pushing
198
+ # filters into a left join can change the semantics of the result.
199
+ if pushed_expression and self .join_type == INNER :
200
+ rerooted_replacement = _get_reroot_replacements (pushed_expression )
187
201
extra_conditions .append (
188
- pushed_expressions .replace_expressions (rerooted_replacement ).as_mql (
189
- compiler , connection
190
- )
202
+ pushed_expression .replace_expressions (rerooted_replacement ).as_mql (compiler , connection )
191
203
)
192
204
193
205
lookup_pipeline = [
0 commit comments