[CSHARP-3067] $convert (aggregation) Created: 18/Apr/20  Updated: 28/Oct/23  Resolved: 16/Feb/22

Status: Closed
Project: C# Driver
Component/s: Linq
Affects Version/s: None
Fix Version/s: 2.14.0

Type: Bug Priority: Major - P3
Reporter: Christos Sakellarios Assignee: Unassigned
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
duplicates CSHARP-2924 Consider adding conversion logic into... Closed
Epic Link: CSHARP-3615

 Description   

Is $convert aggregation supported at any way by the C# driver?

I can't find a way to cast values other than using BsonDocument. 

Example:

var excercice1Aggregate = collection.Aggregate()
    .Match(Builders<User>.Filter.Gte(u => u.Salary, 1500) &
           Builders<User>.Filter.Lte(u => u.Salary, 3000))
    .Group(u => u.Gender,
        ac => new
        {
            gender = ac.Key,
            averageMonthlyExpenses = ac.Average(u => u.MonthlyExpenses), // needs to convert
            total = ac.Sum(u => 1)
        })

User.MonthlyExpense_s is an integer but ac.Average returns decimal which results in strange numbers. So either during grouping or at least on a next step, I need to convert the _averageMonthlyExpenses to an integer.

If I add a new Project stage to convert the averageMonthlyExpenses to Int, using any type of cast the driver throws an exception.

Is there any way to make the cast?

 

 



 Comments   
Comment by James Kovacs [ 16/Feb/22 ]

This issue has been fixed in the new LINQ provider (known as LINQ3), which is included in the 2.14 release. Although this is a Fluent Aggregate expression, we use the configured LINQ provider to support Expressions in Fluent Aggregate. If you add a cast to your group expression similar to the following:

var query = coll.Aggregate()
    .Match(Builders<User>.Filter.Gte(u => u.Salary, 1500) &
           Builders<User>.Filter.Lte(u => u.Salary, 3000))
    .Group(u => u.Gender,
        ac => new
        {
            gender = ac.Key,
            averageMonthlyExpenses = (int)ac.Average(u => u.MonthlyExpenses), // needs to convert
            total = ac.Sum(u => 1)
    });

This will add a $project stage with a $convert after the $group stage similar to the following:

aggregate([{ "$match" : { "Salary" : { "$gte" : 1500, "$lte" : 3000 } } }, { "$group" : { "_id" : "$Gender", "__agg0" : { "$avg" : "$MonthlyExpenses" }, "__agg1" : { "$sum" : 1 } } }, { "$project" : { "gender" : "$_id", "averageMonthlyExpenses" : { "$convert" : { "input" : "$__agg0", "to" : "int" } }, "total" : "$__agg1", "_id" : 0 } }])

Configure your MongoClientSettings to use LinqProvider.V3 if you want to use this functionality.

To configure a client to use the LINQ3 provider use code like the following

var connectionString = "mongodb://localhost";
var clientSettings = MongoClientSettings.FromConnectionString(connectionString);
clientSettings.LinqProvider = LinqProvider.V3;
var client = new MongoClient(clientSettings);

Generated at Wed Feb 07 21:44:16 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.