12
12
13
13
14
14
class EmbeddedModelArrayField (ArrayField ):
15
- ALLOWED_LOOKUPS = {"exact" , "len" , "overlap" }
15
+ ALLOWED_LOOKUPS = {
16
+ "in" ,
17
+ "exact" ,
18
+ "iexact" ,
19
+ "regex" ,
20
+ "iregex" ,
21
+ "gt" ,
22
+ "gte" ,
23
+ "lt" ,
24
+ "lte" ,
25
+ "all" ,
26
+ "contained_by" ,
27
+ }
16
28
17
29
def __init__ (self , embedded_model , ** kwargs ):
18
30
if "size" in kwargs :
@@ -63,7 +75,7 @@ def get_lookup(self, name):
63
75
return super ().get_lookup (name ) if name in self .ALLOWED_LOOKUPS else None
64
76
65
77
66
- class EMFArrayRHSMixin :
78
+ class EMFArrayBuildinLookup ( Lookup ) :
67
79
def check_lhs (self ):
68
80
if not isinstance (self .lhs , KeyTransform ):
69
81
raise ValueError (
@@ -72,28 +84,35 @@ def check_lhs(self):
72
84
)
73
85
74
86
def process_rhs (self , compiler , connection ):
75
- values = self .rhs
87
+ value = self .rhs
88
+ if not self .get_db_prep_lookup_value_is_iterable :
89
+ value = [value ]
76
90
# Value must be serealized based on the query target.
77
- # If querying a subfield inside the array (i.e., a nested KeyTransform), use the output
91
+ # If querying a subfield inside tche array (i.e., a nested KeyTransform), use the output
78
92
# field of the subfield. Otherwise, use the base field of the array itself.
79
93
get_db_prep_value = self .lhs ._lhs .output_field .get_db_prep_value
80
- return None , [get_db_prep_value (values , connection , prepared = True )]
94
+ return None , [
95
+ v if hasattr (v , "as_mql" ) else get_db_prep_value (v , connection , prepared = True )
96
+ for v in value
97
+ ]
81
98
82
-
83
- @EmbeddedModelArrayField .register_lookup
84
- class EMFArrayExact (EMFArrayRHSMixin , lookups .Exact ):
85
99
def as_mql (self , compiler , connection ):
86
100
self .check_lhs ()
101
+ # Querying a subfield within the array elements (via nested KeyTransform).
102
+ # Replicates MongoDB's implicit ANY-match by mapping over the array and applying
103
+ # `$in` on the subfield.
87
104
lhs_mql , inner_lhs_mql = process_lhs (self , compiler , connection )
88
- value = process_rhs (self , compiler , connection )
105
+ values = process_rhs (self , compiler , connection )
89
106
return {
90
107
"$anyElementTrue" : {
91
108
"$ifNull" : [
92
109
{
93
110
"$map" : {
94
111
"input" : lhs_mql ,
95
112
"as" : "item" ,
96
- "in" : {"$eq" : [inner_lhs_mql , value ]},
113
+ "in" : connection .mongo_operators [self .lookup_name ](
114
+ inner_lhs_mql , values
115
+ ),
97
116
}
98
117
},
99
118
[],
@@ -103,31 +122,90 @@ def as_mql(self, compiler, connection):
103
122
104
123
105
124
@EmbeddedModelArrayField .register_lookup
106
- class ArrayOverlap (EMFArrayRHSMixin , Lookup ):
107
- lookup_name = "overlap"
125
+ class EMFArrayIn (EMFArrayBuildinLookup , lookups .In ):
126
+ pass
127
+
128
+
129
+ @EmbeddedModelArrayField .register_lookup
130
+ class EMFArrayExact (EMFArrayBuildinLookup , lookups .Exact ):
131
+ pass
132
+
133
+
134
+ @EmbeddedModelArrayField .register_lookup
135
+ class EMFArrayIExact (EMFArrayBuildinLookup , lookups .IExact ):
136
+ get_db_prep_lookup_value_is_iterable = False
137
+
138
+
139
+ @EmbeddedModelArrayField .register_lookup
140
+ class EMFArrayGreaterThan (EMFArrayBuildinLookup , lookups .GreaterThan ):
141
+ pass
142
+
143
+
144
+ @EmbeddedModelArrayField .register_lookup
145
+ class EMFArrayGreaterThanOrEqual (EMFArrayBuildinLookup , lookups .GreaterThanOrEqual ):
146
+ pass
147
+
148
+
149
+ @EmbeddedModelArrayField .register_lookup
150
+ class EMFArrayLessThan (EMFArrayBuildinLookup , lookups .LessThan ):
151
+ pass
152
+
153
+
154
+ @EmbeddedModelArrayField .register_lookup
155
+ class EMFArrayLessThanOrEqual (EMFArrayBuildinLookup , lookups .LessThanOrEqual ):
156
+ pass
157
+
158
+
159
+ @EmbeddedModelArrayField .register_lookup
160
+ class EMFArrayAll (EMFArrayBuildinLookup , Lookup ):
161
+ lookup_name = "all"
162
+ get_db_prep_lookup_value_is_iterable = False
108
163
109
164
def as_mql (self , compiler , connection ):
110
165
self .check_lhs ()
111
- # Querying a subfield within the array elements (via nested KeyTransform).
112
- # Replicates MongoDB's implicit ANY-match by mapping over the array and applying
113
- # `$in` on the subfield.
114
- lhs_mql = process_lhs (self , compiler , connection )
166
+ lhs_mql , inner_lhs_mql = process_lhs (self , compiler , connection )
115
167
values = process_rhs (self , compiler , connection )
116
- lhs_mql , inner_lhs_mql = lhs_mql
117
168
return {
118
- "$anyElementTrue" : {
119
- "$ifNull" : [
120
- {
121
- "$map" : {
122
- "input" : lhs_mql ,
123
- "as" : "item" ,
124
- "in" : {"$in" : [inner_lhs_mql , values ]},
125
- }
126
- },
127
- [],
128
- ]
169
+ "$setIsSubset" : [
170
+ values ,
171
+ {
172
+ "$ifNull" : [
173
+ {
174
+ "$map" : {
175
+ "input" : lhs_mql ,
176
+ "as" : "item" ,
177
+ "in" : inner_lhs_mql ,
178
+ }
179
+ },
180
+ [],
181
+ ]
182
+ },
183
+ ]
184
+ }
185
+
186
+
187
+ @EmbeddedModelArrayField .register_lookup
188
+ class ArrayContainedBy (EMFArrayBuildinLookup , Lookup ):
189
+ lookup_name = "contained_by"
190
+ get_db_prep_lookup_value_is_iterable = False
191
+
192
+ def as_mql (self , compiler , connection ):
193
+ lhs_mql , inner_lhs_mql = process_lhs (self , compiler , connection )
194
+ lhs_mql = {
195
+ "$map" : {
196
+ "input" : lhs_mql ,
197
+ "as" : "item" ,
198
+ "in" : inner_lhs_mql ,
129
199
}
130
200
}
201
+ value = process_rhs (self , compiler , connection )
202
+ return {
203
+ "$and" : [
204
+ {"$ne" : [lhs_mql , None ]},
205
+ {"$ne" : [value , None ]},
206
+ {"$setIsSubset" : [lhs_mql , value ]},
207
+ ]
208
+ }
131
209
132
210
133
211
class KeyTransform (Transform ):
@@ -159,6 +237,8 @@ def get_transform(self, name):
159
237
# Once the sub lhs is a transform, all the filter are applied over it.
160
238
# Otherwise get transform from EMF.
161
239
if transform := self ._lhs .get_transform (name ):
240
+ if isinstance (transform , KeyTransformFactory ):
241
+ raise ValueError ("Cannot perform multiple levels of array traversal in a query." )
162
242
self ._sub_transform = transform
163
243
return self
164
244
output_field = self ._lhs .output_field
0 commit comments