Skip to content
This repository was archived by the owner on Dec 1, 2025. It is now read-only.

Conversation

@wojcikstefan
Copy link
Member

The test suite currently fails for this version of MongoDB even though this version of MongoEngine is confirmed to run well with it. Need to look into the failures more closely before shipping this.

@wojcikstefan
Copy link
Member Author

wojcikstefan commented Jan 14, 2025

Summary of test failures:

  1. 16 tests fail currently. That is despite this version of MongoDB being proven to run well with this version of MongoEngine.
  2. The failures are likely related to functionality that's never used and thus can be removed.
  3. There are only a few higher-level categories of failures:
    • Some tests expect a different output from the DB, which is no longer valid on MongoDB v5.
    • Some tests work with $eval, which doesn't seem like valid input anymore.
    • Some tests work with map reduce (either directly or indirectly via e.g. QuerySet.item_frequencies), which uses map_reduce under the hood, which is no longer valid or has changed its input formatting.
    • Some tests use $pushAll, which either isn't valid anymore or has changed the expected input format.
FAILED tests/document/test_instance.py::InstanceTest::test_reload_sharded - KeyError: 'query'
FAILED tests/document/test_instance.py::InstanceTest::test_update_shard_key_routing - KeyError: 'query'
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_exec_js_field_sub - pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_exec_js_query - pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_item_frequencies - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_item_frequencies_normalize - pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_item_frequencies_null_values - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_item_frequencies_on_embedded - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_item_frequencies_with_0_values - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_item_frequencies_with_False_values - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_item_frequencies_with_null_embedded - pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_map_reduce - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_map_reduce_finalize - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_map_reduce_with_custom_object_ids - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_update - mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_update_push_and_pull_add_to_set - mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})
More detailed test failures ``` ============================= test session starts ============================== platform linux -- Python 3.10.1, pytest-8.3.4, pluggy-1.5.0 rootdir: /home/circleci/code collected 490 items

tests/document/test_class_methods.py ........... [ 2%]
tests/document/test_delta.py ....ssss [ 3%]
tests/document/test_dynamic.py sssssssssss [ 6%]
tests/document/test_indexes.py ........................s... [ 11%]
tests/document/test_inheritance.py ................ [ 15%]
tests/document/test_instance.py ..s............s.s.........ss......sss.s [ 23%]
....ss.s......F..................s.s...ssss...sF. [ 33%]
tests/document/test_json_serialisation.py .. [ 33%]
tests/document/test_validation.py ...... [ 34%]
tests/fields/test_fields.py ....s.................s.......s............. [ 43%]
.s..s......ss............ssssss........ [ 51%]
tests/fields/test_file.py ssssssssssssssssss [ 55%]
tests/fields/test_geo.py ssssssssss [ 57%]
tests/queryset/test_field_list.py ................. [ 61%]
tests/queryset/test_geo.py sssssss [ 62%]
tests/queryset/test_queryset.py ......................................FF [ 70%]
............FFFFFFF..FFF..s........................................F..F. [ 85%]
.......... [ 87%]
tests/queryset/test_transform.py ..s... [ 88%]
tests/queryset/test_visitor.py ............. [ 91%]
tests/test_all_warnings.py . [ 91%]
tests/test_benchmark.py .. [ 91%]
tests/test_connection.py ..... [ 92%]
tests/test_context_managers.py ...... [ 94%]
tests/test_dereference.py ...sss.ss.s.sss.....ss..s. [ 99%]
tests/test_signals.py ... [100%]

=================================== FAILURES ===================================
_______________________ InstanceTest.test_reload_sharded _______________________

self = <tests.document.test_instance.InstanceTest testMethod=test_reload_sharded>

def test_reload_sharded(self):
    class Animal(Document):
        superphylum = StringField()
        meta = {'shard_key': ('superphylum',)}

    Animal.drop_collection()
    doc = Animal(superphylum='Deuterostomia')
    doc.save()

    with query_counter() as q:
        doc.reload()
        query_op = q.db.system.profile.find_one({
            'ns': 'mongoenginetest.animal'
        })
        self.assertEqual(
          set(query_op['query']['filter'].keys()),
            set(['_id', 'superphylum'])
        )

E KeyError: 'query'

tests/document/test_instance.py:386: KeyError
__________________ InstanceTest.test_update_shard_key_routing __________________

self = <tests.document.test_instance.InstanceTest testMethod=test_update_shard_key_routing>

def test_update_shard_key_routing(self):
    """Ensures updating a doc with a specified shard_key includes it in
    the query.
    """
    class Animal(Document):
        is_mammal = BooleanField()
        name = StringField()
        meta = {'shard_key': ('is_mammal', 'id')}

    Animal.drop_collection()
    doc = Animal(is_mammal=True, name='Dog')
    doc.save()

    with query_counter() as q:
        doc.name = 'Cat'
        doc.save()
        query_op = q.db.system.profile.find({ 'ns': 'mongoenginetest.animal' })[0]
        self.assertEqual(query_op['op'], 'update')
      self.assertEqual(set(query_op['query'].keys()), set(['_id', 'is_mammal']))

E KeyError: 'query'

tests/document/test_instance.py:410: KeyError
_____________________ QuerySetTest.test_exec_js_field_sub ______________________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_exec_js_field_sub>

def test_exec_js_field_sub(self):
    """Ensure that field substitutions occur properly in exec_js functions.
    """
    class Comment(EmbeddedDocument):
        content = StringField(db_field='body')

    class BlogPost(Document):
        name = StringField(db_field='doc-name')
        comments = ListField(EmbeddedDocumentField(Comment),
                             db_field='cmnts')

    BlogPost.drop_collection()

    comments1 = [Comment(content='cool'), Comment(content='yay')]
    post1 = BlogPost(name='post1', comments=comments1)
    post1.save()

    comments2 = [Comment(content='nice stuff')]
    post2 = BlogPost(name='post2', comments=comments2)
    post2.save()

    code = """
    function getComments() {
        var comments = [];
        db[collection].find(query).forEach(function(doc) {
            var docComments = doc[~comments];
            for (var i = 0; i < docComments.length; i++) {
                comments.push({
                    'document': doc[~name],
                    'comment': doc[~comments][i][~comments.content]
                });
            }
        });
        return comments;
    }
    """

    sub_code = BlogPost.objects._sub_js_fields(code)
    code_chunks = ['doc["cmnts"];', 'doc["doc-name"],',
                   'doc["cmnts"][i]["body"]']
    for chunk in code_chunks:
        self.assertTrue(chunk in sub_code)
  results = BlogPost.objects.exec_js(code)

tests/queryset/test_queryset.py:1132:


mongoengine/queryset/queryset.py:1170: in exec_js
return db.eval(code, *fields)
../.local/lib/python3.10/site-packages/pymongo/database.py:1646: in eval
result = self.command("$eval", code, args=args)
../.local/lib/python3.10/site-packages/pymongo/database.py:759: in command
return self._command(sock_info, command, secondary_ok, value,
../.local/lib/python3.10/site-packages/pymongo/database.py:641: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 59, 'codeName': 'CommandNotFound', 'errmsg': "no such command: '$eval'", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None
parse_write_concern_error = False

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
_______________________ QuerySetTest.test_exec_js_query ________________________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_exec_js_query>

def test_exec_js_query(self):
    """Ensure that queries are properly formed for use in exec_js.
    """
    class BlogPost(Document):
        hits = IntField()
        published = BooleanField()

    BlogPost.drop_collection()

    post1 = BlogPost(hits=1, published=False)
    post1.save()

    post2 = BlogPost(hits=1, published=True)
    post2.save()

    post3 = BlogPost(hits=1, published=True)
    post3.save()

    js_func = """
        function(hitsField) {
            var count = 0;
            db[collection].find(query).forEach(function(doc) {
                count += doc[hitsField];
            });
            return count;
        }
    """

    # Ensure that normal queries work
  c = BlogPost.objects(published=True).exec_js(js_func, 'hits')

tests/queryset/test_queryset.py:1081:


mongoengine/queryset/queryset.py:1170: in exec_js
return db.eval(code, *fields)
../.local/lib/python3.10/site-packages/pymongo/database.py:1646: in eval
result = self.command("$eval", code, args=args)
../.local/lib/python3.10/site-packages/pymongo/database.py:759: in command
return self._command(sock_info, command, secondary_ok, value,
../.local/lib/python3.10/site-packages/pymongo/database.py:641: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 59, 'codeName': 'CommandNotFound', 'errmsg': "no such command: '$eval'", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None
parse_write_concern_error = False

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
______________________ QuerySetTest.test_item_frequencies ______________________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_item_frequencies>

def test_item_frequencies(self):
    """Ensure that item frequencies are properly generated from lists.
    """
    class BlogPost(Document):
        hits = IntField()
        tags = ListField(StringField(), db_field='blogTags')

    BlogPost.drop_collection()

    BlogPost(hits=1, tags=['music', 'film', 'actors', 'watch']).save()
    BlogPost(hits=2, tags=['music', 'watch']).save()
    BlogPost(hits=2, tags=['music', 'actors']).save()

    def test_assertions(f):
        f = dict((key, int(val)) for key, val in list(f.items()))
        self.assertEqual(set(['music', 'film', 'actors', 'watch']), set(f.keys()))
        self.assertEqual(f['music'], 3)
        self.assertEqual(f['actors'], 2)
        self.assertEqual(f['watch'], 2)
        self.assertEqual(f['film'], 1)
  exec_js = BlogPost.objects.item_frequencies('tags')

tests/queryset/test_queryset.py:1951:


mongoengine/queryset/queryset.py:1238: in item_frequencies
return self._item_frequencies_map_reduce(field,
mongoengine/queryset/queryset.py:1408: in _item_frequencies_map_reduce
for f in values:
mongoengine/queryset/queryset.py:1115: in map_reduce
results = getattr(queryset._collection, map_reduce_function)(
../.local/lib/python3.10/site-packages/pymongo/collection.py:2941: in inline_map_reduce
res = self._map_reduce(map, reduce, {"inline": 1}, session,
../.local/lib/python3.10/site-packages/pymongo/collection.py:2830: in _map_reduce
return self._command(
../.local/lib/python3.10/site-packages/pymongo/collection.py:238: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 2, 'codeName': 'BadValue', 'errmsg': "'map' must be of string or code type", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
_________________ QuerySetTest.test_item_frequencies_normalize _________________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_item_frequencies_normalize>

def test_item_frequencies_normalize(self):
    class Test(Document):
        val = IntField()

    Test.drop_collection()

    for i in range(50):
        Test(val=1).save()

    for i in range(20):
        Test(val=2).save()
  freqs = Test.objects.item_frequencies('val', map_reduce=False, normalize=True)

tests/queryset/test_queryset.py:2140:


mongoengine/queryset/queryset.py:1240: in item_frequencies
return self._item_frequencies_exec_js(field, normalize=normalize)
mongoengine/queryset/queryset.py:1469: in _item_frequencies_exec_js
total, data, types = self.exec_js(freq_func, field)
mongoengine/queryset/queryset.py:1170: in exec_js
return db.eval(code, *fields)
../.local/lib/python3.10/site-packages/pymongo/database.py:1646: in eval
result = self.command("$eval", code, args=args)
../.local/lib/python3.10/site-packages/pymongo/database.py:759: in command
return self._command(sock_info, command, secondary_ok, value,
../.local/lib/python3.10/site-packages/pymongo/database.py:641: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 59, 'codeName': 'CommandNotFound', 'errmsg': "no such command: '$eval'", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None
parse_write_concern_error = False

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
________________ QuerySetTest.test_item_frequencies_null_values ________________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_item_frequencies_null_values>

def test_item_frequencies_null_values(self):

    class Person(Document):
        name = StringField()
        city = StringField()

    Person.drop_collection()

    Person(name="Wilson Snr", city="CRB").save()
    Person(name="Wilson Jr").save()
  freq = Person.objects.item_frequencies('city')

tests/queryset/test_queryset.py:2062:


mongoengine/queryset/queryset.py:1238: in item_frequencies
return self._item_frequencies_map_reduce(field,
mongoengine/queryset/queryset.py:1408: in _item_frequencies_map_reduce
for f in values:
mongoengine/queryset/queryset.py:1115: in map_reduce
results = getattr(queryset._collection, map_reduce_function)(
../.local/lib/python3.10/site-packages/pymongo/collection.py:2941: in inline_map_reduce
res = self._map_reduce(map, reduce, {"inline": 1}, session,
../.local/lib/python3.10/site-packages/pymongo/collection.py:2830: in _map_reduce
return self._command(
../.local/lib/python3.10/site-packages/pymongo/collection.py:238: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 2, 'codeName': 'BadValue', 'errmsg': "'map' must be of string or code type", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
________________ QuerySetTest.test_item_frequencies_on_embedded ________________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_item_frequencies_on_embedded>

def test_item_frequencies_on_embedded(self):
    """Ensure that item frequencies are properly generated from lists.
    """

    class Phone(EmbeddedDocument):
        number = StringField()

    class Person(Document):
        name = StringField()
        phone = EmbeddedDocumentField(Phone)

    Person.drop_collection()

    doc = Person(name="Guido")
    doc.phone = Phone(number='62-3331-1656')
    doc.save()

    doc = Person(name="Marr")
    doc.phone = Phone(number='62-3331-1656')
    doc.save()

    doc = Person(name="WP Junior")
    doc.phone = Phone(number='62-3332-1656')
    doc.save()

    def test_assertions(f):
        f = dict((key, int(val)) for key, val in list(f.items()))
        self.assertEqual(set(['62-3331-1656', '62-3332-1656']), set(f.keys()))
        self.assertEqual(f['62-3331-1656'], 2)
        self.assertEqual(f['62-3332-1656'], 1)
  exec_js = Person.objects.item_frequencies('phone.number')

tests/queryset/test_queryset.py:2025:


mongoengine/queryset/queryset.py:1238: in item_frequencies
return self._item_frequencies_map_reduce(field,
mongoengine/queryset/queryset.py:1408: in _item_frequencies_map_reduce
for f in values:
mongoengine/queryset/queryset.py:1115: in map_reduce
results = getattr(queryset._collection, map_reduce_function)(
../.local/lib/python3.10/site-packages/pymongo/collection.py:2941: in inline_map_reduce
res = self._map_reduce(map, reduce, {"inline": 1}, session,
../.local/lib/python3.10/site-packages/pymongo/collection.py:2830: in _map_reduce
return self._command(
../.local/lib/python3.10/site-packages/pymongo/collection.py:238: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 2, 'codeName': 'BadValue', 'errmsg': "'map' must be of string or code type", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
_______________ QuerySetTest.test_item_frequencies_with_0_values _______________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_item_frequencies_with_0_values>

def test_item_frequencies_with_0_values(self):
    class Test(Document):
        val = IntField()

    Test.drop_collection()
    t = Test()
    t.val = 0
    t.save()
  ot = Test.objects.item_frequencies('val', map_reduce=True)

tests/queryset/test_queryset.py:2109:


mongoengine/queryset/queryset.py:1238: in item_frequencies
return self._item_frequencies_map_reduce(field,
mongoengine/queryset/queryset.py:1408: in _item_frequencies_map_reduce
for f in values:
mongoengine/queryset/queryset.py:1115: in map_reduce
results = getattr(queryset._collection, map_reduce_function)(
../.local/lib/python3.10/site-packages/pymongo/collection.py:2941: in inline_map_reduce
res = self._map_reduce(map, reduce, {"inline": 1}, session,
../.local/lib/python3.10/site-packages/pymongo/collection.py:2830: in _map_reduce
return self._command(
../.local/lib/python3.10/site-packages/pymongo/collection.py:238: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 2, 'codeName': 'BadValue', 'errmsg': "'map' must be of string or code type", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
_____________ QuerySetTest.test_item_frequencies_with_False_values _____________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_item_frequencies_with_False_values>

def test_item_frequencies_with_False_values(self):
    class Test(Document):
        val = BooleanField()

    Test.drop_collection()
    t = Test()
    t.val = False
    t.save()
  ot = Test.objects.item_frequencies('val', map_reduce=True)

tests/queryset/test_queryset.py:2123:


mongoengine/queryset/queryset.py:1238: in item_frequencies
return self._item_frequencies_map_reduce(field,
mongoengine/queryset/queryset.py:1408: in _item_frequencies_map_reduce
for f in values:
mongoengine/queryset/queryset.py:1115: in map_reduce
results = getattr(queryset._collection, map_reduce_function)(
../.local/lib/python3.10/site-packages/pymongo/collection.py:2941: in inline_map_reduce
res = self._map_reduce(map, reduce, {"inline": 1}, session,
../.local/lib/python3.10/site-packages/pymongo/collection.py:2830: in _map_reduce
return self._command(
../.local/lib/python3.10/site-packages/pymongo/collection.py:238: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 2, 'codeName': 'BadValue', 'errmsg': "'map' must be of string or code type", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
____________ QuerySetTest.test_item_frequencies_with_null_embedded _____________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_item_frequencies_with_null_embedded>

def test_item_frequencies_with_null_embedded(self):
    class Data(EmbeddedDocument):
        name = StringField()

    class Extra(EmbeddedDocument):
        tag = StringField()

    class Person(Document):
        data = EmbeddedDocumentField(Data, required=True)
        extra = EmbeddedDocumentField(Extra)

    Person.drop_collection()

    p = Person()
    p.data = Data(name="Wilson Jr")
    p.save()

    p = Person()
    p.data = Data(name="Wesley")
    p.extra = Extra(tag="friend")
    p.save()
  ot = Person.objects.item_frequencies('extra.tag', map_reduce=False)

tests/queryset/test_queryset.py:2094:


mongoengine/queryset/queryset.py:1240: in item_frequencies
return self._item_frequencies_exec_js(field, normalize=normalize)
mongoengine/queryset/queryset.py:1469: in _item_frequencies_exec_js
total, data, types = self.exec_js(freq_func, field)
mongoengine/queryset/queryset.py:1170: in exec_js
return db.eval(code, *fields)
../.local/lib/python3.10/site-packages/pymongo/database.py:1646: in eval
result = self.command("$eval", code, args=args)
../.local/lib/python3.10/site-packages/pymongo/database.py:759: in command
return self._command(sock_info, command, secondary_ok, value,
../.local/lib/python3.10/site-packages/pymongo/database.py:641: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 59, 'codeName': 'CommandNotFound', 'errmsg': "no such command: '$eval'", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None
parse_write_concern_error = False

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
_________________________ QuerySetTest.test_map_reduce _________________________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_map_reduce>

def test_map_reduce(self):
    """Ensure map/reduce is both mapping and reducing.
    """
    class BlogPost(Document):
        title = StringField()
        tags = ListField(StringField(), db_field='post-tag-list')

    BlogPost.drop_collection()

    BlogPost(title="Post #1", tags=['music', 'film', 'print']).save()
    BlogPost(title="Post #2", tags=['music', 'film']).save()
    BlogPost(title="Post #3", tags=['film', 'photography']).save()

    map_f = """
        function() {
            this[~tags].forEach(function(tag) {
                emit(tag, 1);
            });
        }
    """

    reduce_f = """
        function(key, values) {
            var total = 0;
            for(var i=0; i<values.length; i++) {
                total += values[i];
            }
            return total;
        }
    """

    # run a map/reduce operation spanning all posts
    results = BlogPost.objects.map_reduce(map_f, reduce_f, "myresults")
  results = list(results)

tests/queryset/test_queryset.py:1758:


mongoengine/queryset/queryset.py:1115: in map_reduce
results = getattr(queryset._collection, map_reduce_function)(
../.local/lib/python3.10/site-packages/pymongo/collection.py:2896: in map_reduce
response = self._map_reduce(map, reduce, out, session,
../.local/lib/python3.10/site-packages/pymongo/collection.py:2830: in _map_reduce
return self._command(
../.local/lib/python3.10/site-packages/pymongo/collection.py:238: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 2, 'codeName': 'BadValue', 'errmsg': "'map' must be of string or code type", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
____________________ QuerySetTest.test_map_reduce_finalize _____________________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_map_reduce_finalize>

def test_map_reduce_finalize(self):
    """Ensure that map, reduce, and finalize run and introduce "scope"
    by simulating "hotness" ranking with Reddit algorithm.
    """
    from time import mktime

    class Link(Document):
        title = StringField(db_field='bpTitle')
        up_votes = IntField()
        down_votes = IntField()
        submitted = DateTimeField(db_field='sTime')

    Link.drop_collection()

    now = datetime.utcnow()

    # Note: Test data taken from a custom Reddit homepage on
    # Fri, 12 Feb 2010 14:36:00 -0600. Link ordering should
    # reflect order of insertion below, but is not influenced
    # by insertion order.
    Link(title = "Google Buzz auto-followed a woman's abusive ex ...",
         up_votes = 1079,
         down_votes = 553,
         submitted = now-timedelta(hours=4)).save()
    Link(title = "We did it! Barbie is a computer engineer.",
         up_votes = 481,
         down_votes = 124,
         submitted = now-timedelta(hours=2)).save()
    Link(title = "This Is A Mosquito Getting Killed By A Laser",
         up_votes = 1446,
         down_votes = 530,
         submitted=now-timedelta(hours=13)).save()
    Link(title = "Arabic flashcards land physics student in jail.",
         up_votes = 215,
         down_votes = 105,
         submitted = now-timedelta(hours=6)).save()
    Link(title = "The Burger Lab: Presenting, the Flood Burger",
         up_votes = 48,
         down_votes = 17,
         submitted = now-timedelta(hours=5)).save()
    Link(title="How to see polarization with the naked eye",
         up_votes = 74,
         down_votes = 13,
         submitted = now-timedelta(hours=10)).save()

    map_f = """
        function() {
            emit(this[~id], {up_delta: this[~up_votes] - this[~down_votes],
                            sub_date: this[~submitted].getTime() / 1000})
        }
    """

    reduce_f = """
        function(key, values) {
            data = values[0];

            x = data.up_delta;

            // calculate time diff between reddit epoch and submission
            sec_since_epoch = data.sub_date - reddit_epoch;

            // calculate 'Y'
            if(x > 0) {
                y = 1;
            } else if (x = 0) {
                y = 0;
            } else {
                y = -1;
            }

            // calculate 'Z', the maximal value
            if(Math.abs(x) >= 1) {
                z = Math.abs(x);
            } else {
                z = 1;
            }

            return {x: x, y: y, z: z, t_s: sec_since_epoch};
        }
    """

    finalize_f = """
        function(key, value) {
            // f(sec_since_epoch,y,z) =
            //                    log10(z) + ((y*sec_since_epoch) / 45000)
            z_10 = Math.log(value.z) / Math.log(10);
            weight = z_10 + ((value.y * value.t_s) / 45000);
            return weight;
        }
    """

    # provide the reddit epoch (used for ranking) as a variable available
    # to all phases of the map/reduce operation: map, reduce, and finalize.
    reddit_epoch = mktime(datetime(2005, 12, 8, 7, 46, 43).timetuple())
    scope = {'reddit_epoch': reddit_epoch}

    # run a map/reduce operation across all links. ordering is set
    # to "-value", which orders the "weight" value returned from
    # "finalize_f" in descending order.
    results = Link.objects.order_by("-value")
    results = results.map_reduce(map_f,
                                 reduce_f,
                                 "myresults",
                                 finalize_f=finalize_f,
                                 scope=scope)
  results = list(results)

tests/queryset/test_queryset.py:1920:


mongoengine/queryset/queryset.py:1115: in map_reduce
results = getattr(queryset._collection, map_reduce_function)(
../.local/lib/python3.10/site-packages/pymongo/collection.py:2896: in map_reduce
response = self._map_reduce(map, reduce, out, session,
../.local/lib/python3.10/site-packages/pymongo/collection.py:2830: in _map_reduce
return self._command(
../.local/lib/python3.10/site-packages/pymongo/collection.py:238: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 2, 'codeName': 'BadValue', 'errmsg': "'map' must be of string or code type", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
_____________ QuerySetTest.test_map_reduce_with_custom_object_ids ______________

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_map_reduce_with_custom_object_ids>

def test_map_reduce_with_custom_object_ids(self):
    """Ensure that QuerySet.map_reduce works properly with custom
    primary keys.
    """

    class BlogPost(Document):
        title = StringField(primary_key=True)
        tags = ListField(StringField())

    post1 = BlogPost(title="Post #1", tags=["mongodb", "mongoengine"])
    post2 = BlogPost(title="Post #2", tags=["django", "mongodb"])
    post3 = BlogPost(title="Post #3", tags=["hitchcock films"])

    post1.save()
    post2.save()
    post3.save()

    self.assertEqual(BlogPost._fields['title'].db_field, '_id')
    self.assertEqual(BlogPost._meta['id_field'], 'title')

    map_f = """
        function() {
            emit(this._id, 1);
        }
    """

    # reduce to a list of tag ids and counts
    reduce_f = """
        function(key, values) {
            var total = 0;
            for(var i=0; i<values.length; i++) {
                total += values[i];
            }
            return total;
        }
    """

    results = BlogPost.objects.map_reduce(map_f, reduce_f, "myresults")
  results = list(results)

tests/queryset/test_queryset.py:1807:


mongoengine/queryset/queryset.py:1115: in map_reduce
results = getattr(queryset._collection, map_reduce_function)(
../.local/lib/python3.10/site-packages/pymongo/collection.py:2896: in map_reduce
response = self._map_reduce(map, reduce, out, session,
../.local/lib/python3.10/site-packages/pymongo/collection.py:2830: in _map_reduce
return self._command(
../.local/lib/python3.10/site-packages/pymongo/collection.py:238: in _command
return sock_info.command(
../.local/lib/python3.10/site-packages/pymongo/pool.py:710: in command
return command(self, dbname, spec, secondary_ok,
../.local/lib/python3.10/site-packages/pymongo/network.py:161: in command
helpers._check_command_response(


response = {'code': 2, 'codeName': 'BadValue', 'errmsg': "'map' must be of string or code type", 'ok': 0.0}
max_wire_version = 13, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(response, max_wire_version,
                            allowable_errors=None,
                            parse_write_concern_error=False):
    """Check the response to a command for errors.
    """
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(response.get("$err"),
                               response.get("code"),
                               response,
                               max_wire_version)

    if parse_write_concern_error and 'writeConcernError' in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({'errorLabels': _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in itervalues(response["raw"]):
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_MASTER_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
  raise OperationFailure(errmsg, code, response, max_wire_version)

E pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:167: OperationFailure
___________________________ QuerySetTest.test_update ___________________________

self = [<BlogPost: BlogPost object>], upsert = False, multi = False
write_concern = {}, read_concern = None
update = {'$pushAll': {'tags': ['db', 'nosql']}}
queryset = [<BlogPost: BlogPost object>], query = {}
collection = Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=True, connect=True, read_preference=Primary()), 'mongoenginetest'), 'blog_post')
update_func = <bound method Collection.update_one of Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=True, connect=True, read_preference=Primary()), 'mongoenginetest'), 'blog_post')>

def update(
    self, upsert=False, multi=True, write_concern=None, read_concern=None, **update
):
    """Perform an atomic update on the fields matched by the query.

    :param upsert: Any existing document with that "_id" is overwritten.
    :param multi: Update multiple documents.
    :param write_concern: Write concern of this operation.
    :param read_concern: Override the read concern for the operation
    :param update: Django-style update keyword arguments

    .. versionadded:: 0.2
    """
    if not update and not upsert:
        raise OperationError("No update parameters, would remove data")

    if write_concern is None:
        write_concern = {}

    queryset = self.clone()
    query = queryset._query
    update = transform.update(queryset._document, **update)

    # If doing an atomic upsert on an inheritable class
    # then ensure we add _cls to the update operation
    if upsert and '_cls' in query:
        if '$set' in update:
            update["$set"]["_cls"] = queryset._document._class_name
        else:
            update["$set"] = {"_cls": queryset._document._class_name}
    try:
        with set_read_write_concern(
            queryset._collection, write_concern, read_concern
        ) as collection:
            update_func = collection.update_one
            if multi:
                update_func = collection.update_many
          result = update_func(query, update, upsert=upsert)

mongoengine/queryset/queryset.py:527:


../.local/lib/python3.10/site-packages/pymongo/collection.py:1028: in update_one
self._update_retryable(
../.local/lib/python3.10/site-packages/pymongo/collection.py:877: in _update_retryable
return self.__database.client._retryable_write(
../.local/lib/python3.10/site-packages/pymongo/mongo_client.py:1552: in _retryable_write
return self._retry_with_session(retryable, func, s, None)
../.local/lib/python3.10/site-packages/pymongo/mongo_client.py:1438: in _retry_with_session
return self._retry_internal(retryable, func, session, bulk)
../.local/lib/python3.10/site-packages/pymongo/mongo_client.py:1470: in _retry_internal
return func(session, sock_info, retryable)
../.local/lib/python3.10/site-packages/pymongo/collection.py:869: in _update
return self._update(
../.local/lib/python3.10/site-packages/pymongo/collection.py:846: in _update
_check_write_command_response(result)
../.local/lib/python3.10/site-packages/pymongo/helpers.py:241: in _check_write_command_response
_raise_last_write_error(write_errors)


write_errors = [{'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array', 'index': 0}]

def _raise_last_write_error(write_errors):
    # If the last batch had multiple errors only report
    # the last error to emulate continue_on_error.
    error = write_errors[-1]
    if error.get("code") == 11000:
        raise DuplicateKeyError(error.get("errmsg"), 11000, error)
  raise WriteError(error.get("errmsg"), error.get("code"), error)

E pymongo.errors.WriteError: Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:211: WriteError

During handling of the above exception, another exception occurred:

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_update>

def test_update(self):
    """Ensure that atomic updates work properly.
    """
    class BlogPost(Document):
        title = StringField()
        hits = IntField()
        tags = ListField(StringField())

    BlogPost.drop_collection()

    post = BlogPost(name="Test Post", hits=5, tags=['test'])
    post.save()

    BlogPost.objects.update(set__hits=10)
    post.reload()
    self.assertEqual(post.hits, 10)

    BlogPost.objects.update_one(inc__hits=1)
    post.reload()
    self.assertEqual(post.hits, 11)

    BlogPost.objects.update_one(dec__hits=1)
    post.reload()
    self.assertEqual(post.hits, 10)

    BlogPost.objects.update(push__tags='mongo')
    post.reload()
    self.assertTrue('mongo' in post.tags)
  BlogPost.objects.update_one(push_all__tags=['db', 'nosql'])

tests/queryset/test_queryset.py:1403:


mongoengine/queryset/queryset.py:552: in update_one
return self.update(


self = [<BlogPost: BlogPost object>], upsert = False, multi = False
write_concern = {}, read_concern = None
update = {'$pushAll': {'tags': ['db', 'nosql']}}
queryset = [<BlogPost: BlogPost object>], query = {}
collection = Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=True, connect=True, read_preference=Primary()), 'mongoenginetest'), 'blog_post')
update_func = <bound method Collection.update_one of Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=True, connect=True, read_preference=Primary()), 'mongoenginetest'), 'blog_post')>

def update(
    self, upsert=False, multi=True, write_concern=None, read_concern=None, **update
):
    """Perform an atomic update on the fields matched by the query.

    :param upsert: Any existing document with that "_id" is overwritten.
    :param multi: Update multiple documents.
    :param write_concern: Write concern of this operation.
    :param read_concern: Override the read concern for the operation
    :param update: Django-style update keyword arguments

    .. versionadded:: 0.2
    """
    if not update and not upsert:
        raise OperationError("No update parameters, would remove data")

    if write_concern is None:
        write_concern = {}

    queryset = self.clone()
    query = queryset._query
    update = transform.update(queryset._document, **update)

    # If doing an atomic upsert on an inheritable class
    # then ensure we add _cls to the update operation
    if upsert and '_cls' in query:
        if '$set' in update:
            update["$set"]["_cls"] = queryset._document._class_name
        else:
            update["$set"] = {"_cls": queryset._document._class_name}
    try:
        with set_read_write_concern(
            queryset._collection, write_concern, read_concern
        ) as collection:
            update_func = collection.update_one
            if multi:
                update_func = collection.update_many
            result = update_func(query, update, upsert=upsert)
        if result.raw_result:
            return result.raw_result['n']
    except pymongo.errors.DuplicateKeyError as err:
        raise NotUniqueError('Update failed (%s)' % str(err))
    except pymongo.errors.OperationFailure as err:
        if str(err) == 'multi not coded yet':
            message = 'update() method requires MongoDB 1.1.3+'
            raise OperationError(message)
      raise OperationError('Update failed (%s)' % str(err))

E mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})

mongoengine/queryset/queryset.py:536: OperationError
______________ QuerySetTest.test_update_push_and_pull_add_to_set _______________

self = [<BlogPost: BlogPost object>], upsert = False, multi = True
write_concern = {}, read_concern = None
update = {'$pushAll': {'tags': ['mongodb', 'code']}}
queryset = [<BlogPost: BlogPost object>]
query = {'_id': ObjectId('67866cab8336e00afa97904c')}
collection = Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=True, connect=True, read_preference=Primary()), 'mongoenginetest'), 'blog_post')
update_func = <bound method Collection.update_many of Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=True, connect=True, read_preference=Primary()), 'mongoenginetest'), 'blog_post')>

def update(
    self, upsert=False, multi=True, write_concern=None, read_concern=None, **update
):
    """Perform an atomic update on the fields matched by the query.

    :param upsert: Any existing document with that "_id" is overwritten.
    :param multi: Update multiple documents.
    :param write_concern: Write concern of this operation.
    :param read_concern: Override the read concern for the operation
    :param update: Django-style update keyword arguments

    .. versionadded:: 0.2
    """
    if not update and not upsert:
        raise OperationError("No update parameters, would remove data")

    if write_concern is None:
        write_concern = {}

    queryset = self.clone()
    query = queryset._query
    update = transform.update(queryset._document, **update)

    # If doing an atomic upsert on an inheritable class
    # then ensure we add _cls to the update operation
    if upsert and '_cls' in query:
        if '$set' in update:
            update["$set"]["_cls"] = queryset._document._class_name
        else:
            update["$set"] = {"_cls": queryset._document._class_name}
    try:
        with set_read_write_concern(
            queryset._collection, write_concern, read_concern
        ) as collection:
            update_func = collection.update_one
            if multi:
                update_func = collection.update_many
          result = update_func(query, update, upsert=upsert)

mongoengine/queryset/queryset.py:527:


../.local/lib/python3.10/site-packages/pymongo/collection.py:1104: in update_many
self._update_retryable(
../.local/lib/python3.10/site-packages/pymongo/collection.py:877: in _update_retryable
return self.__database.client._retryable_write(
../.local/lib/python3.10/site-packages/pymongo/mongo_client.py:1552: in _retryable_write
return self._retry_with_session(retryable, func, s, None)
../.local/lib/python3.10/site-packages/pymongo/mongo_client.py:1438: in _retry_with_session
return self._retry_internal(retryable, func, session, bulk)
../.local/lib/python3.10/site-packages/pymongo/mongo_client.py:1470: in _retry_internal
return func(session, sock_info, retryable)
../.local/lib/python3.10/site-packages/pymongo/collection.py:869: in _update
return self._update(
../.local/lib/python3.10/site-packages/pymongo/collection.py:846: in _update
_check_write_command_response(result)
../.local/lib/python3.10/site-packages/pymongo/helpers.py:241: in _check_write_command_response
_raise_last_write_error(write_errors)


write_errors = [{'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array', 'index': 0}]

def _raise_last_write_error(write_errors):
    # If the last batch had multiple errors only report
    # the last error to emulate continue_on_error.
    error = write_errors[-1]
    if error.get("code") == 11000:
        raise DuplicateKeyError(error.get("errmsg"), 11000, error)
  raise WriteError(error.get("errmsg"), error.get("code"), error)

E pymongo.errors.WriteError: Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'}

../.local/lib/python3.10/site-packages/pymongo/helpers.py:211: WriteError

During handling of the above exception, another exception occurred:

self = <tests.queryset.test_queryset.QuerySetTest testMethod=test_update_push_and_pull_add_to_set>

def test_update_push_and_pull_add_to_set(self):
    """Ensure that the 'pull' update operation works correctly.
    """
    class BlogPost(Document):
        slug = StringField()
        tags = ListField(StringField())

    BlogPost.drop_collection()

    post = BlogPost(slug="test")
    post.save()

    BlogPost.objects.filter(id=post.id).update(push__tags="code")
    post.reload()
    self.assertEqual(post.tags, ["code"])
  BlogPost.objects.filter(id=post.id).update(push_all__tags=["mongodb", "code"])

tests/queryset/test_queryset.py:1440:


self = [<BlogPost: BlogPost object>], upsert = False, multi = True
write_concern = {}, read_concern = None
update = {'$pushAll': {'tags': ['mongodb', 'code']}}
queryset = [<BlogPost: BlogPost object>]
query = {'_id': ObjectId('67866cab8336e00afa97904c')}
collection = Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=True, connect=True, read_preference=Primary()), 'mongoenginetest'), 'blog_post')
update_func = <bound method Collection.update_many of Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=True, connect=True, read_preference=Primary()), 'mongoenginetest'), 'blog_post')>

def update(
    self, upsert=False, multi=True, write_concern=None, read_concern=None, **update
):
    """Perform an atomic update on the fields matched by the query.

    :param upsert: Any existing document with that "_id" is overwritten.
    :param multi: Update multiple documents.
    :param write_concern: Write concern of this operation.
    :param read_concern: Override the read concern for the operation
    :param update: Django-style update keyword arguments

    .. versionadded:: 0.2
    """
    if not update and not upsert:
        raise OperationError("No update parameters, would remove data")

    if write_concern is None:
        write_concern = {}

    queryset = self.clone()
    query = queryset._query
    update = transform.update(queryset._document, **update)

    # If doing an atomic upsert on an inheritable class
    # then ensure we add _cls to the update operation
    if upsert and '_cls' in query:
        if '$set' in update:
            update["$set"]["_cls"] = queryset._document._class_name
        else:
            update["$set"] = {"_cls": queryset._document._class_name}
    try:
        with set_read_write_concern(
            queryset._collection, write_concern, read_concern
        ) as collection:
            update_func = collection.update_one
            if multi:
                update_func = collection.update_many
            result = update_func(query, update, upsert=upsert)
        if result.raw_result:
            return result.raw_result['n']
    except pymongo.errors.DuplicateKeyError as err:
        raise NotUniqueError('Update failed (%s)' % str(err))
    except pymongo.errors.OperationFailure as err:
        if str(err) == 'multi not coded yet':
            message = 'update() method requires MongoDB 1.1.3+'
            raise OperationError(message)
      raise OperationError('Update failed (%s)' % str(err))

E mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})

mongoengine/queryset/queryset.py:536: OperationError


</details>

This is an obscure piece of functionality that's hasn't been working
for quite some time and is not needed for us going forward.

The only remaining references to `item_frequencies` are in the old
upgrade doc and in a changelog that's unmaintained in this repo:
```
(vevn) wojcikstefan@stefans-mbp mongoengine % rg item_frequencies
docs/changelog.rst
233:- Added support for null / zero / false values in item_frequencies
432:- Updated item_frequencies to handle embedded document lookups
443:- Added map_reduce method item_frequencies and set as default (as db.eval doesn't work in sharded environments)
464:- Fixed item_frequencies when using name thats the same as a native js function

docs/upgrade.rst
388:item_frequencies
392:item_frequencies.  A side effect was to return keys in the value they are
449:    * :meth:`~mongoengine.queryset.QuerySet.item_frequencies`
```
@wojcikstefan
Copy link
Member Author

Removed QuerySet.item_frequencies in 34ca448. This dropped the number of test failures to 9:

FAILED tests/document/test_instance.py::InstanceTest::test_reload_sharded - KeyError: 'query'
FAILED tests/document/test_instance.py::InstanceTest::test_update_shard_key_routing - KeyError: 'query'
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_exec_js_field_sub - pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_exec_js_query - pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_map_reduce - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_map_reduce_finalize - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_map_reduce_with_custom_object_ids - pymongo.errors.OperationFailure: 'map' must be of string or code type, full error: {'ok': 0.0, 'errmsg': "'map' must be of string or code type", 'code': 2, 'codeName': 'BadValue'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_update - mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_update_push_and_pull_add_to_set - mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})

This is an obscure piece of functionality that's hasn't been working
for quite some time and is not needed for us going forward.

The only remaining references to `map_reduce` are in the old
upgrade doc and in a changelog that's unmaintained in this repo:
```
(vevn) wojcikstefan@stefans-mbp mongoengine % rg map_reduce
docs/changelog.rst
418:- Updated sum / average to use map_reduce as db.eval doesn't work in sharded environments
443:- Added map_reduce method item_frequencies and set as default (as db.eval doesn't work in sharded environments)
444:- Added inline_map_reduce option to map_reduce
459:  map_reduce now requires an output.

docs/upgrade.rst
428:main areas of changed are: choices in fields, map_reduce and collection names.
444:More methods now use map_reduce as db.eval is not supported for sharding as
```
@wojcikstefan
Copy link
Member Author

Removed QuerySet.map_reduce in 70806f9. Test failures are down to 6:

FAILED tests/document/test_instance.py::InstanceTest::test_reload_sharded - KeyError: 'query'
FAILED tests/document/test_instance.py::InstanceTest::test_update_shard_key_routing - KeyError: 'query'
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_exec_js_field_sub - pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_exec_js_query - pymongo.errors.OperationFailure: no such command: '$eval', full error: {'ok': 0.0, 'errmsg': "no such command: '$eval'", 'code': 59, 'codeName': 'CommandNotFound'}
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_update - mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_update_push_and_pull_add_to_set - mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})

These weren't really working well already and we don't need them. It's also
dangerous to run arbitrary and possibly unvalidated / unsanitized JS code from
Python.

The only remaining mention of `exec_js` is in the unmaintained changelog:
```
(vevn) wojcikstefan@stefans-mbp mongoengine % rg exec_js
docs/changelog.rst
535:- Fixed bug where ``QuerySet.exec_js`` ignored ``Q`` objects
```
@wojcikstefan
Copy link
Member Author

Removed QuerySet.exec_js and QuerySet.where in 70806f9. Down to 4 test failures:

FAILED tests/document/test_instance.py::InstanceTest::test_reload_sharded - KeyError: 'query'
FAILED tests/document/test_instance.py::InstanceTest::test_update_shard_key_routing - KeyError: 'query'
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_update - mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})
FAILED tests/queryset/test_queryset.py::QuerySetTest::test_update_push_and_pull_add_to_set - mongoengine.errors.OperationError: Update failed (Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array, full error: {'index': 0, 'code': 9, 'errmsg': 'Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array'})

This operator has been deprecated since MongoDB v2.4 and has been removed from
subsequent versions.
@wojcikstefan
Copy link
Member Author

Removed the $pushAll operator in 5234645 since it has been deprecated since MongoDB v2.4 and was removed from subsequent versions.

I had to manually download the super old v3.2 docs to confirm that this operator even existed and has been deprecated:
Screenshot 2025-01-14 at 3 37 08 PM

Down to 2 failures:

FAILED tests/document/test_instance.py::InstanceTest::test_reload_sharded - KeyError: 'query'
FAILED tests/document/test_instance.py::InstanceTest::test_update_shard_key_routing - KeyError: 'query'

@wojcikstefan
Copy link
Member Author

Ok, last 2 tests needed fixing because the shape of the documents in MongoDB's profile collection has changed. All good now.

@wojcikstefan wojcikstefan requested a review from jkassemi January 14, 2025 15:04
@wojcikstefan wojcikstefan changed the title [WIP] Run tests on MongoDB v5.0.23 instead of v3.2.19 Run tests on MongoDB v5.0.23 instead of v3.2.19 Jan 14, 2025
@jkassemi jkassemi merged commit 009aefd into master Jan 14, 2025
2 checks passed
@jkassemi jkassemi deleted the test-on-mongodb-v5 branch January 14, 2025 16:00
This was referenced Jan 14, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants