Help regarding Meteor Pub/Sub - meteor

I have more than one million records in mongo collection.
It is obviously is bad strategy to send all these record to client browser.
So, I created a publish function which just return count for "Searched" parameter only amongst those million records.
Meteor.publish('count', function(query) {
return Collection.find({ fieldName : "query" }).count();
});
So, I got response from above query in following format.
We are counting for pain and count is [object Object]
I want to show the count from above publication,But I receive objects only in response not number. Though When I executed this same query in dev console then , it did showed "Number" as output.
Where am I going wrong?
Can anyone show me a simple example that helps this to resolve?

There is a package for it: https://atmospherejs.com/tmeasday/publish-counts
Simply call Counts.publish within a publication, passing in a name and a cursor:
//server code
Meteor.publish('posts', function() {
Counts.publish(this, 'postsCount', Posts.find());
return Posts.find({}, { limit: 10 });
});
Subscribe on the client:
Meteor.subscribe('posts');
Then just call Counts.get('postsCount'); to get counter number. For example in your helper:
Template.posts.helpers({
postsCount: function() {
return Counts.get('postsCount');
}
});

Related

Meteor - Publications causing long loading time

I am trying to make a fairly big meteor app, and I noticed that it has gotten slower over the past few days and I read on the meteor forum that publications can cause slow loading times. After I refresh the page when making a change in the application itself (code change), it usually takes between 1-2 minutes for a single change. Is there anything wrong with my publications? Although, when the page is loaded, and I reload it loads up really fast.
if(Meteor.isServer){
Meteor.publish('notes', function () {
return Notes.find()
});
Meteor.publish('users', function () {
return Meteor.users.find()
});
Meteor.publish("user", function(){
return Meteor.user()
})
Meteor.publish('notes-newest', function () {
return Notes.find({}, {sort: {createdAt: -1}, limit: 10});
});
}
document example:
let noteInfo = { title, subject, description, imageURL, userId, userEmail, createdAt }
let title = this.refs.title.value;
let subject = this.refs.subject.value;
let description = this.refs.description.value;
let allUrls = [this.refs.imageURL.value].concat(this.state.urls);
let imageURL = allUrls.filter(function(entry) { return entry.trim() != ''; });
let userId = Meteor.userId();
let userEmail = Meteor.user().emails[0].address;
let createdAt = Date.parse(new Date());
This is a very broad performance tuning question. You haven't told us how many documents are in your collections or how large these documents are. Some possible issues:
You are over-publishing either by publishing too many documents or because your documents are too big. For example when you do:
Meteor.publish('notes', function () {
return Notes.find()
});
If there are 100,000 Notes documents at 100 bytes each then that's 10 MB that needs to go over the network to the client. This is also the case if there are 1,000 notes documents at 10KB each.
Solution: Limit the number of documents with limit and/or reduce the number of fields transmitted with fields:
Meteor.publish('notes', function () {
return Notes.find({},{ limit: 100, fields: { key1: 1, key2: 1 }});
});
Your collection is missing one or more indexes. When you do:
Meteor.publish('notes-newest', function () {
return Notes.find({}, {sort: {createdAt: -1}, limit: 10});
});
if there are 1M notes documents but there is no index on the createdAt key then this will be terribly slow.
Solution: Adding an index on the createdAt key will make such a publication much faster.
You have an invalid publication.
Meteor.publish("user", function(){
return Meteor.user();
})
Is invalid because Meteor.user() is not a cursor and a publication must return either a cursor or an array of cursors. It is also redundant because Meteor.user() is automatically available on the client, albeit it doesn't include all the keys.
Solution: Remove this unnecessary publication altogether. If you want to publish some of the keys that are not available on the client for the current user you can do so as follows:
Meteor.publish("user", function(){
return Meteor.users.find(this.userId,{fields: {services: 1, emails: 1, profile: 1}})
});
After I refresh the page when making a change, it usually takes between 1-2 minutes for a single change. Is there anything wrong with my publications? Although, when the page is loaded, and I reload it loads up really fast.
What does this mean? Do you mean when you change the code of your website, it takes 1-2 minutes for your changes to appear in a newly loaded page? That's to be expected: it takes time for Meteor to rebuild your application. Watch the terminal for the progress.
Using lots of packages and external code will slow down building. The speed of compilation is unrelated to your end user's experience.
A couple things I do with my meteor projects is that I send specific fields "profile.name" etc. I've also used collection.createIndex({}) which creates an index and inside it and you could put your sort in which is created on server startup and works well with your collection.find.sort. I think limiting your notes would be quite essential as you have a limit on one and not the other.
It happened to me too. At that time I checked with kadira (formerly still opensource but now it is not). Source code at kadira on github, but service is gone kadira.io
The problem is in pubication and subscribtion. When client request subscribtions, he opens up some kind of "connection" - i say that, and it applies to others who call the same publication. imagine how many connections are formed.
where the same data accessed by many people must server side must request data to mongodb, that same to all of number of such connections. This is what makes it slow.
Finally I added redis.io.
meteor npm install --save redis
meteor npm install --save hiredis
On server side :
import { Meteor } from "meteor/meteor";
var redis = require("redis");
var clientRedis = redis.createClient({
host: "YOURREDIS_IP",
port: "YOURREDIS_PORT"
});
clientRedis.setSync = Meteor.wrapAsync(clientRedis.set);
clientRedis.getSync = Meteor.wrapAsync(clientRedis.get);
setRedis_Object = function (keyREDIS, timeRefresh, valueREDIS) {
return clientRedis.setSync(keyREDIS + moment(new Date()).format(timeRefresh), JSON.stringify(valueREDIS));
};
getRedis_Object = function (keyREDIS, timeRefresh) {
return JSON.parse(clientRedis.getSync(keyREDIS + moment(new Date()).format(timeRefresh)));
};
Meteor.methods({
getRedis_YOURCOLLECTIONS: function(timeRefresh) {
var data = getRedis_Object(getRedis_YOURCOLLECTIONS, timeRefresh);
if (data != null) {
return data;
} else {
var data = YOURCOLLECTIONS.find().fetch();
setRedis_Object(key, timeRefresh, data);
return data
}
},
});
On Client Side
Meteor.call("getRedis_YOURCOLLECTIONS", "YYYYMMDD", function (error, result) {
if (error) {
console.log(error);
} else {
Session.set("getRedis_YOURCOLLECTIONS", result);
}
});
After i am add this REDIS consumtion Memory and CPU on server low, and app more Fast. I hope its work for you. Thanks

