Skip to content

Commit b10c6df

Browse files
committed
Implementation of KeyTransformIsNull, KeyTransformIn.
1 parent b11fb50 commit b10c6df

File tree

2 files changed

+39
-32
lines changed

2 files changed

+39
-32
lines changed

django_mongodb/features.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
99
supports_ignore_conflicts = False
1010
# Not implemented: https://github.com/mongodb-labs/django-mongodb/issues/8
1111
supports_json_field = True
12+
supports_json_field_contains = False
1213
has_native_json_field = True
1314
# BSON Date type doesn't support microsecond precision.
1415
supports_microsecond_precision = False
@@ -373,4 +374,9 @@ class DatabaseFeatures(BaseDatabaseFeatures):
373374
"model_fields.test_jsonfield.JSONFieldTests.test_invalid_value",
374375
"model_fields.test_jsonfield.JSONFieldTests.test_db_check_constraints",
375376
},
377+
"Mongodb's Null behaviour is different from sql's": {
378+
"model_fields.test_jsonfield.TestQuerying.test_none_key_exclude",
379+
"model_fields.test_jsonfield.TestQuerying.test_isnull_key",
380+
},
381+
"Pipeline filtering": {"model_fields.test_jsonfield.TestQuerying.test_icontains"},
376382
}

django_mongodb/fields/auto.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from bson import ObjectId, errors
22
from django.core import exceptions
3+
from django.db import NotSupportedError
34
from django.db.models import JSONField
45
from django.db.models.fields import AutoField, Field
56
from django.db.models.fields.json import (
@@ -11,6 +12,8 @@
1112
HasKeys,
1213
JSONExact,
1314
KeyTransform,
15+
KeyTransformIn,
16+
KeyTransformIsNull,
1417
)
1518
from django.utils.translation import gettext_lazy as _
1619

@@ -59,8 +62,14 @@ def from_db_value(self, value, expression, connection):
5962
)
6063

6164

62-
def process_rhs(node, compiler, connection):
65+
def json_process_rhs(node, compiler, connection):
6366
_, value = node.process_rhs(compiler, connection)
67+
68+
# Django's framework transform the [None] into a [null],
69+
# we have to revertit.
70+
if value == ["null"]:
71+
value = [None]
72+
6473
lookup_name = node.lookup_name
6574
if lookup_name not in ("in", "range"):
6675
value = value[0] if len(value) > 0 else []
@@ -78,41 +87,33 @@ def key_transform(self, compiler, connection):
7887
return ".".join([lhs_mql, *key_transforms])
7988

8089

81-
def data_contains(self, compiler, connection):
82-
lhs_mql = process_lhs(self, compiler, connection)
83-
value = process_rhs(self, compiler, connection)
84-
if not value:
85-
return {lhs_mql: {"$type": "array"}}
86-
if isinstance(value, list):
87-
return {"$and": [{lhs_mql: v} for v in value]}
88-
if isinstance(value, dict):
89-
return {"$and": [{f"{lhs_mql}.{k}": v} for k, v in value.items()]}
90-
return {lhs_mql: value}
90+
def data_contains(self, compiler, connection): # noqa: ARG001
91+
raise NotSupportedError("contains lookup is not supported on this database backend.")
92+
93+
94+
def contained_by(self, compiler, connection): # noqa: ARG001
95+
raise NotSupportedError("contained_by lookup is not supported on this database backend.")
9196

9297

9398
def json_exact(self, compiler, connection):
99+
rhs_mql = json_process_rhs(self, compiler, connection)
100+
lhs_mql = process_lhs(self, compiler, connection)
101+
return {lhs_mql: {"$eq": rhs_mql, "$exists": True}}
102+
103+
104+
def key_transform_isnull(self, compiler, connection):
94105
lhs_mql = process_lhs(self, compiler, connection)
95-
rhs_mql = process_rhs(self, compiler, connection)
96-
return {lhs_mql: {"$eq": rhs_mql}}
106+
rhs_mql = json_process_rhs(self, compiler, connection)
107+
if rhs_mql is False:
108+
return {lhs_mql: {"$neq": None}}
109+
return {"$or": [{lhs_mql: {"$eq": None}}, {lhs_mql: {"$exists": False}}]}
97110

98111

99-
def contained_by(self, compiler, connection):
112+
def key_transform_in(self, compiler, connection):
100113
lhs_mql = process_lhs(self, compiler, connection)
101-
value = process_rhs(self, compiler, connection)
102-
if isinstance(value, list):
103-
if not value:
104-
return {lhs_mql: []}
105-
return {"$and": [{"$or": [{lhs_mql: v}, {lhs_mql: {"$exists": False}}]} for v in value]}
106-
if isinstance(value, dict):
107-
if not value:
108-
return {lhs_mql: {}}
109-
return {
110-
"$and": [
111-
{"$or": [{f"{lhs_mql}.{k}": v}, {f"{lhs_mql}.{k}": {"$exists": False}}]}
112-
for k, v in value.items()
113-
]
114-
}
115-
return {lhs_mql: value}
114+
value = json_process_rhs(self, compiler, connection)
115+
rhs_mql = connection.operators[self.lookup_name](value)
116+
return {lhs_mql: rhs_mql}
116117

117118

118119
def has_key_lookup(self, compiler, connection):
@@ -124,9 +125,7 @@ def has_key_lookup(self, compiler, connection):
124125
for key in rhs:
125126
if isinstance(key, KeyTransform):
126127
*_, rhs_key_transforms = key.preprocess_lhs(compiler, connection)
127-
rhs_key_transforms = ".".join(
128-
rhs_key_transforms
129-
) # process_lhs(key, compiler, connection)
128+
rhs_key_transforms = ".".join(rhs_key_transforms)
130129
else:
131130
rhs_key_transforms = str(key)
132131
rhs_json_path = f"{lhs}.{rhs_key_transforms}"
@@ -151,3 +150,5 @@ def load_fields():
151150
HasAnyKeys.mongo_operator = "$or"
152151
HasKey.mongo_operator = None
153152
HasKeys.mongo_operator = "$and"
153+
KeyTransformIsNull.as_mql = key_transform_isnull
154+
KeyTransformIn.as_mql = key_transform_in

0 commit comments

Comments
 (0)