Для редактирования документов коллекции применяется ряд методов:
ReplaceOne()/ReplaceOneAsync(): полностью заменяет один документ другим
UpdateOne()/UpdateOneAsync(): обновляет один документ/p>
UpdateMany()/UpdateManyAsync(): обновляет набор документов
FindOneAndReplace()/FindOneAndReplaceAsync(): если находит документ, который соответствует фильтру, то заменяет его и возвращает заменный документ
ReplaceOne()/ReplaceOneAsync(): полностью заменяет один документ другим
Они имеют разные версии, принимают разное количество параметров. Но все они как минимум принимают два параметра:
Фильтр, который позволяет отфильтровать документы для обновления
новый документ, который заменяет старый или описывает принципы изменения старого
При обновлении надо учитывать, что мы не можем изменить поле _id
.
Метод ReplaceOneAsync() позволяет полностью заменить старый документ новым. Допустим, у нас есть коллекция с именем "users", которая хранит следующие документы:
{ "_id" : ObjectId("63596dc76348749cac779373"), "Name" : "Tom", "Age" : 38, "Languages" : ["english", "german", "spanish"] } { "_id" : ObjectId("63596dc76348749cac779374"), "Name" : "Bob", "Age" : 42, "Languages" : ["english"] } { "_id" : ObjectId("63596dc76348749cac779375"), "Name" : "Sam", "Age" : 25, "Languages" : ["english", "spanish"] } { "_id" : ObjectId("63596dc76348749cac779376"), "Name" : "Alice", "Age" : 33 } { "_id" : ObjectId("63596dc76348749cac779377"), "Name" : "Tom", "Age" : 33, "Languages" : ["english"] }
Например:
using MongoDB.Bson; using MongoDB.Driver; MongoClient client = new MongoClient("mongodb://localhost:27017"); var db = client.GetDatabase("test"); var collection = db.GetCollection<BsonDocument>("users"); // определяем фильтр - документ, где Name = Tom и Age = 33 var filter = new BsonDocument { { "Name", "Tom" }, { "Age", 33 } }; // определяем документ, на который будет заменять var newDocument = new BsonDocument { { "Name", "Tomas" }, { "Age", 34 } }; // выполняем замену var result = await collection.ReplaceOneAsync(filter, newDocument); Console.WriteLine($"Найдено по соответствию: {result.MatchedCount}; обновлено: {result.ModifiedCount}"); // проверяем - выводи документы после обновления var users = await collection.Find("{}").ToListAsync(); foreach (var user in users) Console.WriteLine(user);
В методе ReplaceOneAsync()
первый параметр описывает критерии выборки. В данном случе фильтр хранится в переменной filter
,
которая представляет все документы, где "Name"="Tom" и "Age" = 34:
var filter = new BsonDocument { { "Name", "Tom" }, { "Age", 33 } };
Второй параметр описывает документ, который вставляется на место старого. Здесь его представляет переменная newDocument
var newDocument = new BsonDocument { { "Name", "Tomas" }, { "Age", 34 } };
Если документов, которые соответствуют фильтру, отсутствуют в выборке, то соответственно и замен не будет. Если фильтру соответствуют несколько документов, то будет заменен только первый из них.
Метод ReplaceOneAsync()
возвращает объект ReplaceOneResult, с помощью которого мы можем получить информацию об обновлении,
в частности, получить количество документов, которые соответствуют критерию, и количество обновленных документов.
Консольный вывод программы:
Найдено по соответствию: 1; обновлено: 1 { "_id" : ObjectId("63596dc76348749cac779373"), "Name" : "Tom", "Age" : 38, "Languages" : ["english", "german", "spanish"] } { "_id" : ObjectId("63596dc76348749cac779374"), "Name" : "Bob", "Age" : 42, "Languages" : ["english"] } { "_id" : ObjectId("63596dc76348749cac779375"), "Name" : "Sam", "Age" : 25, "Languages" : ["english", "spanish"] } { "_id" : ObjectId("63596dc76348749cac779376"), "Name" : "Alice", "Age" : 33 } { "_id" : ObjectId("63596dc76348749cac779377"), "Name" : "Tomas", "Age" : 34 }
Обратите внимание, что начальный документ содержал массив "Languages", в новом же документе такой массив не определен, то есть произошла полная замена документа.
С помощью дополнительного параметра ReplaceOptions метода ReplaceOneAsync()
можно управлять параметрами обновления.
new ReplaceOptions { IsUpsert = true }
мы можем указать обязательность вставки документа в коллекцию, даже если не найдено соответствий по критерию:
using MongoDB.Bson; using MongoDB.Driver; MongoClient client = new MongoClient("mongodb://localhost:27017"); var db = client.GetDatabase("test"); var collection = db.GetCollection("users"); // определяем фильтр - документ, где Name = Alex и Age = 33 var filter = new BsonDocument { { "Name", "Alex" }, { "Age", 33 } }; // определяем документ, на который будет заменять var newDocument = new BsonDocument { { "Name", "Alexander" }, { "Age", 34 } }; // выполняем замену, если документ не найден, то вставку var result = await collection.ReplaceOneAsync(filter, newDocument, new ReplaceOptions { IsUpsert=true }); Console.WriteLine($"Matched: {result.MatchedCount}; Modified: {result.ModifiedCount}; UpsertedId: {result.UpsertedId}"); var users = await collection.Find("{}").ToListAsync(); foreach (var user in users) Console.WriteLine(user);
В данном случае меняем документ, который попадает под фильтр new BsonDocument { { "Name", "Alex" }, { "Age", 33 } }
на документ
new BsonDocument { { "Name", "Alexander" }, { "Age", 34 } }
. С помощью свойства result.UpsertedId
можно получить id добавленного оъекта.
Консольный вывод программы:
Matched: 0; Modified: 0; UpsertedId: 635bcb6a9abf25799fc1c4c3 { "_id" : ObjectId("63596dc76348749cac779373"), "Name" : "Tom", "Age" : 38, "Languages" : ["english", "german", "spanish"] } { "_id" : ObjectId("63596dc76348749cac779374"), "Name" : "Bob", "Age" : 42, "Languages" : ["english"] } { "_id" : ObjectId("63596dc76348749cac779375"), "Name" : "Sam", "Age" : 25, "Languages" : ["english", "spanish"] } { "_id" : ObjectId("63596dc76348749cac779376"), "Name" : "Alice", "Age" : 33 } { "_id" : ObjectId("63596dc76348749cac779377"), "Name" : "Tomas", "Age" : 34 } { "_id" : ObjectId("635bcb6a9abf25799fc1c4c3"), "Name" : "Alexander", "Age" : 34 }
Если ReplaceOne/ReplaceOneAsync
полностью заменяют весь документу, то методы UpdateOne().UpdateOneAsync()
позволяют обновить только
отдельные поля документа:
using MongoDB.Bson; using MongoDB.Driver; MongoClient client = new MongoClient("mongodb://localhost:27017"); var db = client.GetDatabase("test"); var collection = db.GetCollection<BsonDocument>("users"); // определяем фильтр - документ, где Name = Tom var filter = new BsonDocument("Name", "Tom"); // определяем параметры обновления - обновляем только поле Age var updateSettings = new BsonDocument("$set", new BsonDocument("Age", 39)); // выполняем обновление var result = await collection.UpdateOneAsync(filter, updateSettings); Console.WriteLine($"Matched: {result.MatchedCount}; Modified: {result.ModifiedCount}"); var users = await collection.Find("{}").ToListAsync(); foreach (var user in users) Console.WriteLine(user);
Первый параметр метода UpdateOneAsync устанавливает фильтр документа - в данном случае ищем документ, где Name = "Tom":
var filter = new BsonDocument("Name", "Tom");
Второй параметр метода UpdateOneAsync задает принципы обновления. В частности здесь используется оператор $set, который задает набор полей и их обновленных значений. То есть здесь обновляем только поле Age - устанавливаем ему значение 39:
var updateSettings = new BsonDocument("$set", new BsonDocument("Age", 39));
Результат метода UpdateOneAsync (как и в случае с ReplaceOneAsync) позволяет узнать количество соответствий и обновлений.
С помощью $set
можно обновить значения сразу нескольких полей. Например, присвоим полю Name значение "Tom", а полю Age число 33:
var filter = new BsonDocument("Name", "Tomas"); var updateSettings = new BsonDocument("$set", new BsonDocument { { "Name", "Tom" }, { "Age", 33 } }); var result = await collection.UpdateOneAsync(filter, updateSettings);
Для обновления мы можем использовать все те же самые операторы консоли mongodb, которые рассматривались в теме Обновление данных в MongoDB. Оператор $set
Например, применим оператор $inc, который увеличивает числовое значение на несколько единиц:
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Bob"), new BsonDocument("$inc", new BsonDocument("Age", 2)));
Здесь увеличиваем у документа с Name="Bob" значение поля "Age" на 2.
Для удаления отдельного поля используется оператор $unset. Например, удалим поле "Age":
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Alexander"), new BsonDocument("$unset", new BsonDocument("Age", 1)));
Для удаляемого поля в качестве значения передается единица. Если вдруг подобного поля в документе не существует, то оператор не оказывает никакого влияния. Также можно удалять сразу несколько полей:
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Sam"), new BsonDocument("$unset", new BsonDocument { { "Age", 1 }, { "Languages", 1 } }));
Оператор $push позволяет добавить еще одно значение в массив. Если поле-массив отсутствует, то оно автоматически создается. Например, добавим значение в поле Languages:
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Sam"), new BsonDocument("$push", new BsonDocument { { "Languages", "english" }}));
Используя оператор $each
, можно добавить сразу несколько значений:
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Sam"), new BsonDocument("$push", new BsonDocument("Languages", new BsonDocument("$each", new BsonArray {"french", "german"}))));
Оператор $addToSet подобно оператору $push добавляет объекты в массив. Отличие состоит в том, что $addToSet добавляет данные, если их еще нет в массиве (
при добавлении через $push
данные дублируются, если добавляются элементы, которые уже есть в массиве):
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Sam"), new BsonDocument("$addToSet", new BsonDocument("Languages", "italian")));
Оператор $pop позволяет удалять элемент из массива:
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Sam"), new BsonDocument("$pop", new BsonDocument("Languages", 1)));
Указывая для поля Languages значение 1, мы удаляем первый элемент с конца. Чтобы удалить первый элемент сначала массива, надо передать отрицательное значение:
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Sam"), new BsonDocument("$pop", new BsonDocument("Languages", -1)));
Несколько иное действие предполагает оператор $pull. Он удаляет каждое вхождение элемента в массив. Например, через оператор $push мы можем добавить одно и то же значение в массив несколько раз. И теперь с помощью $pull удалим его:
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Sam"), new BsonDocument("$pull", new BsonDocument("Languages", "german")));
А если мы хотим удалить не одно значение, а сразу несколько, тогда мы можем применить оператор $pullAll
:
var result = await collection.UpdateOneAsync( new BsonDocument("Name", "Sam"), new BsonDocument("$pullAll", new BsonDocument("Languages", new BsonArray { "english", "french" })));
Для обновления множества документов применяются методы UpdateMany()/UpdateManyAsync(), использование которых аналогично UpdateOne/UpdateOneAsync. Например, увеличим значение поля Age на 1 в документах, где Name = "Tom":
using MongoDB.Bson; using MongoDB.Driver; MongoClient client = new MongoClient("mongodb://localhost:27017"); var db = client.GetDatabase("test"); var collection = db.GetCollection<BsonDocument>("users"); // Увеличим Age на 1 в документах, где Name = Tom var result = await collection.UpdateManyAsync( new BsonDocument("Name", "Tom"), new BsonDocument("$inc", new BsonDocument("Age", 1))); Console.WriteLine($"Matched: {result.MatchedCount}; Modified: {result.ModifiedCount}"); var users = await collection.Find("{}").ToListAsync(); foreach (var user in users) Console.WriteLine(user);
Для обновления документов вместо BsonDocument можно использовать объект UpdateDefinitionBuilder, у которого определяется ряд методов, соответствющих консольным операторам mongodb:
Set: изменяет значение поля
AddToSet: добавляет новые элементы в поле документа, которое представляет массив. Например, добавляем в массив
Languages новую строку: Builders<BsonDocument>.Update.AddToSet("Languages", "spanish")
Inc: инкрементирует значение числового свойства на указанное число единиц. Например, увеличим значение свойства Age на две единицы:
Builders<BsonDocument>.Update.Inc("Age", 2)
Mul: умножает значение числового свойства на указанное число
Push: добавляет новые элементы для ключа, который представляет массив. Например: Builders<BsonDocument>.Update.Push("Languages", "french")
Pull: удаляет определенный элемент из массива. Например: Builders<BsonDocument>.Update.Pull("Languages", "french")
Unset: удаляет ключ и его значение. Например: Builders<BsonDocument>.Update.Unset("Age")
PopFirst: выбирает первый элемент для свойства, которое представляет массив
PopLast: выбирает последний элемент для свойства, которое представляет массив
Rename: переименовывает название ключа в документе. Например, переименуем поле Age в Year: Builders<BsonDocument>.Update.Rename("Age", "Year");
.
Например, применим метод Set(). Этот метод в качестве парамета принимает свойство, которое надо изменить, и его обновленное значение. Например, установим свойство Age равным 30 у всех объектов, у которых Name="Tom".
// параметр фильтрации var filter = Builders<BsonDocument>.Filter.Eq("Name", "Tom"); // параметр обновления var update = Builders<BsonDocument>.Update.Set("Age", 30); var result = await collection.UpdateManyAsync(filter, update);
При работе с методом Set также надо учитывать, что если такого поля еще нет в документе в базе данных, например, оно просто ранее не было установлено, то данное поле добавляется в документ.
C помощью вспомогательного метода CurrentDate()
, который имеется в UpdateDefinitionBuilder, можно установить у документа время последнего изменения:
var filter = Builders<BsonDocument>.Filter.Eq("Name", "Tom"); var update = Builders<BsonDocument>.Update.Set("Age", 33).CurrentDate("LastModified"); var result = await collection.UpdateManyAsync(filter, update);
В данном случае к документу добавляется поле LastModified, которое хранит время обновления.
Метод FindOneAndReplaceAsync() ищет документ, который соответствует критерию. И если документ найден, полностью заменяет его другим и возвращает его в качестве результата. Если документ не найден, ничего не делает и просто возвращает null:
using MongoDB.Bson; using MongoDB.Driver; MongoClient client = new MongoClient("mongodb://localhost:27017"); var db = client.GetDatabase("test"); var collection = db.GetCollection<BsonDocument>("users"); // заменяем документ с Name="Tom" на {{"Name", "Tomas"}, { "Age", 22}} var result = await collection.FindOneAndReplaceAsync(new BsonDocument("Name", "Tom"), new(){{"Name", "Tomas"}, { "Age", 22}}); if (result == null) Console.WriteLine("Документ не найдены"); else Console.WriteLine($"Заменен документ: {result}");
В данном случае документ с "Name"="Tom" заменяется на документ {{"Name", "Tomas"}, { "Age", 22}}
. В качестве результата возвращается старый документ,
который был заменен. Если нам надо возвращать новый документ, на который произошла замена, то необходимо в метод в качестве третьего параметра
передать объект FindOneAndReplaceOptions
со свойством ReturnDocument = ReturnDocument.After
.
var result = await collection.FindOneAndReplaceAsync( new BsonDocument("Name", "Tomas"), // фильтр new BsonDocument { { "Name", "Tom" }, { "Age", 38 } }, // на какой документ заменяем new FindOneAndReplaceOptions<BsonDocument>() { ReturnDocument = ReturnDocument.After });
Метод FindOneAndUpdateAsync() ищет документ, который соответствует критерию. И если документ найден, полностью обновляет его в соответствии с параметрами обновления. В качестве результата по умолчанию возвращаются поля документа до обновления другим. Если документ, который соответствует фильтру, не найден, то метод просто возвращает null:
using MongoDB.Bson; using MongoDB.Driver; MongoClient client = new MongoClient("mongodb://localhost:27017"); var db = client.GetDatabase("test"); var collection = db.GetCollection<BsonDocument>("users"); var result = await collection.FindOneAndUpdateAsync( new BsonDocument("Name", "Tom"), // фильтр new BsonDocument("$set", new BsonDocument("Age", 39))); if (result == null) Console.WriteLine("Документ не найдены"); else Console.WriteLine($"Обновленный документ: {result}");
В данном случае в документе с "Name"="Tom" для поля "Age" устанавливается значение 39. В качестве результата возвращается документ со старыми значениями
до обновления. Если нам надо возвращать документ со значениями после обновления, то необходимо в метод в качестве третьего параметра
передать объект FindOneAndUpdateOptions
со свойством ReturnDocument = ReturnDocument.After
.
var result = await collection.FindOneAndUpdateAsync( new BsonDocument("Name", "Tom"), // фильтр new BsonDocument("$set", new BsonDocument("Age", 22)), new FindOneAndUpdateOptions<BsonDocument>() { ReturnDocument = ReturnDocument.After });
Обновление стандартных Poco-типов в C#, например, объектов Person, будет аналогично:
using MongoDB.Bson; using MongoDB.Driver; MongoClient client = new MongoClient("mongodb://localhost:27017"); var db = client.GetDatabase("test"); var collection = db.GetCollection<Person>("users"); // параметр фильтрации var filter = Builders<Person>.Filter.Eq(p=>p.Name, "Tom"); // параметр обновления var update = Builders<Person>.Update.Set(p=>p.Age, 34); // обновляем var result = await collection.UpdateManyAsync(filter, update); Console.WriteLine($"найдено по соответствию: {result.MatchedCount}; обновлено: {result.ModifiedCount}"); var users = await collection.Find("{}").ToListAsync(); foreach (var user in users) Console.WriteLine(user.ToJson()); class Person { public ObjectId Id { get; set; } public string Name { get; set; } = ""; public int Age { get; set; } public List<string>? Languages { get; set; } public DateTime? LastModified { get; set; } }
При использовании типов POCO, например, Person, в методах UpdateDefinitionBuilder мы можем применять лямбда-выражения: Update.Set(x=>x.Age, 34)