Permalink
Browse files

[soc2010/query-refactor] Cleaned up implementation of negation in Mon…

…goDB, and no longer rely on a feature from MongoDB unstable version.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/query-refactor@13368 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent dd5e718 commit 706b18966f7f6dcef715ca7b5230e72147cefda6 @alex alex committed Jun 21, 2010
@@ -10,6 +10,7 @@
class DatabaseFeatures(object):
interprets_empty_strings_as_nulls = False
typed_columns = False
+ sql_nulls = False
class DatabaseOperations(object):
@@ -1,11 +1,14 @@
+from django.db.models.sql.datastructures import FullResultSet
+
+
# TODO: ...
class SQLCompiler(object):
def __init__(self, query, connection, using):
self.query = query
self.connection = connection
self.using = using
- def get_filters(self, where, correct=False):
+ def get_filters(self, where):
assert where.connector == "AND"
filters = {}
for child in where.children:
@@ -15,13 +18,15 @@ def get_filters(self, where, correct=False):
if k in filters:
v = {"$and": [filters[k], v]}
if where.negated:
- v = {"$not": v}
- filters[k] = v
+ filters.update(self.negate(k, v))
+ else:
+ filters[k] = v
else:
- field, val = self.make_atom(*child, **{"negated": where.negated})
- filters[field] = val
- if correct:
- self.correct_filters(filters)
+ try:
+ field, val = self.make_atom(*child, **{"negated": where.negated})
+ filters[field] = val
+ except FullResultSet:
+ pass
return filters
def make_atom(self, lhs, lookup_type, value_annotation, params_or_value, negated):
@@ -48,17 +53,10 @@ def make_atom(self, lhs, lookup_type, value_annotation, params_or_value, negated
val = {"$not": val}
return column, val
elif lookup_type == "lt":
+ if negated:
+ return {"$gte": params[0]}
return column, {"$lt": params[0]}
- def correct_filters(self, filters):
- for k, v in filters.items():
- if isinstance(v, dict) and v.keys() == ["$not"]:
- if isinstance(v["$not"], dict) and v["$not"].keys() == ["$and"]:
- del filters[k]
- or_vals = [self.negate(k, v) for v in v["$not"]["$and"]]
- assert "$or" not in filters
- filters["$or"] = or_vals
-
def negate(self, k, v):
if isinstance(v, dict):
if v.keys() == ["$not"]:
@@ -76,7 +74,7 @@ def build_query(self, aggregates=False):
assert self.query.high_mark is None
assert not self.query.order_by
- filters = self.get_filters(self.query.where, correct=True)
+ filters = self.get_filters(self.query.where)
return self.connection.db[self.query.model._meta.db_table].find(filters)
def results_iter(self):
@@ -1077,7 +1077,11 @@ def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
# it's short-circuited in the Where class.
# We also need to handle the case where a subquery is provided
entry = self.where_class()
- entry.add((Constraint(alias, col, None), 'isnull', True), AND)
+ entry.add((
+ Constraint(alias, col, None, eliminatable_if=lambda connection: not getattr(connection.features, "sql_nulls", True)),
+ 'isnull',
+ True
+ ), AND)
entry.negate()
self.where.add(entry, AND)
@@ -267,8 +267,9 @@ class Constraint(object):
An object that can be passed to WhereNode.add() and knows how to
pre-process itself prior to including in the WhereNode.
"""
- def __init__(self, alias, col, field):
+ def __init__(self, alias, col, field, eliminatable_if=None):
self.alias, self.col, self.field = alias, col, field
+ self.elimintable_if = eliminatable_if
def __getstate__(self):
"""Save the state of the Constraint for pickling.
@@ -321,6 +322,9 @@ def process(self, lookup_type, value, connection):
except ObjectDoesNotExist:
raise EmptyShortCircuit
+ if self.elimintable_if and self.elimintable_if(connection):
+ raise FullResultSet
+
return (self.alias, self.col, db_type), params
def relabel_aliases(self, change_map):
@@ -62,20 +62,8 @@ def test_not_equals(self):
q = Group.objects.create(name="Queen", year_formed=1971)
e = Group.objects.create(name="The E Street Band", year_formed=1972)
- qs = Group.objects.exclude(year_formed=1972)
- v = qs.query.get_compiler(qs.db).get_filters(qs.query.where, correct=True)
- self.assertEqual(v, {
- "$or": [
- {"year_formed": {"$ne": 1972}},
- {"year_formed": None},
- ]
- })
- # A bug in MongoDB prevents this query from actually working, but test
- # that we're at least generating the right query.
- return
-
self.assertQuerysetEqual(
- qs, [
+ Group.objects.exclude(year_formed=1972), [
"Queen",
],
lambda g: g.name,
@@ -105,4 +93,10 @@ def test_less_than(self):
[],
lambda g: g.name
)
-
+
+ self.assertQuerysetEqual(
+ Group.objects.exclude(year_formed__lt=1972), [
+ "The E Street Band"
+ ],
+ lambda g: g.name,
+ )

0 comments on commit 706b189

Please sign in to comment.