Обновление документов в MongoDB
Узнайте, как обновлять документы MongoDB в Python с помощью update_one(), update_many(), upsert и операторов $set, $inc, $push.
PyMongo предоставляет два основных метода для изменения документов в коллекции: update_one() изменяет первый документ, соответствующий фильтру, а update_many() изменяет каждый подходящий документ. Оба метода используют операторы обновления MongoDB — инструкции вида $set, $inc и $push — чтобы точно описать, что именно должно измениться, оставляя остальную часть документа нетронутой.
Эта глава охватывает:
- Подключение к MongoDB с помощью PyMongo
update_one()— изменение одного документаupdate_many()— изменение нескольких документов одновременно- Операторы обновления:
$set,$unset,$inc,$push,$pull - Обновление вложенных полей с помощью точечной нотации
- Upsert — вставка или обновление за один шаг
- Проверка результатов с помощью объекта результата
Если вы ещё не вставляли документы, сначала прочитайте MongoDB Insert. Для получения информации о синтаксисе фильтрации см. MongoDB Query.
Подключение к MongoDB
Установите драйвер, если вы ещё этого не сделали:
pip install pymongoЗатем подключитесь к базе данных и коллекции:
import pymongo
client = pymongo.MongoClient("mongodb://localhost:27017/")
db = client["mydatabase"]
mycol = db["mycollection"]MongoClient использует ленивое подключение — сокет открывается только при первой операции. База данных и коллекция создаются автоматически при первой записи документа в них.
Обновление одного документа
update_one(filter, update) находит первый документ, соответствующий filter, и применяет к нему update.
result = mycol.update_one(
{"name": "Alice"}, # filter: which document to change
{"$set": {"age": 31}} # update: what to change
)
print(result.matched_count) # 1 if a document was found
print(result.modified_count) # 1 if a value actually changedОператор $set перезаписывает указанные поля и оставляет все остальные поля документа в неизменном виде. Без $set второй аргумент заменит весь документ целиком, что редко бывает нужным.
Почему matched_count и modified_count могут различаться
Если у документа уже есть age: 31, MongoDB находит его, но ничего не записывает. В этом случае matched_count равен 1, а modified_count равен 0. Всегда проверяйте modified_count, чтобы убедиться, что реальная запись произошла.
Обновление нескольких документов
update_many(filter, update) применяет одно и то же обновление ко всем документам, соответствующим фильтру.
result = mycol.update_many(
{"status": {"$ne": "closed"}}, # every document where status != "closed"
{"$set": {"status": "open"}}
)
print(f"Matched: {result.matched_count}")
print(f"Modified: {result.modified_count}")update_many() атомарен на уровне каждого документа, но не на уровне всей операции. Каждый отдельный документ изменяется атомарно, однако другие клиенты могут читать или писать между обновлениями отдельных документов в рамках одного вызова update_many().
Операторы обновления
MongoDB предоставляет набор операторов обновления, позволяющих точно выразить изменения без передачи всего документа по сети.
$set — установка значений полей
# Set one field
mycol.update_one({"name": "Alice"}, {"$set": {"city": "Boston"}})
# Set multiple fields at once
mycol.update_one({"name": "Alice"}, {"$set": {"city": "Boston", "active": True}})$unset — удаление поля
# Remove the "temporary" field from the matching document
mycol.update_one({"name": "Alice"}, {"$unset": {"temporary": ""}})Значение, которое вы присваиваете полю внутри $unset, не имеет значения — MongoDB его игнорирует. По соглашению используется пустая string.
$inc — увеличение или уменьшение числа
# Add 5 to the "score" field (creates it at 5 if it does not exist)
mycol.update_one({"name": "Alice"}, {"$inc": {"score": 5}})
# Subtract 1
mycol.update_one({"name": "Alice"}, {"$inc": {"score": -1}})$inc безопасен для счётчиков, поскольку является единственной атомарной операцией — два одновременных увеличения будут применены корректно.
$push — добавление элемента в array
# Add a tag to the "tags" array (creates the array if it does not exist)
mycol.update_one({"name": "Alice"}, {"$push": {"tags": "python"}})$pull — удаление элементов из array
# Remove all occurrences of "python" from the "tags" array
mycol.update_one({"name": "Alice"}, {"$pull": {"tags": "python"}})Обновление вложенных документов
Используйте точечную нотацию, чтобы обращаться к полям во вложенных документах без перезаписи всего вложенного object.
Рассмотрим документ следующей структуры:
{
"name": "Alice",
"address": {
"city": "New York",
"state": "NY"
}
}Чтобы изменить только поле state внутри address:
mycol.update_one(
{"name": "Alice"},
{"$set": {"address.state": "NJ"}}
)Поле city внутри address остаётся нетронутым. Вы можете углубляться на столько уровней, сколько требует ваша схема — например, "address.geo.lat".
Upsert: вставка или обновление за один шаг
Upsert говорит MongoDB: «обнови этот документ, если он существует; вставь его, если не существует». Передайте upsert=True в качестве именованного аргумента:
result = mycol.update_one(
{"name": "Bob"},
{"$set": {"age": 25, "status": "new"}},
upsert=True
)
if result.upserted_id:
print(f"Inserted new document with _id: {result.upserted_id}")
else:
print("Updated an existing document")Когда MongoDB вставляет новый документ через upsert, атрибут upserted_id объекта результата содержит новый _id. Когда обновляется существующий документ, upserted_id равен None.
update_many() также принимает upsert=True. Если ни один документ не совпадает, MongoDB вставляет один новый документ — при одном upsert-запросе никогда не вставляется несколько документов.
Изучение объекта результата
И update_one(), и update_many() возвращают объект UpdateResult с тремя полезными атрибутами:
| Атрибут | Описание |
|---|---|
matched_count | Количество документов, соответствующих фильтру |
modified_count | Количество документов, которые были фактически изменены |
upserted_id | _id вновь вставленного документа (только при upsert); иначе None |
result = mycol.update_many(
{"role": "guest"},
{"$set": {"role": "member"}}
)
print(f"Matched: {result.matched_count}")
print(f"Modified: {result.modified_count}")
print(f"Upserted: {result.upserted_id}")Распространённые ошибки
Забытый оператор обновления заменяет документ целиком. Этот вызов удаляет все поля, кроме _id, и заменяет документ только значением age: 31:
# Dangerous — replaces the whole document
mycol.update_one({"name": "Alice"}, {"age": 31})Всегда оборачивайте изменения в оператор ($set, $inc и т.д.).
Фильтрация по _id требует объекта ObjectId. Идентификаторы документов хранятся как объекты ObjectId, а не обычные string:
from bson.objectid import ObjectId
mycol.update_one(
{"_id": ObjectId("64a1f2c3d4e5f6a7b8c9d0e1")},
{"$set": {"verified": True}}
)Передача ID в виде обычной string ничего не найдёт и молча ничего не сделает.
Дальнейшие шаги
- MongoDB Delete — удаление документов с помощью
delete_one()иdelete_many() - MongoDB Find — получение документов и фильтрация результатов
- MongoDB Query — расширенные операторы и шаблоны запросов