doableware / djongo

Django and MongoDB database connector
https://www.djongomapper.com
GNU Affero General Public License v3.0
1.88k stars 355 forks source link

How can I search Empty JSONField? #603

Open ghoul-ipg opened 2 years ago

ghoul-ipg commented 2 years ago

One line description of the issue

I have a Data JsonField that defaults to {}

from djongo import models
class Resume(models.Model):
    name = models.CharField(max_length=255)
    data = models.JSONField(default=dict)

I want to get all the resumes whoes data is {}

Python script

```python resume = Resume.objects.filter(data={}).all() ``` #### Traceback ``` Traceback (most recent call last): File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\query.py", line 857, in parse return handler(self, statement) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\query.py", line 933, in _select return SelectQuery(self.db, self.connection_properties, sm, self._params) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\query.py", line 116, in __init__ super().__init__(*args) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\query.py", line 62, in __init__ self.parse() File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\query.py", line 152, in parse self.where = WhereConverter(self, statement) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\converters.py", line 27, in __init__ self.parse() File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\converters.py", line 119, in parse self.op = WhereOp( File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\operators.py", line 475, in __init__ self._statement2ops() File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\operators.py", line 428, in _statement2ops op = self._token2op(tok, statement) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\operators.py", line 395, in _token2op op = CmpOp(tok, self.query) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\operators.py", line 517, in __init__ self._field_ext, self._constant = next(iter(self._constant.items())) StopIteration The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\cursor.py", line 51, in execute self.result = Query( File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\query.py", line 784, in __init__ self._query = self.parse() File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\sql2mongo\query.py", line 885, in parse raise exe from e djongo.exceptions.SQLDecodeError: Keyword: None Sub SQL: None FAILED SQL: SELECT "mm_resume"."id", "mm_resume"."name", "mm_resume"."data" FROM "mm_resume" WHERE "mm_resume"."data" = %(0)s ORDER BY "mm_resume"."id" ASC LIMIT 1 Params: ({},) Version: 1.3.6 The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\backends\utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\cursor.py", line 59, in execute raise db_exe from e djongo.database.DatabaseError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Users\EDZ\PycharmProjects\testmongo\mysite\manage.py", line 22, in main() File "C:\Users\EDZ\PycharmProjects\testmongo\mysite\manage.py", line 18, in main execute_from_command_line(sys.argv) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\core\management\__init__.py", line 425, in execute_from_command_line utility.execute() File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\core\management\__init__.py", line 419, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\core\management\base.py", line 373, in run_from_argv self.execute(*args, **cmd_options) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\core\management\base.py", line 417, in execute output = self.handle(*args, **options) File "C:\Users\EDZ\PycharmProjects\testmongo\mysite\mm\management\commands\aa.py", line 11, in handle re = Resume.objects.filter(data={}).first() File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\models\query.py", line 682, in first for obj in (self if self.ordered else self.order_by('pk'))[:1]: File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\models\query.py", line 280, in __iter__ self._fetch_all() File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\models\query.py", line 1354, in _fetch_all self._result_cache = list(self._iterable_class(self)) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\models\query.py", line 51, in __iter__ results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\models\sql\compiler.py", line 1202, in execute_sql cursor.execute(sql, params) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\backends\utils.py", line 99, in execute return super().execute(sql, params) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\backends\utils.py", line 67, in execute return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\backends\utils.py", line 76, in _execute_with_wrappers return executor(sql, params, many, context) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\backends\utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\utils.py", line 90, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\django\db\backends\utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "C:\Users\EDZ\PycharmProjects\testmongo\pvenv\lib\site-packages\djongo\cursor.py", line 59, in execute raise db_exe from e django.db.utils.DatabaseError ```
ghoul-ipg commented 2 years ago

I tried to modify CmpOp to implement my results, or do you have any other methods?


class CmpOp(_Op):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._identifier = SQLToken.token2sql(self.statement.left, self.query)
        self._is_empty = False
        if isinstance(self.statement.right, Identifier):
            raise SQLDecodeError('Join using WHERE not supported')

        self._operator = OPERATOR_MAP[self.statement.token_next(0)[1].value]
        index = re_index(self.statement.right.value)

        self._constant = self.params[index] if index is not None else None
        if isinstance(self._constant, dict):
            if not self._constant:
                self._field_ext = None
                self._is_empty = True
            else:
                self._field_ext, self._constant = next(iter(self._constant.items()))
        else:
            self._field_ext = None

    def negate(self):
        self.is_negated = True

    def evaluate(self):
        pass

    def to_mongo(self):
        field = self._identifier.field
        if self._field_ext:
            field += '.' + self._field_ext
        if self._is_empty:
            value = {}
        else:
            value = {self._operator: self._constant}
        if not self.is_negated:
            return {field: value}
        else:
            return {field: {'$not': value}}
MiltosD commented 1 year ago

What if you remove ".all()" ?