-
Notifications
You must be signed in to change notification settings - Fork 22
add support for JSONField #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
a661dac
to
f0f46f4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial comments.
9f29d68
to
e8d257a
Compare
617dc03
to
13f58ab
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't an exhaustive review but here are a few things to work on while I continue reviewing.
django_mongodb/features.py
Outdated
@@ -202,6 +204,9 @@ class DatabaseFeatures(BaseDatabaseFeatures): | |||
"annotations.tests.NonAggregateAnnotationTestCase.test_order_by_annotation", | |||
# annotate().filter().count() gives incorrect results. | |||
"db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_year_exact_lookup", | |||
"model_fields.test_jsonfield.TestQuerying.test_nested_key_transform_on_subquery", | |||
"model_fields.test_jsonfield.TestQuerying.test_ordering_grouping_by_count", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is "FieldDoesNotExist with ordering."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I meant is that there is already a section (a few lines above) with this name.
django_mongodb/features.py
Outdated
@@ -279,6 +284,10 @@ class DatabaseFeatures(BaseDatabaseFeatures): | |||
"update.tests.SimpleTest.test_empty_update_with_inheritance", | |||
"update.tests.SimpleTest.test_foreign_key_update_with_id", | |||
"update.tests.SimpleTest.test_nonempty_update_with_inheritance", | |||
"model_fields.test_jsonfield.TestQuerying.test_join_key_transform_annotation_expression", | |||
"model_fields.test_jsonfield.TestQuerying.test_order_grouping_custom_decoder", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these last three don't actually do joins but the logic in SQLCompiler._get_ordering()
is naive to the fact that __
is a key transform. You can defer this to a separate commit or PR, but I would put these skips in a separate section that describes the issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmh you are right, so for this one I think you should create a new PR. The if that check joins is wonky (only checks for __) I think it should be another short task. I will move those test to expected failures.
64e25e3
to
1395430
Compare
django_mongodb/features.py
Outdated
@@ -8,8 +8,9 @@ class DatabaseFeatures(BaseDatabaseFeatures): | |||
supports_date_lookup_using_string = False | |||
supports_foreign_keys = False | |||
supports_ignore_conflicts = False | |||
# Not implemented: https://github.com/mongodb-labs/django-mongodb/issues/8 | |||
supports_json_field = False | |||
supports_json_field = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this line since it's what it's the superclass.
django_mongodb/features.py
Outdated
@@ -202,6 +204,9 @@ class DatabaseFeatures(BaseDatabaseFeatures): | |||
"annotations.tests.NonAggregateAnnotationTestCase.test_order_by_annotation", | |||
# annotate().filter().count() gives incorrect results. | |||
"db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_year_exact_lookup", | |||
"model_fields.test_jsonfield.TestQuerying.test_nested_key_transform_on_subquery", | |||
"model_fields.test_jsonfield.TestQuerying.test_ordering_grouping_by_count", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I meant is that there is already a section (a few lines above) with this name.
django_mongodb/query_utils.py
Outdated
@@ -48,4 +48,6 @@ def process_rhs(node, compiler, connection): | |||
def regex_match(field, value, regex, *re_args, **re_kwargs): | |||
regex = re.compile(regex % re.escape(value), *re_args, **re_kwargs) | |||
options = "i" if regex.flags & re.I else "" | |||
return {"$regexMatch": {"input": field, "regex": regex.pattern, "options": options}} | |||
return { | |||
"$regexMatch": {"input": {"$toString": field}, "regex": regex.pattern, "options": options} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this change end up solving anything for JSONField? I reverted and model_fields.test_jsonfield
still passed. Maybe there's another affected test. Anyway, I'd make it a separate PR since it fixes other non-JSONField tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it doesn't. It solve only when a json field is a string, number, or null. But It does not pass any new test.
30a3188
to
c475a78
Compare
This is looking pretty good to me. I added a couple of commits. Two remaining concerns:
|
Yes, I can, I will add some comments there. There are two method that I patched because I wasn't able to find a way to handle them in process_rhs or process_lhs.
The behavior of condition = Q(value__foo="bax") To return less values than expected when using (I expect everything with: B or ~B, that is the question) NullableJSONModel.objects.filter(condition | ~condition) In SQL, this query could return unexpected results because Handling this behavior in MongoDB requires extra logic. In MongoDB, |
django_mongodb/fields/json.py
Outdated
for path in paths: | ||
keys.append(_has_key_predicate(path, lhs)) | ||
if self.mongo_operator is None: | ||
assert len(keys) == 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this mainly for debugging? Can we remove it now? We shouldn't rely on assert
for anything critical since asserts are removed when running with python -O
. If so, I'll make the update along with my next batch of language edits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, will remove. It was for debugging. Shall we also remove this assert: https://github.com/mongodb-labs/django-mongodb/blob/main/django_mongodb/base.py#L121?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made a brief attempt to refactor the connection code to conform to Django's normal API, but it wasn't successful. I may return to it later. For now I wouldn't bother with a commit just to remove that line.
Thanks for the documentation. I found some of it a little redundant, so I made it more concise, hopefully without losing any important info. I'll check this once more tomorrow, but I think we're close to a merge. 👍 |
7aa1ec0
to
e509877
Compare
result = { | ||
"$and": [ | ||
# The path must exist (i.e. not be "missing"). | ||
{"$ne": [{"$type": path}, "missing"]}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what case would the path return missing? Wouldn't it always return null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the argument is a field that is missing in the input document, $type returns the string "missing".
@@ -49,6 +51,12 @@ def adapt_decimalfield_value(self, value, max_digits=None, decimal_places=None): | |||
return None | |||
return Decimal128(value) | |||
|
|||
def adapt_json_value(self, value, encoder): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When is this method called and what's the purpose? I ask because json.loads+json.dumps
is expensive and ends up with the same value. Also it won't work with pymongo's extended JSON types like ObjectId, Code, Binary, etc...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. We can skip it unless the user has specified a custom encoder
.
It's called before saving a value to the database. Normally it's just json.dumps()
on databases that don't have a json data type.
Co-authored-by: Tim Graham <[email protected]>
226e9f2
to
9019a33
Compare
fixes #8