But I believe that’s a bad way, because .fetch() will cause recomputing all the data on every change. The growing list will slow down depending on the number of elements.
What is the best way to do such grouping? Hey, meteors? May be we need to insert delimiters and give special classes to the elements via jQuery depending on data-attributes in afterFlush callback? Or to use cursor.observe and fill up special local collection? Or?
Otherwise, the basic logic would just be to get the timestamp, and if a message block for that time hasnt been created, create a new messageblock with the messages, else append the message to a matching messageblock.
Edit: Also there seems to be a few chat packages on atmosphere…check them out.
I try to find the best and fast solution to create that message blocks. I used to use .fetch() for that and imperative approach, but I’m not happy with that, because it breaks fine-grained reactivity and recomputing the whole list on every change, that’s not good for chat app.
I had to deal with something similar in my app, but could not use the aggregation pipeline since the results were coming out of two different publications of the same collection, and I had to group my data by a common ID.
This is how I worked around it: I subscribed to both publications, and once done I created a helper that minimongoes the published data into groups.
<template name="Rows">
{{#each rows}}
<div>Row {{this._id}}</div>
{{#if grouppedDataMatches this}}
{{ > groupedDataTemplate this }}
{{/if}}
{{/each rows}}
</template>
<template name="groupedDataTemplate">
{{! Show your groupped data in any way you want }}
</template>
----
Handlebars.registerHelper('rows', function(){
return RowsCollection.find({query that gives results with unique timestamp or whatever});
});
Handlebars.registerHelper('grouppedDataMatches ', function(parent_row){
return RowsCollection.find({query that gives results related to parent_row});
});
Of course this can’t work with everything, but seems to be working fine and fast for me.
@artpolikarpov is your performance concern an actual observation or purely theoretical ?
Many time, performances concerns exist only in the mind of the developer and doesn’t translate in actual problem in practice. Morever with a client app, computations are done on the client’s computer.
Now, if this is really slow, have you tried cursor.map instead of fetching then working on the resultant array ?
The doc says :
> When called from a reactive computation, map registers dependencies on
the matching documents.
I have not made tests, but perhaps it could improve your situation, and lead to fewer reprocessing.
I’m curious how others fixed this as well. Grouping data from multiple (compositely) published collections without having to re-run a huge group function on every change.
FWIW, I use one function that get’s called on every reactivity change and group that in a flat array/object structure for every level I want to group on. Hacky, but it works. Not very re-usable yet though.
Why so complicated? It appears to me that a simple brilliant application of list (and map/object) comprehension calls such as available from underscore or lodash would easily and quickly solve the problem at hand.
The processing, in order, would be… (assuming they’re already sorted by createdAt; also assuming lodash as _ since I’m not as familiar with the underscore library)
“round” all the createdAt dates to the minute (using _.map)
group all messages by createdAt (using _.groupBy)
remove the createdAt property for all but the first item (using _.each on the list of groups, then _.map again on the grouped-together messages, the latter removing the property when the index > 0)
then flatten that out into an array again (using _.values, then _.flatten)
return it as the model for the template to work on
The template then simply checks for existence of createdAt in the current item and if it finds it then it uses the output format with date, and if it doesn’t it just outputs the message itself. Should work mostly. It might be that additional trickery then in Template.messages.onRendered is required, running through the list using JS/jQuery once more, but I’m not sure about that, and it would be minor – like, I imagine, adjusting the parent/child relationship / grouping up all message-without-date-messages in a common container or so, which you cannot do with just the handlebars template. (I don’t want to say “cannot” because there’s always some way to do things… ;))
Does that help? I think that should solve the problem without any excessive .fetch()ing etc, just pure client-side grouping and minorly adjusting the publication results.
EDIT: And sure, you could move into MongoDB aggregation territory. But generally, whatever you can do purely on the client, you should. And here, using MongoDB aggregation would mean losing the power of reactive subscriptions, so it’s not something to do lightly.