9
9
Case ,
10
10
Col ,
11
11
CombinedExpression ,
12
+ Exists ,
12
13
ExpressionWrapper ,
13
14
F ,
14
15
NegatedExpression ,
@@ -50,6 +51,15 @@ def case(self, compiler, connection):
50
51
51
52
52
53
def 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 )} "
53
63
# Add the column's collection's alias for columns in joined collections.
54
64
prefix = f"{ self .alias } ." if self .alias != compiler .collection_name else ""
55
65
return f"${ prefix } { self .target .column } "
@@ -79,8 +89,64 @@ def order_by(self, compiler, connection):
79
89
return self .expression .as_mql (compiler , connection )
80
90
81
91
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 } "
84
150
85
151
86
152
def raw_sql (self , compiler , connection ): # noqa: ARG001
@@ -100,8 +166,13 @@ def star(self, compiler, connection): # noqa: ARG001
100
166
return {"$literal" : True }
101
167
102
168
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 )
105
176
106
177
107
178
def when (self , compiler , connection ):
@@ -130,6 +201,7 @@ def register_expressions():
130
201
Case .as_mql = case
131
202
Col .as_mql = col
132
203
CombinedExpression .as_mql = combined_expression
204
+ Exists .as_mql = exists
133
205
ExpressionWrapper .as_mql = expression_wrapper
134
206
F .as_mql = f
135
207
NegatedExpression .as_mql = negated_expression
0 commit comments