1
1
import datetime
2
2
from decimal import Decimal
3
+ from functools import partialmethod
3
4
from uuid import UUID
4
5
5
6
from bson import Decimal128
29
30
from django_mongodb_backend .query_utils import process_lhs
30
31
31
32
32
- def base_expression (self , compiler , connection , as_path = False , ** extra ):
33
- if as_path and hasattr (self , "as_mql_path" ) and getattr (self , "can_use_path" , False ):
33
+ def base_expression (self , compiler , connection , as_expr = False , ** extra ):
34
+ if not as_expr and hasattr (self , "as_mql_path" ) and getattr (self , "can_use_path" , False ):
34
35
return self .as_mql_path (compiler , connection , ** extra )
35
36
36
37
expr = self .as_mql_expr (compiler , connection , ** extra )
37
- return {"$expr" : expr } if as_path else expr
38
+ return expr if as_expr else {"$expr" : expr }
38
39
39
40
40
41
def case (self , compiler , connection ):
41
42
case_parts = []
42
43
for case in self .cases :
43
44
case_mql = {}
44
45
try :
45
- case_mql ["case" ] = case .as_mql (compiler , connection )
46
+ case_mql ["case" ] = case .as_mql (compiler , connection , as_expr = True )
46
47
except EmptyResultSet :
47
48
continue
48
49
except FullResultSet :
49
- default_mql = case .result .as_mql (compiler , connection )
50
+ default_mql = case .result .as_mql (compiler , connection , as_expr = True )
50
51
break
51
- case_mql ["then" ] = case .result .as_mql (compiler , connection )
52
+ case_mql ["then" ] = case .result .as_mql (compiler , connection , as_expr = True )
52
53
case_parts .append (case_mql )
53
54
else :
54
- default_mql = self .default .as_mql (compiler , connection )
55
+ default_mql = self .default .as_mql (compiler , connection , as_expr = True )
55
56
if not case_parts :
56
57
return default_mql
57
58
return {
@@ -62,7 +63,7 @@ def case(self, compiler, connection):
62
63
}
63
64
64
65
65
- def col (self , compiler , connection , as_path = False ): # noqa: ARG001
66
+ def col (self , compiler , connection , as_expr = False ): # noqa: ARG001
66
67
# If the column is part of a subquery and belongs to one of the parent
67
68
# queries, it will be stored for reference using $let in a $lookup stage.
68
69
# If the query is built with `alias_cols=False`, treat the column as
@@ -80,39 +81,39 @@ def col(self, compiler, connection, as_path=False): # noqa: ARG001
80
81
# Add the column's collection's alias for columns in joined collections.
81
82
has_alias = self .alias and self .alias != compiler .collection_name
82
83
prefix = f"{ self .alias } ." if has_alias else ""
83
- if not as_path :
84
+ if as_expr :
84
85
prefix = f"${ prefix } "
85
86
return f"{ prefix } { self .target .column } "
86
87
87
88
88
- def col_pairs (self , compiler , connection , as_path = False ):
89
+ def col_pairs (self , compiler , connection , as_expr = False ):
89
90
cols = self .get_cols ()
90
91
if len (cols ) > 1 :
91
92
raise NotSupportedError ("ColPairs is not supported." )
92
- return cols [0 ].as_mql (compiler , connection , as_path = as_path )
93
+ return cols [0 ].as_mql (compiler , connection , as_expr = as_expr )
93
94
94
95
95
96
def combined_expression (self , compiler , connection ):
96
97
expressions = [
97
- self .lhs .as_mql (compiler , connection ),
98
- self .rhs .as_mql (compiler , connection ),
98
+ self .lhs .as_mql (compiler , connection , as_expr = True ),
99
+ self .rhs .as_mql (compiler , connection , as_expr = True ),
99
100
]
100
101
return connection .ops .combine_expression (self .connector , expressions )
101
102
102
103
103
104
def expression_wrapper (self , compiler , connection ):
104
- return self .expression .as_mql (compiler , connection )
105
+ return self .expression .as_mql (compiler , connection , as_expr = True )
105
106
106
107
107
108
def negated_expression (self , compiler , connection ):
108
109
return {"$not" : expression_wrapper (self , compiler , connection )}
109
110
110
111
111
112
def order_by (self , compiler , connection ):
112
- return self .expression .as_mql (compiler , connection )
113
+ return self .expression .as_mql (compiler , connection , as_expr = True )
113
114
114
115
115
- def query (self , compiler , connection , get_wrapping_pipeline = None , as_path = False ):
116
+ def query (self , compiler , connection , get_wrapping_pipeline = None , as_expr = False ):
116
117
subquery_compiler = self .get_compiler (connection = connection )
117
118
subquery_compiler .pre_sql_setup (with_col_aliases = False )
118
119
field_name , expr = subquery_compiler .columns [0 ]
@@ -132,7 +133,7 @@ def query(self, compiler, connection, get_wrapping_pipeline=None, as_path=False)
132
133
"as" : table_output ,
133
134
"from" : from_table ,
134
135
"let" : {
135
- compiler .PARENT_FIELD_TEMPLATE .format (i ): col .as_mql (compiler , connection )
136
+ compiler .PARENT_FIELD_TEMPLATE .format (i ): col .as_mql (compiler , connection , as_expr = True )
136
137
for col , i in subquery_compiler .column_indices .items ()
137
138
},
138
139
}
@@ -154,16 +155,16 @@ def query(self, compiler, connection, get_wrapping_pipeline=None, as_path=False)
154
155
# Erase project_fields since the required value is projected above.
155
156
subquery .project_fields = None
156
157
compiler .subqueries .append (subquery )
157
- if as_path :
158
- return f"{ table_output } .{ field_name } "
159
- return f"$ { table_output } .{ field_name } "
158
+ if as_expr :
159
+ return f"$ { table_output } .{ field_name } "
160
+ return f"{ table_output } .{ field_name } "
160
161
161
162
162
163
def raw_sql (self , compiler , connection ): # noqa: ARG001
163
164
raise NotSupportedError ("RawSQL is not supported on MongoDB." )
164
165
165
166
166
- def ref (self , compiler , connection , as_path = False ): # noqa: ARG001
167
+ def ref (self , compiler , connection , as_expr = False ): # noqa: ARG001
167
168
prefix = (
168
169
f"{ self .source .alias } ."
169
170
if isinstance (self .source , Col ) and self .source .alias != compiler .collection_name
@@ -173,7 +174,7 @@ def ref(self, compiler, connection, as_path=False): # noqa: ARG001
173
174
refs , _ = compiler .columns [self .ordinal - 1 ]
174
175
else :
175
176
refs = self .refs
176
- if not as_path :
177
+ if as_expr :
177
178
prefix = f"${ prefix } "
178
179
return f"{ prefix } { refs } "
179
180
@@ -187,27 +188,29 @@ def star(self, compiler, connection): # noqa: ARG001
187
188
return {"$literal" : True }
188
189
189
190
190
- def subquery (self , compiler , connection , get_wrapping_pipeline = None ):
191
+ def subquery (self , compiler , connection , get_wrapping_pipeline = None , as_expr = False ):
191
192
return self .query .as_mql (
192
- compiler , connection , get_wrapping_pipeline = get_wrapping_pipeline , as_path = False
193
+ compiler , connection , get_wrapping_pipeline = get_wrapping_pipeline , as_expr = as_expr
193
194
)
194
195
195
196
196
197
def exists (self , compiler , connection , get_wrapping_pipeline = None ):
197
198
try :
198
- lhs_mql = subquery (self , compiler , connection , get_wrapping_pipeline = get_wrapping_pipeline )
199
+ lhs_mql = subquery (
200
+ self , compiler , connection , get_wrapping_pipeline = get_wrapping_pipeline , as_expr = True
201
+ )
199
202
except EmptyResultSet :
200
- return Value (False ).as_mql (compiler , connection )
203
+ return Value (False ).as_mql (compiler , connection , as_expr = True )
201
204
return connection .mongo_expr_operators ["isnull" ](lhs_mql , False )
202
205
203
206
204
207
def when (self , compiler , connection ):
205
- return self .condition .as_mql (compiler , connection )
208
+ return self .condition .as_mql (compiler , connection , as_expr = True )
206
209
207
210
208
- def value (self , compiler , connection , as_path = False ): # noqa: ARG001
211
+ def value (self , compiler , connection , as_expr = False ): # noqa: ARG001
209
212
value = self .value
210
- if isinstance (value , (list , int )) and not as_path :
213
+ if isinstance (value , (list , int )) and as_expr :
211
214
# Wrap lists & numbers in $literal to prevent ambiguity when Value
212
215
# appears in $project.
213
216
return {"$literal" : value }
@@ -248,6 +251,7 @@ def register_expressions():
248
251
Ref .is_simple_column = ref_is_simple_column
249
252
ResolvedOuterRef .as_mql = ResolvedOuterRef .as_sql
250
253
Star .as_mql_expr = star
251
- Subquery .as_mql_expr = subquery
254
+ Subquery .as_mql_expr = partialmethod (subquery , as_expr = True )
255
+ Subquery .as_mql_path = partialmethod (subquery , as_expr = False )
252
256
When .as_mql_expr = when
253
257
Value .as_mql = value
0 commit comments