问题
I'm building a simple messaging app with Meteor. The section I'm struggling with in unread messages. I would like to return a list, showing the username (I'm not concerned about this, please don't focus on this aspect, around reactive joins/ composites etc ) and the latest message from that user What I need to return therefore, in the publish function below, is the newest unread messages, BUT obviously only one from each unique user id.
to do this im trying to manipulate the results of a find query in my publish method, but I'm unclear as to how to manipulate the document set without breaking the reactivity as I've shown in currently in the code below, this is what i've got so far :
Meteor.publish('unreadmessages', function() {
if (!this.userId) {
throw new Meteor.Error('denied', 'not-authorized');
}
var messageQuery, messages, userGroupQuery, userGroups;
var self = this;
var user = Meteor.users.findOne(self.userId);
var userIdArr = [self.userId]; // for use where queries require an array
var contacts = user.contacts;
// get groups
userGroupQuery = Groups.find({
$or : [
{ owner : self.userId },
{ members : self.userId }
]
}, { // Projection to only return the _id field
fields : { _id:1 }
}
);
userGroups = _.pluck(userGroupQuery.fetch(), '_id'); // create an array of id's
messages = Messages.find({
$or : [
{
$and : [
{ participant : self.userId },
{ userId : { $in : contacts } },
{ readBy : { $nin : userIdArr } }
]
},
{
$and : [
{ groupId : { $in : userGroups } },
{ readBy : { $nin : userIdArr } }
]
},
]
});
// TODO : also handle groups here
uniqueMessages = _.uniq(messages.fetch(), function(msg) {
return msg.userId;
});
return uniqueMessages; // obviously an array and not a cursor - meteor errors out.
});
i realize that my underscore function is of course working with, and indeed returning, an array rather than the reactive cursor i need. i know one solution would be to simply pluck the message ids and then run another .find on messages, but is there another / better / more efficient / more natural way to return a cursor with the result set i'm looking for?
回答1:
You can use observeChanges and makes this reactive. On the added
, you can add fields. I'm using this amazing package: meteor-publish-composite, It saves you time.
Use pagination, otherwise you won't enjoy the performance.
回答2:
The following code does publish a reactive cursor and is a viable solution, but I suppose the crux of my question is if there is a better way to manipulate the result set of of the second last Messages.find
to still publish a reactive cursor (I'm thinking along the lines o the cursor.forEach or .map but I'm not sure how to approach this).
Basically - is there a better way to do this :
Meteor.publish('unreads', function() {
if (!this.userId) {
throw new Meteor.Error('denied', 'not-authorized');
}
// setup some vars
var messageQuery, messages, userGroupQuery, userGroups, uniqeMsgIds;
var self = this;
var user = Meteor.users.findOne(self.userId);
var userIdArr = [self.userId]; // for use where queries require an array
var contacts = user.contacts;
// get groups
userGroupQuery = Groups.find({
$or : [
{ owner : self.userId },
{ members : self.userId }
]
}, { // Projection to only return the _id field
fields : { _id:1 }
}
);
// create an array of group id's that belong to the user.
userGroups = _.pluck(userGroupQuery.fetch(), '_id');
messages = Messages.find({
$or : [
{ // unread direct messages
$and : [
{ participant : self.userId },
{ userId : { $in : contacts } },
{ readBy : { $nin : userIdArr } }
]
},
{ // unread group messages
$and : [
{ groupId : { $in : userGroups } },
{ readBy : { $nin : userIdArr } }
]
},
]
}, { sort : { // put newest messages first
time : -1
}
});
// returns an array of unique documents based on userId or groupId
uniqueMessages = _.uniq(messages.fetch(), function(msg) {
if (msg.groupId) {
return msg.groupId;
}
return msg.userId;
});
// Get the id's of all the latest unread messages (one per user or group)
uniqeMsgIds = _.pluck(uniqueMessages, '_id');
// finally publish a reactive cursor containing one unread message(latest) for each user/group
return Messages.find({
_id : { $in : uniqeMsgIds };
});
});
回答3:
Heres the final working code. This does return a cursor as basically what I'm doing is taking the results from multiple queries / modifications and pumping a set of id's into the final .find query.
Meteor.publish('unreads', function() {
if (!this.userId) {
throw new Meteor.Error('denied', 'not-authorized');
}
// setup some vars
var messageQuery,
messages,
userGroupQuery,
userGroups,
uniqeMsgIds;
var self = this;
var user = Meteor.users.findOne(self.userId);
var userIdArr = [self.userId]; // for use where queries require an array
var contacts = user.contacts;
// get groups
userGroupQuery = Groups.find({
$or : [
{ owner : self.userId },
{ members : self.userId }
]
}, { // Projection to only return the _id field
fields : { _id:1 }
}
);
// create an array of group id's that belong to the user.
userGroups = _.pluck(userGroupQuery.fetch(), '_id');
messages = Messages.find({
$or : [
{ // unread direct messages
$and : [
{ participant : self.userId },
{ userId : { $in : contacts } },
{ readBy : { $nin : userIdArr } }
]
},
{ // unread group messages
$and : [
{ groupId : { $in : userGroups } },
{ readBy : { $nin : userIdArr } }
]
},
]
}, { sort : { // put newest messages first
time : -1
}
});
// returns an array of unique documents based on userId or groupId
uniqueMessages = _.uniq(messages.fetch(), function(msg) {
if (msg.groupId) {
return msg.groupId;
}
return msg.userId;
});
/* Get the id's of all the latest unread messages
(one per user or group) */
uniqeMsgIds = _.pluck(uniqueMessages, '_id');
/* finally publish a reactive cursor containing
one unread message(latest) for each user/group */
return Messages.find({
_id : { $in : uniqeMsgIds }
});
});
来源:https://stackoverflow.com/questions/27771318/in-meteor-how-can-i-publish-processed-results-of-a-find-query-as-a-cursor