I have a collection Feeds
and a collection FeedElements
. FeedElements
documents have a reference to the Feeds
collection via the feedId
field. Furthermore, all FeedElements
documents have the date field submitted
.
I want to publish only the newest (determined by the field submitted
) FeedElements
document which corresponds to exactly one Feeds
document.
Here is what I have tried:
Meteor.publish('recentFeedElements', function (userId) {
var feedIds = Feeds.find({'userId': userId}).map(function(feed) {
return feed._id;
});
if (feedsIds.length > 0) return FeedElements.find({feedId: {$in: feedIds}}, {sort: {submitted: -1});
else this.ready();
});
The problem is, if I use limit
in combination with sort
inside the FeedElements.find()
query, I only get the newest documents of all Feed
documents. However, I want to have a strict 1-1 relation. So, one Feed
document -> the newest FeedElements
document with the appropriate reference.
If I understand you right, you want every feed to have lastActivity
field which contains timestamp of last submitted feed element of this feed. You want this field to be reactive and you don't want to publish all feed elements.
In this case, aggregation is not a solution, because Meteor does not allow reactive aggregations.
You need to use low-level publish API: http://docs.meteor.com/#/full/meteor_publish (see example section).
Low-level publish API (this.added
/this.removed
/this.changed
/others) gives you full control of what data you send to client via Meteor.publish
method.
Here is how you can solve your problem (I use ES2015 syntax):
// client/client.js
Meteor.subscribe('userFeeds', { userId: 1 });
// lib/lib.js
Feeds = new Mongo.Collection('feeds');
FeedElements = new Mongo.Collection('FeedElements');
// server/server.js
// When setting `submitted` field, automatically set `submittedAt` field
// (I use matb33:collection-hooks here)
FeedElements.before.update((userId, elem, fieldNames, modifier) => {
if (modifier.$set.submitted) {
modifier.$set.submittedAt = new Date();
}
});
function getLastActivity(feedId) {
const lastSubmittedElem = FeedElements.findOne(
{
feedId,
submitted: true,
},
{
sort: { submittedAt: -1 }
}
);
return lastSubmittedElem ? lastSubmittedElem.submittedAt : null;
}
Meteor.publish('userFeeds', function({ userId }) {
const elemsObservers = {};
// Observe all user feeds
var feedObserver = Feeds.find({ userId: userId }).observeChanges({
added: (feedId, fields) => {
// Observe feed elements of the feed
elemsObservers[feedId] = FeedElements.find({ feedId }).observeChanges({
changed: (elemId, fields) => {
// Update `lastActivity` field when new element is submitted
if (fields.submitted) {
this.changed('feeds', feedId, { lastActivity: fields.submittedAt });
}
},
});
fields.lastActivity = getLastActivity(feedId);
this.added('feeds', feedId, fields);
},
changed: (feedId, fields) => {
this.changed('feeds', feedId, fields);
},
removed: (feedId) => {
elemsObservers[feedId].stop();
delete elemsObservers[feedId];
this.removed('feeds', feedId);
},
});
this.ready();
this.onStop(function() {
feedObserver.stop();
for (const feedId in elemsObservers) {
elemsObservers[feedId].stop();
}
});
});
Also I prepared github repo https://github.com/imkost/feeds. Just git clone
and meteor run
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With