Using NuGet MongoDB.Driver version 2.14.0, I’m trying to perform update of document - copying one field (primary) to another (secondary) and then updating copied field value.
db.templates.updateOne(
{ _id: ... },
[
/* copy `primary` to `secondary` */
{ $set: { secondary: "$primary" } },
/* update `updatedAt` at `secondary` with current time */
{ $set: { "secondary.updatedAt": "2022-03-24T00:00:01.000Z" } }
])
(… at least I hope this query will work as expected, not sure whether I should worry about race condition)
Now I want to achieve this from .NET (5), but I can’t find any article, example nor unit test demonstrating how to do it.
I have vague feeling I need to use EmptyPipelineDefinition<> but I can’t figure out how; it is also preferable to use my entity avoiding writing BsonDocument manually - which could be error prone (having field names in code in strings).
Could anyone point me to proper place where I can get more details?
I understand that you are trying to compose an update operation that involves setting one field in a document to the value of another field in the same document. In C#, this would look like:
var update = Builders<Article>.Update.Set(x => x.Secondary, x => x.Primary); // will not compile
While this is possible in MongoDB update operations, we have not implemented support yet for this in the .NET/C# driver. See CSHARP-3677 for this feature request.
In the meantime, you can implement this using EmptyPipelineDefinition<> as you suspected. After creating an EmptyPipelineDefinition<>, then call AppendStage stage for each update operation that you want to perform. You can specify these stages using BsonDocument objects or a string containing valid JSON. Note that I had to use Extended JSON v1 Syntax to ensure that the date value was not converted to a string during the update operation.
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
var client = new MongoClient();
var db = client.GetDatabase("test");
var coll = db.GetCollection<Article>("articles");
var filter = Builders<Article>.Filter.Empty; // actual filter goes here
var pipeline = new EmptyPipelineDefinition<Article>()
.AppendStage<Article, Article, Article>("{ $set: { secondary: '$primary' } }")
.AppendStage<Article, Article, Article>($"{{ $set: {{ 'secondary.updatedAt': {{$date: '{DateTime.UtcNow}' }} }} }}");
var update = Builders<Article>.Update.Pipeline(pipeline);
coll.UpdateOne(filter, update);
class Article
{
public ObjectId Id { get; set; }
[BsonElement("primary")]
public Entry Primary { get; set; }
[BsonElement("secondary")]
public Entry Secondary { get; set; }
}
class Entry
{
public DateTime UpdatedAt { get; set; }
};
Please let me know if you have any additional questions and be sure to watch/vote for CSHARP-3677 if it would be of use to you.
Hi @James_Kovacs ! Thanks, that worked like a charm I was bit confused by that JSON string input - made it to BsonDocument in my code (it’s bit longer but probably not so controversial to my fellow .NET developers).