Update with aggregation pipeline in C# .NET

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.

Document looks like this:

{
  _id: ...,
  primary: {
    /* fields */
    updatedAt: "2022-03-24T00:00:00.000Z"
  },
  secondary: {
    /* fields */
    updatedAt: "2022-01-01T00:00:00.000Z"
  }
}

This is MongoDB query which does what I want:

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?

Hi, @Zdenek_Havlin,

Welcome to the MongoDB Community Forums.

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.

Sincerely,
James

Hi @James_Kovacs ! Thanks, that worked like a charm :slight_smile: 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).

Once again, thank you for your help!

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.