I want to sort a list. When it was IEnumerable it was working fine, but I changed it to IQueryable to query MongoDB, it does not work. Giving me this error
System.NotSupportedException: ‘Only fields are allowed in a $sort.’
//Query 1
var query = from d in list
orderby
d.Item.Value1 + d.Item.Value2 descending
select d;
//Query 2
var query = from d in list
orderby
RecommendationFormula(d) descending
select d;
private double RecommendationFormula(IItem d)
{
var quality = ((int)(d.ValueX) / 18.0) * 50.0;
var recent = ((DateTime.MinValue - (d.Item.Released ?? DateTime.MinValue)).TotalDays / (DateTime.MinValue - new DateTime(1990, 1, 1)).TotalDays) * 30;
var rating = ((d.Item.XRating + d.Item.YRating) / 20) * 15;
var quantity = (Quantity(d) / 1000.0) * 5;
return quality + rating + recent + quantity;
}
I also understand that it does not support functions(as shown in Query 2),but Query 1 also gives the error, when I try ToList() the list.
How can I write this kind of complex sort for IQueryable list?
4
Answers
I was able to sort the issue, in a different way, but avoided bringing the objects to memory for filter and sorting. But it does take 2 queries to MongoDB.
I believe the result you are looking for can be achieved by using LINQ lambdas
I believe you can also write it as the following to convert the lambda into a method group but please correct me if I’m wrong:
It’s about the way MongoDB driver works. Can you please cast the
IQueryable
to a list withToList()
just before using it? It will add an overhead since it needs to load all the elements in the query to memory first.If the data you are using will create a headache due to the volume, I would suggest either solving it in the database or process the data in memory in chunks, e.g. using
Skip()
&Take()
.P.S: The method
RecommendationFormula(IItem d)
can be a static method.As it’s stated in the exception, the typed way to configure sorting in LINQ supports only fields and not calculated values. It looks like it’s a server restriction based on this. So it can be solved by adding projection:
that triggers this MQL:
you can add more complex logic there, but don’t expect anything too complicated, in your particular case, supporting for Timespan properties will be triggering an unsupported exception, I’m not sure whether server even has equivalent for Timespan logic in .net.
If you still has to look at more complex scenarios that will trigger an unsupported exception with typed LINQ queries, you have 2 options:
UPDATE:
It looks like
$substract
is supported by LINQ2:but it cannot extract
TotalDays
and similar properties. However it looks the server stages support it via 2 operators:$substact
.Idea is to create a raw MQL request that you can check in the shell similar to:
and then pass each stage separately via
AppendStage
method.NOTE: you can combine typed stages(if it’s supported) and stages with raw MQL similar to:
The generated MQL for the above LINQ query will be:
UPDATE2
if fields in your projected output document match to fields in your input document, you can avoid reprojecting (ie additional server side step) and instead just replace the output serializer on the client side via
As
:The generated MQL for the above case will be: