99 Case ,
1010 Col ,
1111 CombinedExpression ,
12+ Exists ,
1213 ExpressionWrapper ,
1314 F ,
1415 NegatedExpression ,
@@ -50,6 +51,15 @@ def case(self, compiler, connection):
5051
5152
5253def col (self , compiler , connection ): # noqa: ARG001
54+ # If it is a subquery and the columns belongs to one of the ancestors,
55+ # the column shall be stored to be passed using $let in a $lookup stage.
56+ if self .alias in compiler .parent_collections :
57+ try :
58+ index = compiler .column_mapping [self ]
59+ except KeyError :
60+ index = len (compiler .column_mapping )
61+ compiler .column_mapping [self ] = index
62+ return f"$${ compiler .PARENT_FIELD_TEMPLATE .format (index )} "
5363 # Add the column's collection's alias for columns in joined collections.
5464 prefix = f"{ self .alias } ." if self .alias != compiler .collection_name else ""
5565 return f"${ prefix } { self .target .column } "
@@ -79,8 +89,64 @@ def order_by(self, compiler, connection):
7989 return self .expression .as_mql (compiler , connection )
8090
8191
82- def query (self , compiler , connection ): # noqa: ARG001
83- raise NotSupportedError ("Using a QuerySet in annotate() is not supported on MongoDB." )
92+ def query (self , compiler , connection ):
93+ subquery_compiler = self .get_compiler (connection = connection )
94+ subquery_compiler .pre_sql_setup (with_col_aliases = False )
95+ subquery_compiler .parent_collections = {compiler .collection_name } | compiler .parent_collections
96+ columns = subquery_compiler .get_columns ()
97+ field_name , expr = columns [0 ]
98+ subquery = subquery_compiler .build_query (
99+ columns
100+ if subquery_compiler .query .annotations or not subquery_compiler .query .default_cols
101+ else None
102+ )
103+ table_output = f"__subquery{ len (compiler .subqueries )} "
104+ result_query = compiler .query_class (compiler )
105+ pipeline = subquery .get_pipeline ()
106+ # the result must be a list of values. Se we compress the output
107+ if not self .has_limit_one ():
108+ pipeline .extend (
109+ [
110+ {
111+ "$group" : {
112+ "_id" : None ,
113+ "dummy_name" : {"$addToSet" : expr .as_mql (subquery_compiler , connection )},
114+ }
115+ },
116+ {"$project" : {field_name : "$dummy_name" }},
117+ ]
118+ )
119+ result_query .lookup_pipeline = [
120+ {
121+ "$lookup" : {
122+ "from" : self .get_meta ().db_table ,
123+ "pipeline" : pipeline ,
124+ "as" : table_output ,
125+ "let" : {
126+ compiler .PARENT_FIELD_TEMPLATE .format (i ): col .as_mql (compiler , connection )
127+ for col , i in subquery_compiler .column_mapping .items ()
128+ },
129+ }
130+ },
131+ {
132+ "$set" : {
133+ table_output : {
134+ "$cond" : {
135+ "if" : {
136+ "$or" : [
137+ {"$eq" : [{"$type" : f"${ table_output } " }, "missing" ]},
138+ {"$eq" : [{"$size" : f"${ table_output } " }, 0 ]},
139+ ]
140+ },
141+ "then" : {},
142+ "else" : {"$arrayElemAt" : [f"${ table_output } " , 0 ]},
143+ }
144+ }
145+ }
146+ },
147+ ]
148+ compiler .subqueries .append (result_query )
149+ return f"${ table_output } .{ field_name } "
84150
85151
86152def raw_sql (self , compiler , connection ): # noqa: ARG001
@@ -100,8 +166,13 @@ def star(self, compiler, connection): # noqa: ARG001
100166 return {"$literal" : True }
101167
102168
103- def subquery (self , compiler , connection ): # noqa: ARG001
104- raise NotSupportedError (f"{ self .__class__ .__name__ } is not supported on MongoDB." )
169+ def subquery (self , compiler , connection ):
170+ return self .query .as_mql (compiler , connection )
171+
172+
173+ def exists (self , compiler , connection ):
174+ lhs_mql = subquery (self , compiler , connection )
175+ return connection .mongo_operators ["isnull" ](lhs_mql , False )
105176
106177
107178def when (self , compiler , connection ):
@@ -130,6 +201,7 @@ def register_expressions():
130201 Case .as_mql = case
131202 Col .as_mql = col
132203 CombinedExpression .as_mql = combined_expression
204+ Exists .as_mql = exists
133205 ExpressionWrapper .as_mql = expression_wrapper
134206 F .as_mql = f
135207 NegatedExpression .as_mql = negated_expression
0 commit comments