[CSHARP-4562] SortBy failure for dictionary entry in LinqProvider.V3 Created: 07/Mar/23  Updated: 28/Oct/23  Resolved: 17/Mar/23

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

Type: Bug Priority: Unknown
Reporter: Mat Filby Assignee: Robert Stam
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
is duplicated by CSHARP-4569 LINQ3 support for bracket notation on... Closed
is duplicated by CSHARP-4577 LINQ3 provider throws ExpressionNotSu... Closed
Related
related to CSHARP-4569 LINQ3 support for bracket notation on... Closed
related to CSHARP-4574 Check whether we need to call Evaluat... Closed
Documentation Changes: Not Needed
Documentation Changes Summary:

1. What would you like to communicate to the user about this feature?
2. Would you like the user to see examples of the syntax and/or executable code and its output?
3. Which versions of the driver/connector does this apply to?


 Description   

Summary

An exception is thrown when performing an aggregate query containing a SortBy(), whose expression is a dictionary entry.

  • This query works correctly with the V2 LinqProvider
  • The exception is: MongoDB.Driver.Linq.ExpressionNotSupportedException "Expression must be a constant"
  • The exception doesn't occur if the dictionary key is hard-coded to a constant value
  • Tested with MongoDB.Driver v2.19.0
  • I suspect this will also affect array indexes, as well as IndexKeysDefinitions

How to Reproduce

class Translation
{
    public int Id { get; set; }
    public Dictionary<string, string>? Text { get; set; }
}
 
static List<Translation> GetSortedTranslations(IMongoCollection<Translation> collection, string language)
{
    return collection.Aggregate()
        .SortBy(c => c.Text[language]) // This works in LinqProvider.V2, but not V3
        .ToList();
}
 
static List<Translation> GetTranslationsSortedByEnglish(IMongoCollection<Translation> collection)
{
    return collection.Aggregate()
        .SortBy(c => c.Text["en"]) // This works
        .ToList();
} 

Additional Background

The exception occurs when GetItemMethodToFilterFieldTranslator.TranslateWithStringIndex() calls indexExpression.GetConstantValue<string>(). GetConstantValue expects the expression to be of type ConstantExpression, whereas it is given a MemberAccess of a ConstantExpression. We may want to consider allowing partial execution, eg:

 

public static TValue GetConstantValue<TValue>(this Expression expression, Expression containingExpression)
{
    if (expression is ConstantExpression constantExpression)
    {
        return (TValue)constantExpression.Value;
    }
 
    LambdaExpression lambda = Expression.Lambda(expression);
    Delegate fn = lambda.Compile();
    return (TValue)fn.DynamicInvoke(null);
} 

 

 



 Comments   
Comment by Githook User [ 24/Mar/23 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-4562: Use PartialEvaluator when translating an Expression to a field.
Branch: v2.19.x
https://github.com/mongodb/mongo-csharp-driver/commit/4643231695f69ed8ad951771c24bb651d10ceea1

Comment by Mat Filby [ 18/Mar/23 ]

Thanks for resolving this issue!

Comment by Githook User [ 17/Mar/23 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-4562: Use PartialEvaluator when translating an Expression to a field.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/0ce403f5aea3013c4e49504841a89de0a32615c2

Comment by Robert Stam [ 16/Mar/23 ]

I was able to reproduce this and you are correct that it is a result of not partially evaluating.

I have a fix in code review which involves calling the PartialEvaluator, but at a much higher level than your proposal.

Thanks again for reporting this and for the clear instructions on how to reproduce it.

Comment by Robert Stam [ 07/Mar/23 ]

Thanks for reporting this. I will investigate further before long.

Comment by Mat Filby [ 07/Mar/23 ]

I've confirmed that this issue also affects the creation of indexes (but again, only fails in LinqProvider.V3, not V2).

static void CreateIndex(IMongoCollection<Translation> collection, string language)
{
    collection.Indexes.CreateOne(
        new CreateIndexModel<Translation>(
            Builders<Translation>.IndexKeys.Ascending(c => c.Text[language])));
    // In LinqProvider V3, this throws: MongoDB.Driver.Linq.ExpressionNotSupportedException
} 

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