MongoDB · UpdateMultiple
Update or delete every document a filter matches
Every document a filter matches, changed or deleted in one call: one updateMany (or deleteMany) with the filter as a query document, in a transaction, so a document refused half-way leaves every document as it was.
Set up for this page
The scenarios build on the sample application: SampleContext.cs (the dbContext every sample is given); SampleOutput.cs (Step and Show, which print); SampleContext.Application.cs (dbContext.Service, the application's ARX service); SampleCatalog.cs (the tables' ids, keys, lookups and display names); SampleContext.Direct.cs (dbContext.Direct); MongoDirectHelper.cs (the checks behind dbContext.Direct).
dbContext.Direct is the samples' own checking code on the plain driver, not part of ARX: leave it out of your application.
Before scenario 1 the sample runs this, so the service and the rows the scenarios use are there:
var accounts = new List<ArxEntity>();
for (var i = 1; i <= 5; i++)
{
var account = new ArxEntity("account");
account["name"] = $"Filter update {i}";
account["account_number"] = $"ACC-MF0{i}";
account["employee_count"] = i * 5;
account["status"] = "ACTIVE";
accounts.Add(account);
}
await service.CreateMultipleAsync(accounts);1 Update by a filter
Small accounts put on hold with one updateMany.
Already in scope: service (ArxMongoDbService)
var change = new ArxEntity("account");
change["status"] = "ON_HOLD";
var filter = new ArxFilterExpression();
filter.AddCondition("account_number", ArxConditionOperator.BeginsWith, "ACC-MF0");
filter.AddCondition("employee_count", ArxConditionOperator.LessThan, 15);
int changed = await service.UpdateMultipleAsync(change, filter);
Console.WriteLine($"{changed} accounts put on hold");Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.account.find({ account_number: /^ACC-MF0/ }, { account_number: 1, employee_count: 1, status: 1 }).sort({ account_number: 1 })Expect: 5 documents; ACC-MF01 and ACC-MF02 (5 and 10 employees) ON_HOLD, the other three ACTIVE.
2 Delete by a filter
The accounts on hold deleted with one deleteMany; the call returns how many went.
Already in scope: service (ArxMongoDbService)
var filter = new ArxFilterExpression();
filter.AddCondition("account_number", ArxConditionOperator.BeginsWith, "ACC-MF0");
filter.AddCondition("status", ArxConditionOperator.Equal, "ON_HOLD");
int deleted = await service.DeleteMultipleAsync("account", filter);
Console.WriteLine($"{deleted} accounts deleted");Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.account.find({ account_number: /^ACC-MF0/ }, { account_number: 1, status: 1 }).sort({ account_number: 1 })Expect: 3 documents, ACC-MF03 to ACC-MF05, all ACTIVE.
Good to know
The filter takes the operators of a query on the collection's own fields; values are never read as query operators.
The entity carries only the values to set: an id or _id is refused (ARX1001), and so is an empty filter unless new ArxFilterWriteOptions { AllRows = true } is passed.
Needs ARX.SDK 0.1.0-preview.5 or later, and a replica set (one node is enough).
Run the complete sample
The code above comes from one program, built in a fresh project and run against the database. To run it yourself, here is every file and step.
The program, its files and how to run it
Run it locally
- .NET SDK: .NET SDK 10.0.100 or later 10.0.x (target
net10.0). - Create the project:
Terminal dotnet new console -n ArxSample -f net10.0 cd ArxSample - Install the packages:
Terminal dotnet add package ARX.SDK.MongoDb --version 1.0.0-rc.1 - Database: a MongoDB 7.0, a replica set (tested with 7.0.45, one node) database with the sample schema.
- Configuration: set
ARX_SAMPLES_MONGODBto its connection string. Nothing is hard-coded. - Source code: add the files of the sample application this sample uses (
SampleContext.cs,SampleOutput.cs,SampleContext.Application.cs,SampleCatalog.cs,SampleContext.Direct.cs,MongoDirectHelper.cs), then replaceProgram.csand add the other file below, with the file names shown. - Run it:
Terminal dotnet run - Expected output: below.
The files
Runs the sample. Generated for this page; it reads the connection string from the environment and first remakes the sample database from sample-schema.json.
using ArxSamples.MongoDb;
using ArxSamples.MongoDb.Operations.UpdateMultiple.Basics;
// The connection string comes from the environment, never from the code (see "Configure the connection").
var connectionString = Environment.GetEnvironmentVariable("ARX_SAMPLES_MONGODB")
?? throw new InvalidOperationException("Set the ARX_SAMPLES_MONGODB environment variable to your MongoDB connection string.");
var dbContext = new SampleContext(connectionString);
// Every run starts from the sample data: this deletes and makes again what sample-schema.json lists
// (save the file in the project folder, see Get started).
await dbContext.Direct.ResetAsync();
await new Basic2_ByAFilter().RunAsync(dbContext);
The sample, as it is in the repository.
using ARX.SDK;
using ARX.SDK.MongoDb.Services;
using ARX.SDK.Query;
using ARX.SDK.Services;
using static ArxSamples.MongoDb.SampleOutput;
namespace ArxSamples.MongoDb.Operations.UpdateMultiple.Basics;
/// <summary>
/// Every document a filter matches, changed or deleted in one call: one updateMany (or deleteMany) with the filter as a
/// query document, in a transaction, so a document refused half-way leaves every document as it was.
/// </summary>
/// <remarks>
/// <para>The filter takes the operators of a query on the collection's own fields; values are never read as query operators.</para>
/// <para>The entity carries only the values to set: an id or _id is refused (ARX1001), and so is an empty filter unless new ArxFilterWriteOptions { AllRows = true } is passed.</para>
/// <para>Needs ARX.SDK 0.1.0-preview.5 or later, and a replica set (one node is enough).</para>
/// </remarks>
public sealed class Basic2_ByAFilter : ISample
{
public string Name => "update-multiple-basics-2-by-a-filter";
public string Title => "Update or delete every document a filter matches";
public async Task RunAsync(SampleContext dbContext)
{
var accounts = new List<ArxEntity>();
for (var i = 1; i <= 5; i++)
{
var account = new ArxEntity("account");
account["name"] = $"Filter update {i}";
account["account_number"] = $"ACC-MF0{i}";
account["employee_count"] = i * 5;
account["status"] = "ACTIVE";
accounts.Add(account);
}
await dbContext.Service.CreateMultipleAsync(accounts);
await Scenario1_UpdateByAFilterAsync(dbContext.Service);
await Scenario2_DeleteByAFilterAsync(dbContext.Service);
}
/// <summary>1. Small accounts put on hold with one updateMany.</summary>
private static async Task Scenario1_UpdateByAFilterAsync(ArxMongoDbService service)
{
Step("1. Update by a filter");
var change = new ArxEntity("account");
change["status"] = "ON_HOLD";
var filter = new ArxFilterExpression();
filter.AddCondition("account_number", ArxConditionOperator.BeginsWith, "ACC-MF0");
filter.AddCondition("employee_count", ArxConditionOperator.LessThan, 15);
int changed = await service.UpdateMultipleAsync(change, filter);
Show($"{changed} accounts put on hold");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.account.find({ account_number: /^ACC-MF0/ }, { account_number: 1, employee_count: 1, status: 1 }).sort({ account_number: 1 })
* -- Expect: 5 documents; ACC-MF01 and ACC-MF02 (5 and 10 employees) ON_HOLD, the other three ACTIVE.
* ===========================================================
*/
}
/// <summary>2. The accounts on hold deleted with one deleteMany; the call returns how many went.</summary>
private static async Task Scenario2_DeleteByAFilterAsync(ArxMongoDbService service)
{
Step("2. Delete by a filter");
var filter = new ArxFilterExpression();
filter.AddCondition("account_number", ArxConditionOperator.BeginsWith, "ACC-MF0");
filter.AddCondition("status", ArxConditionOperator.Equal, "ON_HOLD");
int deleted = await service.DeleteMultipleAsync("account", filter);
Show($"{deleted} accounts deleted");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.account.find({ account_number: /^ACC-MF0/ }, { account_number: 1, status: 1 }).sort({ account_number: 1 })
* -- Expect: 3 documents, ACC-MF03 to ACC-MF05, all ACTIVE.
* ===========================================================
*/
}
}
Expected output
What dotnet run printed when the sample was verified, on a freshly created sample schema. Ids differ when the tables already hold rows.
> 1. Update by a filter
2 accounts put on hold
> 2. Delete by a filter
2 accounts deleted