[CSHARP-4930] ExpressionNotSupportedException thrown when .First() is used with a predicate when building SortDefinition Created: 12/Jan/24  Updated: 30/Jan/24  Resolved: 30/Jan/24

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

Type: Bug Priority: Unknown
Reporter: Rana Ahmed Assignee: Robert Stam
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

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

ExpressionNotSupportedException thrown when .First() is used with a predicate when building SortDefinition.

Using 2.19.1 driver with 5.0.23 server.

How to Reproduce

SortDefinition<Order> sortDefinition1 = Builders<Order>.Sort.Ascending(c => c.Items.First(x => x.Type == "XYZ").Rate);
SortDefinition<Order> sortDefinition2 = Builders<Order>.Sort.Ascending(c => c.Items.First().Rate);

The first one throws an exception when executed but the second one works as expected.



 Comments   
Comment by Rana Ahmed [ 23/Jan/24 ]

This provide me an alternate, so this ticket can be closed.

Comment by Robert Stam [ 23/Jan/24 ]

All LINQ queries translate to aggregation pipelines. The example above translates to the following pipeline:

 

{ $project : { 
    Document : '$$ROOT', 
    Key : { $let : { 
        vars : { this : { $arrayElemAt : [{ $filter : { input : '$Items', as : 'x', cond : { $eq : ['$$x.Type', 'XYZ'] } } }, 0] } }, 
        in : '$$this.Rate' } },
    _id : 0 } },
{ $sort : { Key : 1 } },
{ $project : { _v : '$Document', _id : 0 } }

I'm not sure what will happen if Items does not contain a matching item. If that's a possibility you might need to write more code to handle that.

 

Comment by Rana Ahmed [ 23/Jan/24 ]

Thanks robert@mongodb.com for the update and the interesting alternate solution. Does this translate to the addFeilds stage on the server side? Just trying to understand how the server will execute this.

Comment by Robert Stam [ 23/Jan/24 ]

The first throws an `ExpressionNotSupportedException` because the expression is too complicated to be translated to something that the server supports.

The server only supports sorting on a field, and not on a computed expression. Therefore, the `Expression` passed to `Ascending` must specify a field.

The second one works because the server allows array items to be referenced as pseudo-fields, where the name of the pseudo-field is the index in the array.

So `Builders<Order>.Sort.Ascending(c => c.Items.First().Rate)`  is translated to `{ "Items.0.Rate" : 1 }`.

You can work around this limitation in the server by assigning the result of the computation you want to sort on to a field. For example, using LINQ you could sort on your computed value this way:

var queryable = collection.AsQueryable()
    .Select(x => new { Document = x, Key = x.Items.First(x => x.Type == "XYZ").Rate })
    .OrderBy(x => x.Key)
    .Select(x => x.Document); 

The server documentation for the `$sort` stage is here:

https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/

Comment by PM Bot [ 12/Jan/24 ]

Hi ranasahmed@outlook.com, thank you for reporting this issue! The team will look into it and get back to you soon.

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