Sort by a custom property after transform a collection in publish


#1

I have a collection and i create a publication in server side to generate a custom property in transformation, then i subscribe this publication and sort my collection by that custom property in client side, it shows an error message “[custom property name] is not defined”, please see my lines of code below

server side:

Meteor.publish('classes', function () {
var self = this;
var observer = Classes.find({}).observe({
        added: function (document) {
            self.added('classes', document._id, transform(document));
        },
        changed: function (newDocument, oldDocument) {
            self.changed('classes', oldDocument._id, transform(newDocument));
        },
        removed: function (oldDocument) {
            self.removed('classes', oldDocument._id);
        }
    });
self.onStop(function () {
    observer.stop();
});
self.ready();

});
function transform(doc) {
doc.teacher = Meteor.users.findOne({ _id: doc.createdBy }, { fields: { _id: true, username: true, rating: true } });
return doc;
}

client side:

$meteor.subscribe('classes').then(function () {
    var classes = Classes.find({}, { sort: teacher.rating }).fetch(); //error occur here
}, function (error) {
    console.log(error);
});

I have to use publication and subscription to transform my collection because some relative collections cannot be publish.
So sorry if my code is difficult to view, this is my first post


#2

Hard to tell right now, but first I’d try wrapping your teacher.rating:

sort : 'teacher.rating'


#3

The syntax for sorting is: {sort: {someField: dir}}, where dir can be 1 or -1.
I would recommend storing the teacher information in your Classes document like this:

//in create class method.
let teacher = Meteor.users.findOne({_id: this.userId},{username:1,rating:1})
let classDoc = {
  title: "Science 101",
  credits: 4,
  createdBy: this.userId,
  teacherRating: teacher.rating,
  teacherUsername: teacher.username
}
Classes.insert(classDoc);

Then, you can just do this:

Meteor.publish('classes', function(){
  return Classes.find({});
})

And your sort on the client will work as expected. The downside to this solution is that it fails when the user’s rating field is changing a lot over the lifetime of the classDoc in question. One problem with the solution you proposed is that you’re going to potentially run tens, or hundreds of queries for every publication. That doesn’t scale, unfortunately. If you don’t mind keeping a slightly out-of-date rating on the class document, then you should denormalize it as mentioned above. A solution for the out-of-date information is to run a CRON job and update the ratings on the class documents every P seconds, where P is the average time it takes for a rating to change appreciably.


#4

Oh my god, you saved my day, what a silly mistake
Thank you so much, it works


#5

You’re right about the syntax, thank you so much.

About your solution, i cannot add everything to a document. In this example, there is only one field that i have to deal with, what happens if i have 10 fields, as you said, that doesn’t scale