Reputation: 293
While building my first Meteor app I found something I don't even know how to approach. I have a list of Entries in a collection and I want to categorise them by date (not to sort them by date). Each entry has a date and I want to build something like a timeline. Here's how one of those entries looks like:
{
"_id": "xHmfEc5gMmGzDZgD2",
"title": "sth",
"labels": [
"pv4LnWNheyYRJqG7D"
],
"images": [
"zitE3FwmesHsNCxGG",
"kYQTJYApDxPr4sMdW",
"4enopyawfqBXRDvuh"
],
"author": "TMviRL8otm3ZsddSt",
"createdAt": "2016-01-31T21:05:55.401Z",
"date": "2016-01-31T21:05:55.401Z",
"description": "desc"
}
In the end I want to achieve something like a timeline and I want the entries to belong to a day. Here's how that might look like on a timeline split on days, each entry belonging to a day:
Day - 11.02.2016
entry - xHmfEc5gMmGzDZgD2
entry - ZhmfEc5gMmGzDZg8z
entry - uafEc5gMmGzDZgD25
Day - 12.02.2016
entry - xHmfEc5gMmGzDZgD2
entry - ZhmfEc5gMmGzDZg8z
Day - 17.02.2016
entry - xHmfEc5gMmGzDZgD2
entry - ZhmfEc5gMmGzDZg8z
entry - xHmfEc5gMmGzDZgD2
entry - ZhmfEc5gMmGzDZg8z
But I want to get the days out of the entries, not to Create a separate collection for a calendar. If it's possible....
What would be a good way to achieve this ?
The only way I can imagine to do this at the moment is to store all the different dates from the entries into a separate collection and then, each day should query the entries collection to get the ones that fit. But I think this is a really bad way to do it as for each day I will have a DB query....
Upvotes: 2
Views: 45
Reputation: 103345
You may want to use the aggregation framework for this, but because aggregate isn't supported yet in meteor, you need to get the aggregation framework package installed - it doesn't do anything to fancy, just wraps up some Mongo methods for you.
Just meteor add meteorhacks:aggregate
and you should be in business. This will add proper aggregation support for Meteor.
Now you would need this pipeline to achieve the desired result. In mongo shell, run the following:
var pipeline = [
{
"$project": {
"yearMonthDay": { "$dateToString": { "format": "%Y-%m-%d", "date": "$date" } },
}
},
{
"$group": {
"_id": "$yearMonthDay",
"entries": { "$push": "$_id" }
}
}
];
db.entry.aggregate(pipeline);
In Meteor, you can publish these results to the Entries collection on the client side:
Meteor.publish('getDayCategorisedEntries', function (opts) {
var initializing = 1;
function run(action) {
// Define the aggregation pipeline ( aggregate(pipeline) )
var pipeline = [
{
"$project": {
"yearMonthDay": { "$dateToString": { "format": "%Y-%m-%d", "date": "$date" } },
}
},
{
"$group": {
"_id": "$yearMonthDay",
"entries": { "$push": "$_id" }
}
}
];
Entries.aggregate(pipeline).forEach(function(e){
this[action]('day-entries', e._id, e)
this.ready()
});
};
// Run the aggregation initially to add some data to your aggregation collection
run('added');
// Track any changes on the collection we are going to use for aggregation
var handle = Entries.find({}).observeChanges({
added(id) {
// observeChanges only returns after the initial `added` callbacks
// have run. Until then, we don't want to send a lot of
// `self.changed()` messages - hence tracking the
// `initializing` state.
if (initializing && initializing--) run('changed');
},
removed(id) {
run('changed');
},
changed(id) {
run('changed');
},
error(err) {
throw new Meteor.Error('Uh oh! something went wrong!', err.message)
}
});
// Stop observing the cursor when client unsubs.
// Stopping a subscription automatically takes
// care of sending the client any removed messages.
this.onStop(function () {
handle.stop();
});
});
The above observes changes and if necessary reruns the aggregation.
Upvotes: 2