Aggregate().Project not using BsonElement names

Greeting,
I’m using the latest C# driver (version 2.14.1) and I’m having trouble with the aggregation pipeline, when I add a projection to a class with BsonElement defined names.

While AsQueryable() correctly uses the overridden names, Project() doesn’t, thus the returned data do not get deserialized to the respective fields.

Here is a small snippet to reproduce:

public class TestClass
{
  [BsonElement("_p")]
  public string Property { get; set; }
}

The following code:

collection.Aggregate()
  .Project(x => new TestClass { Property = x.Property.ToUpper() 
});

will produce this query:

"aggregate([{ \"$project\" : { \"Property\" : { \"$toUpper\" : \"$_p\" }, \"_id\" : 0 } }])"

While this code:

var queryable = collection.AsQueryable()
  .Select(x => new TestClass{ Property = x.Property.ToUpper() 
});

will produce the correct query:

"test.test.Aggregate([{ \"$project\" : { \"_p\" : { \"$toUpper\" : \"$_p\" }, \"_id\" : 0 } }])"

I also tried using ProjectionDefinitionBuilder<TestClass> but - unless I’m using it wrong - it seems limited and resides to simple _p: 1 leaving the actual projection at client-side.

collection.Aggregate()
  .Project(
    Builders<TestClass>.Projection
      .Expression(x => new TestClass { Property = x.Property.ToUpper()
}));

with result:

aggregate([{ \"$project\" : { \"_p\" : 1, \"_id\" : 0 } }])

Is this a bug? Or is there a way to have the excepted query and results using the aggregation pipeline?

Just to add to my post, the problem is present also with any BsonId property, which gets automatically assigned the element name _id.
If one projects to such class, the projection generated by the Project stage will be:

"$project" : { "Id" : "$_id" }

which means that the receiving object won’t get the value.

Hello @adas ,
Please check this behavior with LINQ3 provider: LINQ3

I’ve been testing the above inside the latest source code, with unit tests, and I was already using V3 (DriverTestConfiguration.Linq3Client). I checked V2 and the results are the same.

BUT, I’ve dug a bit further and it appears that the queries show above (produced by the aggregation pipeline’s ToString()) go through a final transformation when the client actually makes the call.

So for example debugging a call to ToList() renders the pipeline as:

{$project={ "_p" : { "$toUpper" : "$_p" }, "_id" : 0 }}

This corrective action only happens with V3.
In V2 that final transformation never happens.

I hope this helps.

This query:

is handled correctly with LINQ3 and produces the following MQL request:

{
    "aggregate": "coll",
    "pipeline": [{
            "$project": {
                "_p": {
                    "$toUpper": "$_p"
                },
                "_id": 0
            }
        }
    ],
    "cursor": {}
}

The issue with this query:

collection.Aggregate()
  .Project(
    Builders<TestClass>.Projection
      .Expression(x => new TestClass { Property = x.Property.ToUpper()
}));

looks like a bug to me, would you mind to create a jira ticket with this description?

Also I’m not sure that I understood your question about auto mapping Id to _id correctly, but if you want to avoid this auto mapping, you can mark that field by BsonNoIdAttribute

Sure I’ll open a ticket.

Regarding the _id, I only wanted to stress-out that this affects even built-in mappings, and not only custom set using BsonElement, since internally it also assigns an ElementName.