|
1 | 1 | import re
|
2 | 2 | from functools import wraps
|
3 | 3 |
|
4 |
| -from django.core.exceptions import FullResultSet |
5 |
| -from django.db import DatabaseError, IntegrityError, NotSupportedError |
6 |
| -from django.db.models.lookups import UUIDTextMixin |
7 |
| -from django.db.models.query import QuerySet |
8 |
| -from django.db.models.sql.where import OR, SubqueryConstraint |
9 |
| -from django.utils.tree import Node |
| 4 | +from django.db import DatabaseError, IntegrityError |
10 | 5 | from pymongo import ASCENDING, DESCENDING
|
11 | 6 | from pymongo.errors import DuplicateKeyError, PyMongoError
|
12 | 7 |
|
@@ -138,210 +133,3 @@ def get_cursor(self):
|
138 | 133 | if self.query.high_mark is not None:
|
139 | 134 | cursor.limit(int(self.query.high_mark - self.query.low_mark))
|
140 | 135 | return cursor
|
141 |
| - |
142 |
| - def add_filters(self, filters, query=None): |
143 |
| - children = self._get_children(filters.children) |
144 |
| - |
145 |
| - if query is None: |
146 |
| - query = self.mongo_query |
147 |
| - |
148 |
| - if filters.connector == OR: |
149 |
| - assert "$or" not in query, "Multiple ORs are not supported." |
150 |
| - or_conditions = query["$or"] = [] |
151 |
| - |
152 |
| - if filters.negated: |
153 |
| - self._negated = not self._negated |
154 |
| - |
155 |
| - for child in children: |
156 |
| - subquery = {} if filters.connector == OR else query |
157 |
| - |
158 |
| - if isinstance(child, Node): |
159 |
| - if filters.connector == OR and child.connector == OR and len(child.children) > 1: |
160 |
| - raise DatabaseError("Nested ORs are not supported.") |
161 |
| - |
162 |
| - if filters.connector == OR and filters.negated: |
163 |
| - raise NotImplementedError("Negated ORs are not supported.") |
164 |
| - |
165 |
| - self.add_filters(child, query=subquery) |
166 |
| - |
167 |
| - if filters.connector == OR and subquery: |
168 |
| - or_conditions.extend(subquery.pop("$or", [])) |
169 |
| - if subquery: |
170 |
| - or_conditions.append(subquery) |
171 |
| - |
172 |
| - continue |
173 |
| - |
174 |
| - try: |
175 |
| - field, lookup_type, value = self._decode_child(child) |
176 |
| - except FullResultSet: |
177 |
| - continue |
178 |
| - |
179 |
| - column = field.column |
180 |
| - existing = subquery.get(column) |
181 |
| - |
182 |
| - if self._negated and lookup_type in NEGATED_OPERATORS_MAP: |
183 |
| - op_func = NEGATED_OPERATORS_MAP[lookup_type] |
184 |
| - already_negated = True |
185 |
| - else: |
186 |
| - op_func = OPERATORS_MAP[lookup_type] |
187 |
| - if self._negated: |
188 |
| - already_negated = False |
189 |
| - |
190 |
| - lookup = op_func(value) |
191 |
| - |
192 |
| - if existing is None: |
193 |
| - if self._negated and not already_negated: |
194 |
| - lookup = {"$not": lookup} |
195 |
| - subquery[column] = lookup |
196 |
| - if filters.connector == OR and subquery: |
197 |
| - or_conditions.append(subquery) |
198 |
| - continue |
199 |
| - |
200 |
| - if not isinstance(existing, dict): |
201 |
| - if not self._negated: |
202 |
| - # {'a': o1} + {'a': o2} --> {'a': {'$all': [o1, o2]}} |
203 |
| - assert not isinstance(lookup, dict) |
204 |
| - subquery[column] = {"$all": [existing, lookup]} |
205 |
| - else: |
206 |
| - # {'a': o1} + {'a': {'$not': o2}} --> {'a': {'$all': [o1], '$nin': [o2]}} |
207 |
| - if already_negated: |
208 |
| - assert list(lookup) == ["$ne"] |
209 |
| - lookup = lookup["$ne"] |
210 |
| - assert not isinstance(lookup, dict) |
211 |
| - subquery[column] = {"$all": [existing], "$nin": [lookup]} |
212 |
| - else: |
213 |
| - not_ = existing.pop("$not", None) |
214 |
| - if not_: |
215 |
| - assert not existing |
216 |
| - if isinstance(lookup, dict): |
217 |
| - assert list(lookup) == ["$ne"] |
218 |
| - lookup = next(iter(lookup.values())) |
219 |
| - assert not isinstance(lookup, dict), (not_, lookup) |
220 |
| - if self._negated: |
221 |
| - # {'not': {'a': o1}} + {'a': {'not': o2}} --> {'a': {'nin': [o1, o2]}} |
222 |
| - subquery[column] = {"$nin": [not_, lookup]} |
223 |
| - else: |
224 |
| - # {'not': {'a': o1}} + {'a': o2} --> |
225 |
| - # {'a': {'nin': [o1], 'all': [o2]}} |
226 |
| - subquery[column] = {"$nin": [not_], "$all": [lookup]} |
227 |
| - else: |
228 |
| - if isinstance(lookup, dict): |
229 |
| - if "$ne" in lookup: |
230 |
| - if "$nin" in existing: |
231 |
| - # {'$nin': [o1, o2]} + {'$ne': o3} --> {'$nin': [o1, o2, o3]} |
232 |
| - assert "$ne" not in existing |
233 |
| - existing["$nin"].append(lookup["$ne"]) |
234 |
| - elif "$ne" in existing: |
235 |
| - # {'$ne': o1} + {'$ne': o2} --> {'$nin': [o1, o2]} |
236 |
| - existing["$nin"] = [existing.pop("$ne"), lookup["$ne"]] |
237 |
| - else: |
238 |
| - existing.update(lookup) |
239 |
| - else: |
240 |
| - if "$in" in lookup and "$in" in existing: |
241 |
| - # {'$in': o1} + {'$in': o2} --> {'$in': o1 union o2} |
242 |
| - existing["$in"] = list(set(lookup["$in"] + existing["$in"])) |
243 |
| - else: |
244 |
| - # {'$gt': o1} + {'$lt': o2} --> {'$gt': o1, '$lt': o2} |
245 |
| - assert all(key not in existing for key in lookup), [ |
246 |
| - lookup, |
247 |
| - existing, |
248 |
| - ] |
249 |
| - existing.update(lookup) |
250 |
| - else: |
251 |
| - key = "$nin" if self._negated else "$all" |
252 |
| - existing.setdefault(key, []).append(lookup) |
253 |
| - |
254 |
| - if filters.connector == OR and subquery: |
255 |
| - or_conditions.append(subquery) |
256 |
| - |
257 |
| - if filters.negated: |
258 |
| - self._negated = not self._negated |
259 |
| - |
260 |
| - def _decode_child(self, child): |
261 |
| - """ |
262 |
| - Produce arguments suitable for add_filter from a WHERE tree leaf |
263 |
| - (a tuple). |
264 |
| - """ |
265 |
| - if isinstance(child, UUIDTextMixin): |
266 |
| - raise NotSupportedError("Pattern lookups on UUIDField are not supported.") |
267 |
| - |
268 |
| - rhs, rhs_params = child.process_rhs(self.compiler, self.connection) |
269 |
| - lookup_type = child.lookup_name |
270 |
| - value = rhs_params |
271 |
| - packed = child.lhs.get_group_by_cols()[0] |
272 |
| - alias = packed.alias |
273 |
| - column = packed.target.column |
274 |
| - field = child.lhs.output_field |
275 |
| - opts = self.query.model._meta |
276 |
| - if alias and alias != opts.db_table: |
277 |
| - raise NotSupportedError("MongoDB doesn't support JOINs and multi-table inheritance.") |
278 |
| - |
279 |
| - # For parent.child_set queries the field held by the constraint |
280 |
| - # is the parent's primary key, while the field the filter |
281 |
| - # should consider is the child's foreign key field. |
282 |
| - if column != field.column: |
283 |
| - if not field.primary_key: |
284 |
| - raise NotSupportedError( |
285 |
| - "MongoDB doesn't support filtering on non-primary key ForeignKey fields." |
286 |
| - ) |
287 |
| - |
288 |
| - field = next(f for f in opts.fields if f.column == column) |
289 |
| - |
290 |
| - value = self._normalize_lookup_value(lookup_type, value, field) |
291 |
| - |
292 |
| - return field, lookup_type, value |
293 |
| - |
294 |
| - def _normalize_lookup_value(self, lookup_type, value, field): |
295 |
| - """ |
296 |
| - Undo preparations done by lookups not suitable for MongoDB, and pass |
297 |
| - the lookup argument through DatabaseOperations.prep_lookup_value(). |
298 |
| - """ |
299 |
| - # Undo Lookup.get_db_prep_lookup() putting params in a list. |
300 |
| - if lookup_type not in ("in", "range"): |
301 |
| - if len(value) > 1: |
302 |
| - raise DatabaseError( |
303 |
| - "Filter lookup type was %s; expected the filter argument " |
304 |
| - "not to be a list. Only 'in'-filters can be used with " |
305 |
| - "lists." % lookup_type |
306 |
| - ) |
307 |
| - value = value[0] |
308 |
| - |
309 |
| - # Remove percent signs added by PatternLookup.process_rhs() for LIKE |
310 |
| - # queries. |
311 |
| - if lookup_type in ("startswith", "istartswith"): |
312 |
| - value = value[:-1] |
313 |
| - elif lookup_type in ("endswith", "iendswith"): |
314 |
| - value = value[1:] |
315 |
| - elif lookup_type in ("contains", "icontains"): |
316 |
| - value = value[1:-1] |
317 |
| - |
318 |
| - return self.ops.prep_lookup_value(value, field, lookup_type) |
319 |
| - |
320 |
| - def _get_children(self, children): |
321 |
| - """ |
322 |
| - Filter out nodes of the given constraint tree not needed for |
323 |
| - MongoDB queries. Check that the given constraints are supported. |
324 |
| - """ |
325 |
| - result = [] |
326 |
| - for child in children: |
327 |
| - if isinstance(child, SubqueryConstraint): |
328 |
| - raise NotSupportedError("Subqueries are not supported.") |
329 |
| - |
330 |
| - if isinstance(child, tuple): |
331 |
| - constraint, lookup_type, _, value = child |
332 |
| - |
333 |
| - # When doing a lookup using a QuerySet Django would use |
334 |
| - # a subquery, but this won't work for MongoDB. |
335 |
| - # TODO: Add a supports_subqueries feature and let Django |
336 |
| - # evaluate subqueries instead of passing them as SQL |
337 |
| - # strings (QueryWrappers) to filtering. |
338 |
| - if isinstance(value, QuerySet): |
339 |
| - raise NotSupportedError("Subqueries are not supported.") |
340 |
| - |
341 |
| - # Remove leafs that were automatically added by |
342 |
| - # sql.Query.add_filter() to handle negations of outer joins. |
343 |
| - if lookup_type == "isnull" and constraint.field is None: |
344 |
| - continue |
345 |
| - |
346 |
| - result.append(child) |
347 |
| - return result |
0 commit comments