Determining full mongo property name (path) from [BsonElement]

As far I’m aware, there’s no way how to use higher level API to write MongoDB aggregation pipeline (using entity types with generics) - so when I use aggregation pipeline, I have to manually build BsonDocuments representing pipeline, e.g.:

var copyAtoB = new BsonDocument { ["$set"] = new BsonDocument { ["B"] = "$A" } };
var setAudit = new BsonDocument
{
    ["$set"] = new BsonDocument
    {
        ["props.audit.updatedBy"] = new BsonString("me"),
        ["props.audit.updatedTimestamp"] = new BsonDateTime(DateTime.UtcNow)
    }
};

var pipeline = new EmptyPipelineDefinition<LeEntity>()
    .AppendStage<LeEntity, LeEntity, LeEntity>(copyAtoB)
    .AppendStage<LeEntity, LeEntity, LeEntity>(setAudit);

var update = Builders<LeEntity>.Update.Pipeline(pipeline);

while entity may looks something like:

class LeEntity
{
    // ... some other props here

    [BsonElement("A")]
    public string Value { get; init; } = null!;

    [BsonElement("B")]
    public string DefaultValue { get; init; } = null!;

    [BsonElement("props")]
    public EntityProps Props { get; init; } = null!;
}

class EntityProps
{
    [BsonElement("audit")]
    public AuditDetails AuditDetails { get; init; } = null!;
}

class AuditDetails
{
    [BsonElement("updatedBy")]
    public string UpdatedBy { get; init; } = null!;

    [BsonElement("updatedTimestamp")]
    public DateTime LastUpdate { get; init; }
}

Is there any helper method which could help me generate values such as "B", "$A" or "props.audit.updatedBy" using my entity type? (effectively reading BsonElement attributes and generating full path)

Hi, @Zdenek_Havlin,

Welcome back to the MongoDB Community Forums.

I understand you are trying to find the serialized names for your entities’ fields. The mapping of C# properties/fields to database field names is handled by IBsonSerializer<T> classes, which are registered in the global BsonSerializerRegistry.

using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;

var serializer = BsonSerializer.LookupSerializer<Person>() as IBsonDocumentSerializer;
serializer.TryGetMemberSerializationInfo("Name", out var serializationInfo);
Console.WriteLine(serializationInfo.ElementName); // writes "nm"

class Person
{
    public ObjectId Id { get; set; }
    [BsonElement("nm")]
    public string Name { get; set; }
}

While the registered serializer for a type will correctly map the C# property/field name to the database field name including any conventions, mapping attributes, and custom class maps, it would be up to you to properly traverse the object graph and build up a dotted path. Note that field names can now contain dots themselves and thus you may need to use $getField if your application uses dots in database field names.

You mention that you are doing this as you have to handroll your aggregation pipelines using BsonDocuments. I find this surprising. In the vast majority of cases, you should be able to write aggregations either using Fluent Aggregate (coll.Aggregate()) or LINQ (coll.AsQueryable()) where coll is of type IMongoCollection<Person>.

var client = new MongoClient();
var db = client.GetDatabase("test");
var coll = db.GetCollection<Person>("people");

var query1 = coll.Aggregate().Match(x => x.Name == "James");
var query2 = coll.AsQueryable().Where(x => x.Name == "James");
var query3 = from p in coll.AsQueryable()
             where p.Name == "James"
             select p; 

Rather than handrolling your aggregation pipelines, I would suggest troubleshooting why your class model cannot be serialized. Feel free to share a self-contained repro of the serialization problem so we can better understand the problem that you’ve encountered.

Sincerely,
James

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