0

I am trying to publish forum replies to a specific thread, but I would like those reply documents to include extra information about the user that posted it.

I don't want to "save" that extra information on the reply itself but rather, publish an "improved" version of it.

I am doing something similar on client-side already with mycollection.find().map() and using the map function to embedded extra information on each of the returned documents, however, Meteor publish cannot seem to publish an array, only a Cursor, so the simple map function is off limits.

Is there a way to achieve this? Maybe a "map" function that returns a Cursor?

I am not using Meteor.methods so that I can have reactivity, because with them I could just return an array and use it as normal.

Here is an example of my code (that fails, but sends gives an idea of what I need):

Meteor.publish("forumthread", function(thread){
    return forumReplies.find({thread: thread}).map(function(r){
        // lets fill in additional data about each replies owner
        var owner = Meteor.users.findOne({_id: r.owner});
        if(!owner)
            return; // no owner no reply..
        if(!owner.forumStats){
            owner.forumStats = {};
            owner.forumStats.postCount = 0;
            owner.forumStats.postLikes = 0;
            owner.forumStats.title = "The Newbie";
            owner.forumStats.tag = "Newbie";
            Meteor.users.update({_id: owner._id}, {$set:{ forumStats:owner.forumStats }});
        }
        r.ownerid = owner._id;
        r.ownerUsername = owner.username;
        r.ownerPostCount = owner.forumStats.postCount;
        r.ownerPostLikes = owner.forumStats.postLikes;
        r.ownerTitle = owner.forumStats.title;
        r.ownerTag = owner.forumStats.tag;
        return r;
    });
});

Thank you.

Christian Fritz
  • 20,641
  • 3
  • 42
  • 71
Joao Carlos
  • 749
  • 1
  • 11
  • 32
  • Take a look on @christian-fritz answer at http://stackoverflow.com/questions/20895154/how-to-transform-data-returned-via-a-meteor-publish – Kuba Wyrobek Jun 17 '14 at 11:17
  • Also see @Akshat answer at http://stackoverflow.com/questions/18343534/how-to-publish-a-view-transform-of-a-collection-in-meteor – Kuba Wyrobek Jun 17 '14 at 11:20
  • 1
    You could also consider publishing `forumReplies` and respective user data together, as pointed out by David Weldon here: http://stackoverflow.com/a/21840513/1087119. I've found these compound publications very powerful yet clean. You would then assemble the extended forum replies client-side. – Christian Fritz Jun 17 '14 at 16:21

1 Answers1

0

Ended up doing this (found out that Christian Fritz also suggested it):

    Meteor.publish("serverforumthread", function(thread){
    check(thread, String);

    var replies = forumReplies.find({thread: thread});
    var users = {};
    replies.map(function(r){
        users[r.owner] = r.owner;
    });
    var userids = _.map(users, function(value, key){ return value; });   
    var projectedFields = {_id:1, username:1, forumStats: 1, services: 0};    
    var usrs = Meteor.users.find({_id:{$in: userids}}, projectedFields);
    var anyUpdateToUsers = false;
    usrs.map(function(owner){
        var changed = false;
        if(!owner.username){
            owner.username = owner.emails[0].address.split("@")[0]; 
            changed = true;
        }
        //owner.forumStats = undefined;
        if(!owner.forumStats){
            owner.forumStats = {};
            owner.forumStats.postCount = 0;
            owner.forumStats.postLikes = 0;
            owner.forumStats.title = "the newbie";
            owner.forumStats.tag = "newbie";
            owner.forumStats.img = "http://placehold.it/122x122";
            changed = true;
        }
        if(changed){
            anyUpdateToUsers = true;
            Meteor.users.update({_id: owner._id}, {$set:{ forumStats:owner.forumStats }});
        }
    });  
    if(anyUpdateToUsers) // refresh it
         usrs = Meteor.users.find({_id:{$in: userids}}, projectedFields);

    usrs.map(function(owner){
        console.log(owner);
    });
    return [replies, usrs];
});

It works great with the following client side:

Template.forumReplyOwner.helpers({
    replyOwner: function(reply){
        var owner = Meteor.users.findOne({_id: reply.owner});
        console.log(reply, owner);
        if(!owner || !owner.forumStats) return; // oh shait!
        var r = {};
        r.owner = owner._id;
        r.ownerUsername = owner.username;
        r.ownerPostCount = owner.forumStats.postCount;
        r.ownerPostLikes = owner.forumStats.postLikes;
        r.ownerTitle = owner.forumStats.title;
        r.ownerTag = owner.forumStats.tag;
        r.ownerImg = owner.forumStats.img;
        return r;
    },
    ownerImgTab: function(){
        return {src: this.ownerImg};
    }
});

However, I am now facing another problem. Even tho I am restricting the fields I am publishing from the Users collection, it is still sending down the "services" field, that contains login data that shouldnt be sent, ideas?

Joao Carlos
  • 749
  • 1
  • 11
  • 32