Meteor Aggregated result not showing in template

I am following this answer on stackoverflow. In console, I get the correct aggregated sum.
Meteor.publish('hoursTotal', function() {
var self = this;
var pipeline = [
{$group: {_id: "$userId", hours: {$sum: "$hours" }}}
];
var result = Reports.aggregate(pipeline);
_.each(result, function(result) {
self.added("hoursTotalSum", result._id, {
userId: result._id,
hours: result.hours
});
});
console.log(result);
self.ready();
});
I've subscribed to the new collection created in client:
Meteor.subscribe('hoursTotalSum');
My template helper:
Template.statsBrief.helpers({
hoursTotalSum: function() {
var currentUserId = Meteor.userId();
return Reports.find({userId: currentUserId});
},
});
My template:
{{#each hoursTotalSum}} {{hours}} {{/each}}
Meteor console retunrs this:
[ { _id: 'F8ZEWeKMoRfXaEGNa', hours: 30 },
I20151221-09:57:09.097(0)? { _id: 'ufALZHfhWyQ8pMkgD', hours: 85 } ]
Meteor templates return this:
50 20 15
Although the summation is happening right, and the backend console confirms that, I'm struggling to get the value into the template.
I successfully use aggregates via the meteorhacks:aggregate package however I do it a bit differently from your example. Rather than debug your code I will show how I would achieve the same goal.
Note :The code below is in coffeescript
Write a Meteor Method
Create a server side Meteor Method to perform the aggregation. You do not need to publish your aggregation as all the processing happens on the server and the results are under your control via the $project step.
Meteor.methods
getHoursTotal: ->
return null unless #userId
check #userId, String
pipeline = [
{$group:{'_id': #userId, 'hours':{$sum:'$hours'}}},
{$project:
_id:false,
userId:'$_id',
hours: '$hours',
}
]
return reports.aggregate pipeline
In your code you were processing each item in the result. It looked like you were just wanting rename _id to userId and include the calculated hours field. I did this in the $project step instead.
Call the Meteor Method
Your code depends on the userId being available, so we only want to run it after the user has signed in. We can make this reactive using tracker.
Tracker.autorun ->
return unless Meteor.userId()?
Meteor.call 'getHoursTotal', (err, res) ->
Session.set 'hoursTotal', res
Now the code will only tun if the Meteor.userId is not null and when it does run the hoursTotal will be stored in the Session ready for use.
Expose the Results
To expose the results nicely it is a good idea to put it on a Template helper.
Template.statsBrief.helpers
hoursTotal: -> Session.get 'hoursTotal'
I think performing the aggregate and exposing the results in this way is simpler whilst also benefiting from being fully reactive.

returning cursor results causes "stack size exceeded" error

I have a page that lists a company's profile and shows its open jobs. I use iron-router to get the companies profile info, but use a Meteor.call to get active jobs once the page has been loaded. However, when I return a cursor it throws a stack size exceeded error.
organization.js
Template.organization.rendered = function() {
Meteor.call('getActiveJobs', function(error, jobs){
if(error){
console.log(error);
} else {
console.log(jobs);
}
});
}
collection
Meteor.methods({
.....
getActiveJobs: function(){
return Jobs.find({organizationId: user.profile.organizationId});
}
.....
});
this throws a "RangeError: Maximum call stack size exceeded" error.
However, I can return
return Jobs.find({organizationId: user.profile.organizationId}).fetch();
without an error, but I'm trying t return the cursor so it's easier to work with using handlebars, but I don't quite understand why I'm getting this error.
Please note, that the values returned from your method need to be transferred from server to client in the JSON format. It follows that you cannot return Objects which are not JSON-serializable (there are small exceptions here, but we can forget about them for now).
If you want to return a cursor you should use Meteor.publish instead of Meteor.methods, so
Meteor.publish('activeJobs', function () {
var user = Meteor.users.findOne({_id: this.userId});
return Jobs.find({organizationId: user.profile.organizationId});
});
Also, remember to call Meteor.subscribe('activeJobs') on the client as soon as you need this data set.

Cannot acces properties of resulting array of cursor.fetch();

I have a helper where I want to acces the properties of a different collection.
Template.notification.helpers({
username: function () {
game = Games.findOne({_id: this.gameId}, {fields: {players:1}});
console.log(game) // output is correct
}
})
If I log this, it wil produce the result I expected:
Object {players: Array[2], _id: "qF3skjX2755BYcr8p"}
However, if I in my helper function I try to use/reach this properties I get an undefined error.
Template.notification.helpers({
username: function () {
game = Games.findOne({_id: this.gameId}, {fields: {players:1}});
console.log(game._id) // error;
console.log(game.players) // error
}
})
Output:
Exception from Deps recompute function: TypeError: Cannot read property 'players' of undefined
Why is this happening?
This happens because when Meteor initiall loads on your web browser, all the html and js is ready, but the data is not yet ready.
If you tried to check console.log(game) it may be null. It does this only when the page has loaded. If you load the template after all the data has downloaded you wouldn't see this issue.
When the data arrives the username helper would re-run with the new data.
In the meanwhile you just need to take care of this exception:
var game = Games.findOne({_id: this.gameId}, {fields: {players:1}});
if(!game) return null;

How to get a published collection's total count, regardless of a specified limit, on the client?

I'm using the meteor-paginated-subscription package in my app. On the server, my publication looks like this:
Meteor.publish("posts", function(limit) {
return Posts.find({}, {
limit: limit
});
});
And on the client:
this.subscriptionHandle = Meteor.subscribeWithPagination("posts", 10);
Template.post_list.events = {
'click #load_more': function(event, template) {
template.subscriptionHandle.loadNextPage();
}
};
This works well, but I'd like to hide the #load_more button if all the data is loaded on the client, using a helper like this:
Template.post_list.allPostsLoaded = function () {
allPostsLoaded = Posts.find().count() <= this.subscriptionHandle.loaded();
Session.set('allPostsLoaded', allPostsLoaded);
return allPostsLoaded;
};
The problem is that Posts.find().count() is returning the number of documents loaded on the client, not the number available on the server.
I've looked through the Telescope project, which also uses the meteor-paginated-subscription package, and I see code that does what I want to do:
allPostsLoaded: function(){
allPostsLoaded = this.fetch().length < this.loaded();
Session.set('allPostsLoaded', allPostsLoaded);
return allPostsLoaded;
}
But I'm not sure if it's actually working. Porting their code into mine does not work.
Finally, it does look like Mongo supports what I want to do. The docs say that, by default, cursor.count() ignores the effects of limit.
Seems like all the pieces are there, but I'm having trouble putting them together.
None of the answers do what you really want becase none provide solution that is reactive.
This package does exactly what you want and also reactive.
publish-counts
I think you can see the demo: counts-by-room in meteor doc
It can help you publish the counts of your posts at server and get it at client
You can simply write this:
// server: publish the current size of your post collection
Meteor.publish("counts-by-room", function () {
var self = this;
var count = 0;
var initializing = true;
var handle = Posts.find().observeChanges({
added: function (id) {
count++;
if (!initializing)
self.changed("counts", 'postCounts', {count: count});
},
removed: function (id) {
count--;
self.changed("counts", postCounts, {count: count});
}
});
initializing = false;
self.added("counts", 'postCounts', {count: count});
self.ready();
self.onStop(function () {
handle.stop();
});
});
// client: declare collection to hold count object
Counts = new Mongo.Collection("counts");
// client: subscribe to the count for posts
Tracker.autorun(function () {
Meteor.subscribe("postCounts");
});
// client: simply use findOne, you can get the count object
Counts.findOne()
The idea of sub.loaded() is to help you with exactly this problem.
Posts.count() isn't going to return the right thing because, as you've guessed, on the client, Meteor has no way of knowing the real number of posts that live on the server. But what the client knows is how many posts it's tried to load. That's what that .loaded() tells you, and is why the line this.fetch().length < this.loaded() will tell you if there are more posts on the server or not.
What I would do is write a Meteor server side method that retrieves the count like so:
Meteor.methods({
getPostsCount: function () {
return Posts.find().count();
}
});
Then call it on the client, in observe to make it reactive:
function updatePostCount() {
Meteor.call('getPostsCount', function (err, count) {
Session.set('postCount', count);
});
}
Posts.find().observe({
added: updatePostCount,
removed: updatePostCount
});
Although this question is old, I thought I would provide an answer that ended up working for me. I did not create the solution, I found the basis for it here (so credit where credit is due): Discover Meteor
Anyway, in my case I was trying to get "size" of the database from client side, so I can determine when to hide the "load more" -button. I was using template level subscriptions. Oh and for this solution to work, you need to add reactive-var -package. Here is my (in short):
/*on the server we define the method which returns
the number of posts in total in the database*/
if(Meteor.isServer){
Meteor.methods({
postsTotal: function() {
return PostsCollection.find().count();
}
});
}
/*In the client side we first create the reactive variable*/
if(Meteor.isClient){
Template.Posts.onCreated(function() {
var self = this;
self.totalPosts = new ReactiveVar();
});
/*then in my case, when the user clicks the load more -button,
we call the postsTotal-method and set the returned value as
the value of the totalPosts-reactive variable*/
Template.Posts.events({
'click .load-more': function (event, instance){
Meteor.call('postsTotal', function(error, result){
instance.totalPosts.set(result);
});
}
});
}
Hope this helps someone (I recommend checking the link first). For template level subscriptions, I used this as my guide Discover Meteor - template level subscriptions. This was my first stacked-post and I am just learning Meteor, so please have mercy...:D
Ouch this post is old, anyway maybe it will help someone.
I had exactly the same issue. I managed to solve it with 2 simple lines...
Remember the :
handle = Meteor.subscribeWithPagination('posts', 10);
Well I used in client handle.loaded() and Posts.find().count(). Because when they are different it means that all the posts are loaded. So here is my code :
"click #nextPosts":function(event){
event.preventDefault();
handle.loadNextPage();
if(handle.loaded()!=Posts.find().count()){
$("#nextPosts").fadeOut();
}
}
I had the same problem, and using the publish-counts package didn't work with the subs-manager package. I created a package that can set a reactive server-to-client session, and keep the document count in this session. You can find an example here:
https://github.com/auweb/server-session/#getting-document-count-on-the-client-before-limit-is-applied
I'm doing something like this:
On cliente
Template.postCount.posts = function() {
return Posts.find();
};
Then you create a template:
<template name="postCount">
{{posts.count}}
</template>
Then, whatever you want to show the counter: {{> postCount}}
Much easier than any solution i have seen.

Resources