Arrays
Push new item - $push
The $push operator is used to append a new item to an array. To push a new item create an UpdateDefinition<T>
by defining the array field and the new item to push.
Builders<T>.Update
.Push(doc => doc.<array-field>, <array-item>);
The sample pushes a new VisitedCountry
in the VisitedCountries array of a Traveler
document.
var collection = database
.GetCollection<Traveler>(Constants.TravelersCollection);
var firstTraveler = Builders<Traveler>.Filter.Empty;
// create a visited country item
var visitedCountry = RandomData
.GenerateVisitedCountries(1).First();
visitedCountry.Name = "South Korea";
visitedCountry.TimesVisited = 5;
visitedCountry.LastDateVisited = DateTime.UtcNow.AddYears(5);
// create the update definition
var pushCountryDefinition = Builders<Traveler>
.Update.Push(t => t.VisitedCountries, visitedCountry);
var addNewVisitedCountryResult = await collection
.UpdateOneAsync(firstTraveler, pushCountryDefinition);
var bsonCollection = database
.GetCollection<BsonDocument>(Constants.TravelersCollection);
var bsonFirstUser = Builders<BsonDocument>.Filter.Empty;
var bsonVisitedCountry = RandomData.GenerateVisitedCountries(1).First();
visitedCountry.Name = "North Korea";
visitedCountry.TimesVisited = 5;
visitedCountry.LastDateVisited = DateTime.UtcNow.AddYears(5);
var bsonPushCountryDefinition = Builders<BsonDocument>.Update
.Push("visitedCountries", visitedCountry.ToBsonDocument());
var bsonAddNewVisitedCountryResult = await bsonCollection
.UpdateOneAsync(bsonFirstUser, bsonPushCountryDefinition);
db.travelers.updateOne( {}, {
$push: { visitedCountries: {
name: "My Own Country",
visitedTimes: 2,
lastDateVisited: ISODate("2018-07-11T10:00:23.454Z") } }
})
----------------------------
// sample result
{
"acknowledged" : true,
"matchedCount" : 1,
"modifiedCount" : 1
}
public class Traveler
{
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public List<string> Activities { get; set; }
public List<VisitedCountry> VisitedCountries { get; set; }
}
public class VisitedCountry
{
public string Name { get; set; }
public int TimesVisited { get; set; }
public DateTime LastDateVisited { get; set; }
public GeoLocation Coordinates { get; set; }
}
public class GeoLocation
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
Push multiple items
To push multiple items in a array field use the UpdateDefinitionBuilder<T>.PushEach
method. The method creates a $push => $each
operation and pushes all array items to the array field.
Builders<Traveler>.Update
.PushEach(t => t.<array-field>,
<array-items>)
The sample adds 10 new VisitedCountry
items in the VisitedCountries array field.
var collection = database
.GetCollection<Traveler>(Constants.TravelersCollection);
var firstTraveler = Builders<Traveler>.Filter.Empty;
var newVisitedCountries = RandomData.GenerateVisitedCountries(10);
var pushCountriesDefinition = Builders<Traveler>.Update
.PushEach(t => t.VisitedCountries, newVisitedCountries);
var addNewVisitedCountriesResult = await collection
.UpdateOneAsync(firstTraveler, pushCountriesDefinition);
var bsonCollection = database
.GetCollection<BsonDocument>(Constants.TravelersCollection);
var bsonFirstUser = Builders<BsonDocument>.Filter.Empty;
var bsonNewVisitedCountries = RandomData.GenerateVisitedCountries(10);
var bsonPushCountriesDefinition = Builders<BsonDocument>.Update
.PushEach("visitedCountries", bsonNewVisitedCountries);
var bsonAddNewVisitedCountries = await bsonCollection
.UpdateOneAsync(bsonFirstUser, bsonPushCountriesDefinition);
db.travelers.updateOne({}, {
$push: {
visitedCountries: {
$each: [
{
"name": "South Korea",
"timesVisited": 5,
"lastDateVisited": ISODate("2025-04-21T19:27:38.700+03:00"),
"coordinates": {
"latitude": 4.2429,
"longitude": -148.3179
}
},
{
"name": "Mozambique",
"timesVisited": 6,
"lastDateVisited": ISODate("2017-01-20T14:42:27.786+02:00"),
"coordinates": {
"latitude": -45.9077,
"longitude": -121.6868
}
}
]
}
}
})
----------------------------
// sample result
{
"acknowledged" : true,
"matchedCount" : 1,
"modifiedCount" : 1
}
public class Traveler
{
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public List<string> Activities { get; set; }
public List<VisitedCountry> VisitedCountries { get; set; }
}
public class VisitedCountry
{
public string Name { get; set; }
public int TimesVisited { get; set; }
public DateTime LastDateVisited { get; set; }
public GeoLocation Coordinates { get; set; }
}
public class GeoLocation
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
Push items to a queue
PushEach
method accepts an optional parameter slice which when set properly can provide a queue functionality. Assume there's a SocialAccount
document containing an array of notifications but you wish to always contain the last 2 inserted.
{
"_id": "5ea5801b0932798e2121ff6d",
"username": "Dimitri.Jaskolski",
"relationShips": {
"friends": [
"Jewell.Turcotte",
"Constantin84",
"Aaron_Brakus",
"Dane.Becker",
"Matteo5"
],
"blocked": [
"Gerhard_Batz",
"Lindsey9"
]
},
"lastNotifications": [
{
"text": "Fuga soluta dolores nulla facilis.",
"link": "/project"
}
]
}
The sample tries to add 4 new notifications in the lastNotifications array field using a $slice:-2 operation. The resulting document contains only the last 2 notifications added.
var newNotifications = new List<Notification>();
for (int i = 0; i < 4; i++)
{
newNotifications.Add(new Notification()
{
Link = $"link-{i}",
Text = $"text-{i}"
});
}
var pushNotificationsDefinition = Builders<SocialAccount>.Update
.PushEach(a => a.LastNotifications, newNotifications,
slice:-2); // $slice:-2
var pushNotificationsResult = await socialNetworkCollection
.UpdateOneAsync(Builders<SocialAccount>
.Filter.Eq(a => a.Id, firstAccount.Id), pushNotificationsDefinition);
firstAccount = await socialNetworkCollection
.Find(Builders<SocialAccount>.Filter.Empty)
.FirstOrDefaultAsync();
db.social_network.updateOne(
{},
{
$push: {
lastNotifications: {
$each: [
{ // this will be discard
text: 'text-0',
link: 'link-0'
},
{ // this will be discard
text: 'text-1',
link: 'link-1'
},
{
text: 'text-2',
link: 'link-2'
},
{
text: 'text-3',
link: 'link-3'
}
],
$slice: -2
}
}
}
);
{
"_id": "5ea5801b0932798e2121ff6d",
"username": "Dimitri.Jaskolski",
"relationShips": {
"friends": [
"Jewell.Turcotte",
"Constantin84",
"Aaron_Brakus",
"Dane.Becker",
"Matteo5"
],
"blocked": [
"Gerhard_Batz",
"Lindsey9"
]
},
"lastNotifications": [
{
"text": "text-2",
"link": "link-2"
},
{
"text": "text-3",
"link": "link-3"
}
]
}
Pull items - $pull
To remove items that match a specified condition use the Builders.Update.PullFilter
method.
Builders<StoreItem>.Update
.PullFilter(doc => doc.<array-field>,
item => condition(item));
The sample removes two string values ("FIFA 20", "NBA 2K17") from the PcGames string array field. The empty filter ensures to remove the items for all documents in the collection.
var storesCollection = database
.GetCollection<StoreItem>(Constants.StoreCollection);
var storeEmptyFilter = Builders<StoreItem>.Filter.Empty;
// items to be removed
var removePcGames = new List<string> { "FIFA 20", "NBA 2K17" };
// create the definition
var pullPcGamesDefinition = Builders<StoreItem>
.Update.PullFilter(s => s.PcGames,
game => removePcGames.Contains(game));
var simplePullResult = await storesCollection
.UpdateManyAsync(storeEmptyFilter,
pullPcGamesDefinition);
// initial data in the db
/* 1 */
{
"_id" : ObjectId("5e9f5fdb6b204f01d96a678b"),
"pcGames" : [
"Football Manager",
"DOOM Eternal",
"FIFA 20",
"Grand Theft Auto",
"NBA 2K17"
],
"xboxGames" : [
"Forza Horizon",
"Call of Duty",
"Mortal Kombat",
"Gears 5"
]
},
/* 2 */
{
"_id" : ObjectId("5e9f5fdb6b204f01d96a678c"),
"pcGames" : [
"Assassin's Creed",
"Final Fantasy",
"The Sims",
"Football Manager",
"FIFA 20"
],
"xboxGames" : [
"Resident Evil",
"Forza Motorsport",
"Battlefield",
"Halo 5 Guardians",
"Mortal Kombat"
]
}
db.stores.updateMany({}, {
"$pull": {
"pcGames": {
"$in": [
"FIFA 20",
"NBA 2K17"
]
}
}
})
----------------------------
// final result
/* 1 */
{
"_id" : ObjectId("5e9f5fdb6b204f01d96a678b"),
"pcGames" : [
"Football Manager",
"DOOM Eternal",
"Grand Theft Auto"
],
"xboxGames" : [
"Forza Horizon",
"Call of Duty",
"Mortal Kombat",
"Gears 5"
]
},
/* 2 */
{
"_id" : ObjectId("5e9f5fdb6b204f01d96a678c"),
"pcGames" : [
"Assassin's Creed",
"Final Fantasy",
"The Sims",
"Football Manager"
],
"xboxGames" : [
"Resident Evil",
"Forza Motorsport",
"Battlefield",
"Halo 5 Guardians",
"Mortal Kombat"
]
}
public class StoreItem
{
public BsonObjectId Id { get; set; }
[BsonIgnoreIfDefault]
public List<string> PcGames { get; set; }
[BsonIgnoreIfDefault]
public List<string> XboxGames { get; set; }
}
The PcGames array field doesn't have to contain both values. If any of the string arguments passed is contained, it will be pulled out from the array.
Pull items from multiple arrays
To remove items from multiple array fields at the same time use the Builders.Update .Combine method to combine 2 or more update definitions.
Builders<StoreItem>.Update
.Combine(UpdateDefinition[] definitions)
The sample removes two string values from the PcGames array field and one from the XboxGames.
var storesCollection = database
.GetCollection<StoreItem>(Constants.StoreCollection);
var storeEmptyFilter = Builders<StoreItem>.Filter.Empty;
var removePcGames = new List<string> { "FIFA 20", "NBA 2K17" };
var removeXboxGames = new List<string> { "Mortal Kombat" };
// create the 1st pull definition
var pullPcGamesDefinition = Builders<StoreItem>
.Update.PullFilter(s => s.PcGames,
game => removePcGames.Contains(game));
// create the 2nd pull definition
var pullXboxGamesDefinition = Builders<StoreItem>
.Update.PullFilter(s => s.XboxGames,
game => removeXboxGames.Contains(game));
// combine the definition
var pullCombined = Builders<StoreItem>.Update
.Combine(pullPcGamesDefinition, pullXboxGamesDefinition);
var removeUpdateResult = await storesCollection
.UpdateManyAsync(storeEmptyFilter, pullCombined);
// initial data in the db
/* 1 */
{
"_id" : ObjectId("5e9f5fdb6b204f01d96a678b"),
"pcGames" : [
"Football Manager",
"DOOM Eternal",
"FIFA 20",
"Grand Theft Auto",
"NBA 2K17"
],
"xboxGames" : [
"Forza Horizon",
"Call of Duty",
"Mortal Kombat",
"Gears 5"
]
},
/* 2 */
{
"_id" : ObjectId("5e9f5fdb6b204f01d96a678c"),
"pcGames" : [
"Assassin's Creed",
"Final Fantasy",
"The Sims",
"Football Manager",
"FIFA 20"
],
"xboxGames" : [
"Resident Evil",
"Forza Motorsport",
"Battlefield",
"Halo 5 Guardians",
"Mortal Kombat"
]
}
db.stores.updateMany({}, {
"$pull": {
"pcGames": {
"$in": [
"FIFA 20",
"NBA 2K17"
]
},
"xboxGames" : {
"$in" : [
"Mortal Kombat"
]
}
}
})
----------------------------
// final result
/* 1 */
{
"_id" : ObjectId("5e9f62f94c1ea5e62f506597"),
"pcGames" : [
"Football Manager",
"DOOM Eternal",
"Grand Theft Auto"
],
"xboxGames" : [
"Forza Horizon",
"Call of Duty",
"Gears 5"
]
},
/* 2 */
{
"_id" : ObjectId("5e9f62f94c1ea5e62f506598"),
"pcGames" : [
"Assassin's Creed",
"Final Fantasy",
"The Sims",
"Football Manager"
],
"xboxGames" : [
"Resident Evil",
"Forza Motorsport",
"Battlefield",
"Halo 5 Guardians"
]
}
public class StoreItem
{
public BsonObjectId Id { get; set; }
[BsonIgnoreIfDefault]
public List<string> PcGames { get; set; }
[BsonIgnoreIfDefault]
public List<string> XboxGames { get; set; }
}
Pull embedded items from array
To remove nested documents from an array field create an UpdateDefinition
with the specified condition to be applied on the documents to be removed. Assuming T
is the type of the root document which contains an array field having E
type of documents, the syntax is the following.
var pullDefinition =
Builders<T>.Update
.PullFilter(root => root.<array-field>,
arrayDoc => condition(arrayDoc<field>));
The sample removes VisitedCountry
documents from the VisitedCountries array field of Traveler
documents based on the VisitedCountry.TimesVisited
value.
var travelersCollection = database
.GetCollection<Traveler>(Constants.TravelersCollection);
// create a filter
var visited8Times = Builders<Traveler>
.Filter.ElemMatch(t => t.VisitedCountries,
country => country.TimesVisited == 8);
var pullVisited8TimesDefinition = Builders<Traveler>.Update
.PullFilter(t => t.VisitedCountries,
country => country.TimesVisited == 8); // condition
var visited8TimesResult = await travelersCollection
.UpdateManyAsync(visited8Times, pullVisited8TimesDefinition);
var bsonTravelersCollection = tripsDatabase
.GetCollection<BsonDocument>(travelerCollectionName);
var bsonVisited9Times = Builders<BsonDocument>.Filter
.ElemMatch<BsonValue>("visitedCountries",
new BsonDocument { { "timesVisited", 9 } });
var bsonTotalDocVisited9Times = await bsonTravelersCollection
.Find(bsonVisited9Times).CountDocumentsAsync();
var bsonPullVisited9TimesDefinition = Builders<BsonDocument>.Update
.PullFilter<BsonValue>("visitedCountries",
new BsonDocument { { "timesVisited", 9 } });
var bsonVisited9TimesResult = await bsonTravelersCollection
.UpdateManyAsync(bsonVisited9Times,
bsonPullVisited9TimesDefinition);
db.travelers.updateMany(
{
"visitedCountries": {
"$elemMatch": {
"timesVisited": 8
}
},
},
{
"$pull": {
"visitedCountries": {
"timesVisited": 8
}
}
}
)
------------------------------
// sample result
{
"acknowledged" : true,
"matchedCount" : 10,
"modifiedCount" : 10
}
// sample document
{
"_id" : ObjectId("5ea073c55c436f374c4c48ae"),
"name" : "Orville White",
"age" : 55,
"activities" : [
"Golf",
"Wine tourism",
"Running",
"Geocaching",
"Snow-kiting",
"Collecting",
"Blogging"
],
"visitedCountries" : [ // docs with timesVisited = 8 removed
{
"name" : "Montenegro",
"timesVisited" : 2,
"lastDateVisited" : ISODate("2018-10-10T09:11:38.314+03:00"),
"coordinates" : {
"latitude" : 82.9026,
"longitude" : 95.4488
}
},
{
"name" : "Ecuador",
"timesVisited" : 9,
"lastDateVisited" : ISODate("2018-01-24T11:30:48.835+02:00"),
"coordinates" : {
"latitude" : 80.4824,
"longitude" : 179.1376
}
}
]
}
public class Traveler
{
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public List<string> Activities { get; set; }
public List<VisitedCountry> VisitedCountries { get; set; }
}
public class VisitedCountry
{
public string Name { get; set; }
public int TimesVisited { get; set; }
public DateTime LastDateVisited { get; set; }
public GeoLocation Coordinates { get; set; }
}
Update matched array document
The sample sets the Name value for all matched array VisitedCountry
elements having Name = Greece and TimesVisited = 3.
var travelersCollection = database
.GetCollection<Traveler>(Constants.TravelersCollection);
// create an elemMatch operator
var visitedGreeceExactly3Times = Builders<Traveler>.Filter
.ElemMatch(t => t.VisitedCountries,
country => country.Name == "Greece"
&& country.TimesVisited == 3);
// create the update definition
var updateDefinition = Builders<Traveler>.Update
.Set(t => t.VisitedCountries[-1].Name, "Hellas");
// this will update only the first matching array element!
// ($) refers to the first match
var updateHellasResult = await travelersCollection
.UpdateManyAsync(visitedGreeceExactly3Times,
updateDefinition);
var bsonTravelersCollection = tripsDatabase
.GetCollection<BsonDocument>(travelerCollectionName);
var bsonVisitedGreeceExactly3Times = Builders<BsonDocument>.Filter
.ElemMatch<BsonValue>("visitedCountries",
new BsonDocument { { "name", "Greece" },
{ "timesVisited", 3 } });
var bsonUpdateDefinition = Builders<BsonDocument>
.Update.Set("visitedCountries.$.name", "Hellas");
var bsonUpdateHellasResult = await bsonTravelersCollection
.UpdateManyAsync(bsonVisitedGreeceExactly3Times,
bsonUpdateDefinition);
db.travelers.updateMany(
{
visitedCountries:
{
$elemMatch:
{ name: "Greece", timesVisited: 3 }
}
},
{ $set: { "visitedCountries.$.name": "Hellas" } }
)
------------------------------
// sample result
{
"acknowledged" : true,
"matchedCount" : 1,
"modifiedCount" : 1
}
// sample document
{
"_id" : ObjectId("5ea073c55c436f374c4c48ae"),
"name" : "Orville White",
"age" : 55,
"activities" : [
"Golf",
"Wine tourism",
"Running",
"Geocaching",
"Snow-kiting",
"Collecting",
"Blogging"
],
"visitedCountries" : [
{
"name" : "Hellas", // updated from "Greece"
"timesVisited" : 3,
"lastDateVisited" : ISODate("2018-10-10T09:11:38.314+03:00"),
"coordinates" : {
"latitude" : 82.9026,
"longitude" : 95.4488
}
},
{
"name" : "Ecuador",
"timesVisited" : 9,
"lastDateVisited" : ISODate("2018-01-24T11:30:48.835+02:00"),
"coordinates" : {
"latitude" : 80.4824,
"longitude" : 179.1376
}
}
]
}
public class Traveler
{
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public List<string> Activities { get; set; }
public List<VisitedCountry> VisitedCountries { get; set; }
}
public class VisitedCountry
{
public string Name { get; set; }
public int TimesVisited { get; set; }
public DateTime LastDateVisited { get; set; }
public GeoLocation Coordinates { get; set; }
}
Normally, based on the $elemMatch operator, the $set operator should have been applied to the root document(s) but the $ position operator causes the update to be applied on the matched document instead
Update multiple elements
To update multiple array elements that match a specified condition, you need to use one or more ArrayFilterDefinition
filters as the 3rd argument in the UpdateOne
or UpdateMany
methods.
Assume that you have a post which contains a list of comments.
{
"_id" : ObjectId("5ea5f66430343616602d6bfe"),
"author" : "chsakell",
"content" : "hello world",
"comments" : [
{
"text" : "comment 1",
"author" : "author 1",
"votes" : 1,
"hidden" : false
},
{
"text" : "comment 2",
"author" : "author 2",
"votes" : 2,
"hidden" : false
},
{
"text" : "comment 3",
"author" : "author 3",
"votes" : 3,
"hidden" : false
},
{
"text" : "comment 4",
"author" : "author 4",
"votes" : 4,
"hidden" : false
}
]
}
The sample hides the comments that have votes greater or equal to 3.
var update = Builders<BsonDocument>.Update
.Set("comments.$[elem].hidden", true);
var updateResult = await bsonPostsCollection.UpdateOneAsync(
Builders<BsonDocument>.Filter.Eq("_id", post.Id), update,
new UpdateOptions()
{
ArrayFilters = new List<ArrayFilterDefinition<BsonValue>>()
{
new BsonDocument("elem.votes",
new BsonDocument("$gte", 3))
}
});
db.posts.updateOne(
{"_id" : ObjectId("5ea5f66430343616602d6bfe") },
{ $set: { "comments.$[elem].hidden" : true } },
{
arrayFilters: [ { "elem.votes": { $gte: 3 } } ]
}
)
------------------------
// document after update
{
"_id" : ObjectId("5ea5f66430343616602d6bfe"),
"author" : "chsakell",
"content" : "hello world",
"comments" : [
{
"text" : "comment 1",
"author" : "author 1",
"votes" : 1,
"hidden" : false
},
{
"text" : "comment 2",
"author" : "author 2",
"votes" : 2,
"hidden" : false
},
{
"text" : "comment 3",
"author" : "author 3",
"votes" : 3, // matched here
"hidden" : true // updated
},
{
"text" : "comment 4",
"author" : "author 4",
"votes" : 4, // matched here
"hidden" : true // updated
}
]
}
public class Post
{
public ObjectId Id { get; set; }
public string Author { get; set; }
public string Content { get; set; }
public List<Comment> Comments { get; set; }
}
public class Comment
{
public string Text { get; set; }
public string Author { get; set; }
public int Votes { get; set; }
public bool Hidden { get; set; }
}
There update operation has 3 distinct parts:
The filter definition - in the sample it's a specific post matched by its _id but it could be anything
The update definition - the sample defines that the matched elements in the comments array field should set their hidden value to true. This is defined using the $ positional operator and the elem identifier for each matching document
The ArrayFilters which defines how elem array elements are matched, in other words, which are the elements to be updated
Last updated