Sorry
Sorry, think I got it wrong, thought you needed the highest one per document. I went wrong when you wrote: “items sorted by most highly rated”. So I expected that you wanted as output:
{
"name": "Some Item",
"ratings": [
{ "userId": "abc123", "rating": 5 },
]
}
Here is an example of that:
https://docs.mongodb.com/manual/reference/operator/projection/elemMatch/
Note that in Meteor you would need to put it in the fields object, the example itself won’t work:
Items.find( { },
{ fields: { students: { $elemMatch: { school: 102 } } } } )
Now I understand you need the average of the ratings first. And work with that value.
To your question, I think you want this:
- Get average of rating, so 5+3 = 8 / 2 = 4, correct?
- Then sort by that value
Aggregate / projection
Here is an example:
Not 100% sure but likely it does not work in MiniMongo on the client.
Practical
In Mongo I would go for this by just storing that average in a field, so you get:
{
"name": "Some Item",
"ratings": [
{ "userId": "abc123", "rating": 5 },
{ "userId": "abc456", "rating": 3 },
],
"averageRating": 4,
}
Then it all becomes easy off course for sorting etcetera. You can update that field in your code where you insert/update this document to keep it up to date.
You can also use the aggregations etc. as @robfallows mentions, that’s also a nice technique. In this case, when you re-use this value, I would store it though since it makes your code very simple. Calculating the average on insert is simple. And it’s a single document so there are no relations which might modify the average.
And it’s very simple to test, easier than having to test your publication as you can do this in a closed unit. The logic is in one place, the rest of the code for sorting is all very simple to understand.
Performance
Also on performance will be fine because only when there is an update to the ratings you need to calculate. When you use an aggregation it will be calculating constantly. This will be a one time calculation, store it, and done.