Reputation: 907
I have a collection Cats
:
{
"_id" : "rRq76LxsnPfmuh9DD",
"register_id" : "20gnr3g",
"name" : "Meow",
"created_at": ...,
...
}
Every item in this collection has unique _id
and unique register_id
.
I also have RatingLog
for the cats and now I want to sort cats by that rating. From the RatingLog
we can get array of cat register_id
s ordered by rating:
[
"20gnr3g",
"3412r23",
"221n415",
"Q0g4rEg",
...
]
Now I want to use that array for sorting the Cats
collection. Note that all entries in Cats
may not have been rated, but all entries should be returned: First in RatingLog
order and rest items by created_at
field.
My publication code atm:
Meteor.publish('allCats', function(sortType) {
if (sortType == 'rating') {
return Cats.find({}, {sort: {
// How to sort by registerIds array???
}});
}
return Cats.find({}, {sort: { created_at: 1 }});
});
So the question is: How to sort Cats
collection by register_id
s array and created_at
field in publication?
Upvotes: 1
Views: 60
Reputation: 488
If you want to do it reactively, you can either store the ratings with the cats or map them later in Javascript. A third, non-reactive option would probably be to use the MongoDB aggregation pipeline with join's (added in 3.2), but I'm not familiar enough with that to provide an example at this time.
If you store the ratings with the cats, it becomes very simple:
Meteor.publish('allCats', function (sortType) {
/**
* Assuming that Cats contains contents of the form:
* {
* "_id" : "rRq76LxsnPfmuh9DD",
* "register_id" : "20gnr3g",
* "name" : "Meow",
* "created_at": ...,
* "rating": 123,
* ...
* }
**/
if (sortType == 'rating') {
return Cats.find({}, { sort: { rating: 1 } });
}
return Cats.find({}, { sort: { created_at: 1 } });
});
If you choose to keep RatingLog separate and want the results to be reactive, you will need to perform your sorting on the client-side; publish both allCats
and allCatRatings
:
Meteor.publish('allCats', function() {
return Cats.find({}, {sort: { created_at: 1 }});
});
Meteor.publish('allCatRatings', function() {
return RatingLog.find();
});
After subscribing to both of those, retrieve the ratings and perform the sort:
Template.myTemplate.helpers({
cats() {
const sortType = Template.instance().sortType;
if (sortType === 'rating') {
return getCatsByRating();
}
return getCatsByCreationDate();
}
});
function getCatsByCreationDate() {
return Cats.find({}, {sort: { created_at: 1 }});
}
function getCatsByRating() {
/**
* Assuming that RatingLog contains contents of the form { _id: catId, rating: 123 }
**/
const ratings = {};
RatingLog.find().forEach(entry=>ratings[entry._id] = entry.rating);
const defaultRating = 0;
return Cats.find().fetch().sort(function (catA, catB) {
const catARating = ratings[catA._id] || defaultRating;
const catBRating = ratings[catB._id] || defaultRating;
return catARating - catBRating;
});
}
Upvotes: 1