Skip to content

Commit d19cba9

Browse files
committed
[soc2010/query-refactor] Implemented not equal (exclude(foo=bar)) in the ORM for MongoDB, note that this doesn't actually work at the moment due to a bug in MongoDB.
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/query-refactor@13358 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 2804e22 commit d19cba9

File tree

3 files changed

+66
-9
lines changed

3 files changed

+66
-9
lines changed

django/contrib/mongodb/compiler.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,27 @@ def __init__(self, query, connection, using):
55
self.connection = connection
66
self.using = using
77

8-
def get_filters(self, where):
8+
def get_filters(self, where, correct=False):
99
assert where.connector == "AND"
10-
assert not where.negated
1110
filters = {}
1211
for child in where.children:
1312
if isinstance(child, self.query.where_class):
14-
# TODO: probably needs to check for dupe keys
15-
filters.update(self.get_filters(child))
13+
child_filters = self.get_filters(child)
14+
for k, v in child_filters.iteritems():
15+
if k in filters:
16+
v = {"$and": [filters[k], v]}
17+
if where.negated:
18+
v = {"$not": v}
19+
filters[k] = v
1620
else:
17-
field, val = self.make_atom(*child)
21+
field, val = self.make_atom(*child, **{"negated": where.negated})
1822
filters[field] = val
23+
if correct:
24+
self.correct_filters(filters)
1925
return filters
2026

21-
def make_atom(self, lhs, lookup_type, value_annotation, params_or_value):
22-
assert lookup_type == "exact"
27+
def make_atom(self, lhs, lookup_type, value_annotation, params_or_value, negated):
28+
assert lookup_type in ["exact", "isnull"], lookup_type
2329
if hasattr(lhs, "process"):
2430
lhs, params = lhs.process(lookup_type, params_or_value, self.connection)
2531
else:
@@ -30,7 +36,33 @@ def make_atom(self, lhs, lookup_type, value_annotation, params_or_value):
3036
assert table == self.query.model._meta.db_table
3137
if column == self.query.model._meta.pk.column:
3238
column = "_id"
33-
return column, params[0]
39+
40+
if lookup_type == "exact":
41+
val = params[0]
42+
if negated:
43+
val = {"$ne": val}
44+
return column, val
45+
elif lookup_type == "isnull":
46+
val = None
47+
if value_annotation == negated:
48+
val = {"$not": val}
49+
return column, val
50+
51+
def correct_filters(self, filters):
52+
for k, v in filters.items():
53+
if isinstance(v, dict) and v.keys() == ["$not"]:
54+
if isinstance(v["$not"], dict) and v["$not"].keys() == ["$and"]:
55+
del filters[k]
56+
or_vals = [self.negate(k, v) for v in v["$not"]["$and"]]
57+
assert "$or" not in filters
58+
filters["$or"] = or_vals
59+
60+
def negate(self, k, v):
61+
if isinstance(v, dict):
62+
if v.keys() == ["$not"]:
63+
return {k: v["$not"]}
64+
return {k: {"$not": v}}
65+
return {k: {"$ne": v}}
3466

3567
def build_query(self, aggregates=False):
3668
assert len([a for a in self.query.alias_map if self.query.alias_refcount[a]]) <= 1
@@ -42,7 +74,7 @@ def build_query(self, aggregates=False):
4274
assert self.query.high_mark is None
4375
assert not self.query.order_by
4476

45-
filters = self.get_filters(self.query.where)
77+
filters = self.get_filters(self.query.where, correct=True)
4678
return self.connection.db[self.query.model._meta.db_table].find(filters)
4779

4880
def results_iter(self):

tests/regressiontests/mongodb/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ def __unicode__(self):
1515
class Group(models.Model):
1616
id = models.NativeAutoField(primary_key=True)
1717
name = models.CharField(max_length=255)
18+
year_formed = models.IntegerField(null=True)
19+

tests/regressiontests/mongodb/tests.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,26 @@ def test_foreignkey(self):
5757
self.assertEqual(b.current_group_id, e.pk)
5858
self.assertFalse(hasattr(b, "_current_group_cache"))
5959
self.assertEqual(b.current_group, e)
60+
61+
def test_lookup(self):
62+
q = Group.objects.create(name="Queen", year_formed=1971)
63+
e = Group.objects.create(name="The E Street Band", year_formed=1972)
64+
65+
qs = Group.objects.exclude(year_formed=1972)
66+
v = qs.query.get_compiler(qs.db).get_filters(qs.query.where, correct=True)
67+
self.assertEqual(v, {
68+
"$or": [
69+
{"year_formed": {"$ne": 1972}},
70+
{"year_formed": None},
71+
]
72+
})
73+
# A bug in MongoDB prevents this query from actually working, but test
74+
# that we're at least generating the right query.
75+
return
76+
77+
self.assertQuerysetEqual(
78+
qs, [
79+
"Queen",
80+
],
81+
lambda g: g.name,
82+
)

0 commit comments

Comments
 (0)