From 7848feb09a12bb6a14fb18deb8b873d8c2eff8a9 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 1 Oct 2024 18:32:41 -0400 Subject: [PATCH] PYTHON-4786 - Fix UpdateResult.did_upsert TypeError (#1878) --- pymongo/results.py | 7 ++-- test/asynchronous/test_client_bulk_write.py | 40 +++++++++++++++++++++ test/asynchronous/test_collection.py | 13 +++++++ test/test_client_bulk_write.py | 40 +++++++++++++++++++++ test/test_collection.py | 13 +++++++ test/test_results.py | 22 ++++++++++++ 6 files changed, 133 insertions(+), 2 deletions(-) diff --git a/pymongo/results.py b/pymongo/results.py index b34f6c492..d17ff1c3e 100644 --- a/pymongo/results.py +++ b/pymongo/results.py @@ -171,9 +171,12 @@ class UpdateResult(_WriteResult): @property def did_upsert(self) -> bool: - """Whether or not an upsert took place.""" + """Whether an upsert took place. + + .. versionadded:: 4.9 + """ assert self.__raw_result is not None - return len(self.__raw_result.get("upserted", {})) > 0 + return "upserted" in self.__raw_result class DeleteResult(_WriteResult): diff --git a/test/asynchronous/test_client_bulk_write.py b/test/asynchronous/test_client_bulk_write.py index 80cfd30bd..946433780 100644 --- a/test/asynchronous/test_client_bulk_write.py +++ b/test/asynchronous/test_client_bulk_write.py @@ -550,6 +550,46 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest): "bulk_write does not currently support automatic encryption", context.exception._message ) + @async_client_context.require_version_min(8, 0, 0, -24) + @async_client_context.require_no_serverless + async def test_upserted_result(self): + client = await self.async_rs_or_single_client() + + collection = client.db["coll"] + self.addAsyncCleanup(collection.drop) + await collection.drop() + + models = [] + models.append( + UpdateOne( + namespace="db.coll", + filter={"_id": "a"}, + update={"$set": {"x": 1}}, + upsert=True, + ) + ) + models.append( + UpdateOne( + namespace="db.coll", + filter={"_id": None}, + update={"$set": {"x": 1}}, + upsert=True, + ) + ) + models.append( + UpdateOne( + namespace="db.coll", + filter={"_id": None}, + update={"$set": {"x": 1}}, + ) + ) + result = await client.bulk_write(models=models, verbose_results=True) + + self.assertEqual(result.upserted_count, 2) + self.assertEqual(result.update_results[0].did_upsert, True) + self.assertEqual(result.update_results[1].did_upsert, True) + self.assertEqual(result.update_results[2].did_upsert, False) + # https://github.com/mongodb/specifications/blob/master/source/client-side-operations-timeout/tests/README.md#11-multi-batch-bulkwrites class TestClientBulkWriteCSOT(AsyncIntegrationTest): diff --git a/test/asynchronous/test_collection.py b/test/asynchronous/test_collection.py index 74a4a5151..612090b69 100644 --- a/test/asynchronous/test_collection.py +++ b/test/asynchronous/test_collection.py @@ -1444,6 +1444,19 @@ class AsyncTestCollection(AsyncIntegrationTest): self.assertRaises(InvalidOperation, lambda: result.upserted_id) self.assertFalse(result.acknowledged) + async def test_update_result(self): + db = self.db + await db.drop_collection("test") + + result = await db.test.update_one({"x": 0}, {"$inc": {"x": 1}}, upsert=True) + self.assertEqual(result.did_upsert, True) + + result = await db.test.update_one({"_id": None, "x": 0}, {"$inc": {"x": 1}}, upsert=True) + self.assertEqual(result.did_upsert, True) + + result = await db.test.update_one({"_id": None}, {"$inc": {"x": 1}}) + self.assertEqual(result.did_upsert, False) + async def test_update_many(self): db = self.db await db.drop_collection("test") diff --git a/test/test_client_bulk_write.py b/test/test_client_bulk_write.py index d1aff03fc..58b5015dd 100644 --- a/test/test_client_bulk_write.py +++ b/test/test_client_bulk_write.py @@ -550,6 +550,46 @@ class TestClientBulkWriteCRUD(IntegrationTest): "bulk_write does not currently support automatic encryption", context.exception._message ) + @client_context.require_version_min(8, 0, 0, -24) + @client_context.require_no_serverless + def test_upserted_result(self): + client = self.rs_or_single_client() + + collection = client.db["coll"] + self.addCleanup(collection.drop) + collection.drop() + + models = [] + models.append( + UpdateOne( + namespace="db.coll", + filter={"_id": "a"}, + update={"$set": {"x": 1}}, + upsert=True, + ) + ) + models.append( + UpdateOne( + namespace="db.coll", + filter={"_id": None}, + update={"$set": {"x": 1}}, + upsert=True, + ) + ) + models.append( + UpdateOne( + namespace="db.coll", + filter={"_id": None}, + update={"$set": {"x": 1}}, + ) + ) + result = client.bulk_write(models=models, verbose_results=True) + + self.assertEqual(result.upserted_count, 2) + self.assertEqual(result.update_results[0].did_upsert, True) + self.assertEqual(result.update_results[1].did_upsert, True) + self.assertEqual(result.update_results[2].did_upsert, False) + # https://github.com/mongodb/specifications/blob/master/source/client-side-operations-timeout/tests/README.md#11-multi-batch-bulkwrites class TestClientBulkWriteCSOT(IntegrationTest): diff --git a/test/test_collection.py b/test/test_collection.py index dab59cf1b..a2c3b0b0b 100644 --- a/test/test_collection.py +++ b/test/test_collection.py @@ -1429,6 +1429,19 @@ class TestCollection(IntegrationTest): self.assertRaises(InvalidOperation, lambda: result.upserted_id) self.assertFalse(result.acknowledged) + def test_update_result(self): + db = self.db + db.drop_collection("test") + + result = db.test.update_one({"x": 0}, {"$inc": {"x": 1}}, upsert=True) + self.assertEqual(result.did_upsert, True) + + result = db.test.update_one({"_id": None, "x": 0}, {"$inc": {"x": 1}}, upsert=True) + self.assertEqual(result.did_upsert, True) + + result = db.test.update_one({"_id": None}, {"$inc": {"x": 1}}) + self.assertEqual(result.did_upsert, False) + def test_update_many(self): db = self.db db.drop_collection("test") diff --git a/test/test_results.py b/test/test_results.py index 19e086a9a..deb09d7ed 100644 --- a/test/test_results.py +++ b/test/test_results.py @@ -122,6 +122,28 @@ class TestResults(unittest.TestCase): self.assertEqual(raw_result["n"], result.matched_count) self.assertEqual(raw_result["nModified"], result.modified_count) self.assertEqual(raw_result["upserted"], result.upserted_id) + self.assertEqual(result.did_upsert, True) + + raw_result_2 = { + "n": 1, + "nModified": 1, + "upserted": [ + {"index": 5, "_id": 1}, + ], + } + self.repr_test(UpdateResult, raw_result_2) + + result = UpdateResult(raw_result_2, True) + self.assertEqual(result.did_upsert, True) + + raw_result_3 = { + "n": 1, + "nModified": 1, + } + self.repr_test(UpdateResult, raw_result_3) + + result = UpdateResult(raw_result_3, True) + self.assertEqual(result.did_upsert, False) result = UpdateResult(raw_result, False) self.assertEqual(raw_result, result.raw_result)