Meteor Publication and Subscription fetch all records after use skip and limit - meteor

We have use publication and subscription with skip and limit in meteor and it fetch all records at client side.
Meteor.publish('jobOrders', function(skip, limit) {
return (jobOrders.find({}, {
skip: skip,
limit: limit
}));
});
Template.templateName.onCreated(function() {
this.subscribe("jobOrders", 0, 2);
});
Template.templateName.helpers({
methodName: function() {
var totalNoOfRecords = jobOrders.find({}).count();
console.og("records are", totalNoOfRecords);
return totalNoOfRecords;
}
});

The reason could be because you have other pub-subs in other templates which are fetching the data from jobOrders collection.
Please comment your subscription in your current template and see whether you are still able to fetch the records from this collection. If you are seeing all the records even after commenting the subscription, it should be because of some other subscription.
To overcome this problem, you have to either do one of the following:
Change the other subscription which is fetching all the records such that the data is available only with in that template by using
Template.instance().subscribe()
instead of using Meteor.subscribe()which will make the data available throughout client side across all templates.
Filter you records on the client side as well.
Template.templateName.helpers({
methodName: function() {
var totalNoOfRecords = jobOrders.find({skip:0, limit: 2}).count(); //Filter on the client side
console.og("records are", totalNoOfRecords);
return totalNoOfRecords;
}
});
Note: Another possibility could be that you did not remove the insecure package which will automatically publish all the records in all the collection.

Related

Meteor publish overwrites another publish

I have two publish method as below but when I subscribe to one of the publish method in client search page, it is being overwritten with the other one which is meant for index page.
Server
Meteor.publish("task.index", function() {
TaskCollection.find()
}
Meteor.publish("task.index.search", function(state) {
TaskCollection.find({ state: state })
}
Client - search page
Meteor.subscribe("task.index.search", state)
// this will always be overwritten with "task.index" published collection
Client - index page
Meteor.subscribe("task.index")
Does anyone know how to avoid this?
Welcome to SO!
There is a high chance the "override" you see is just the normal Meteor behaviour for Publish/Subscribe mechanism.
Your "task.index" publication sends all your TaskCollection documents to the Client.
Therefore any other publication on that same TaskCollection will send documents that the Client already knows.
Then in your Client, filtering some documents from TaskCollection is independent from your subscription and publication. Just perform your TaskCollection.find({ state: state }) Client side, and you will get the documents you need.
When you publish only a subset of documents of a Collection, it happens that what you publish is exactly already the filtered documents that you want to display on your Client, therefore on your Client you just display all Collection documents you know of. But you have to understand that these are 2 different steps:
Subscription to send some documents to the Client. Several Subscriptions may be set, filling the same Collection on the Client.
Filtering on the Client, based on the documents sent by the (possibly several) Subscription(s).
See also: Publish subscribe doesn't seem to work
If your client index and search pages are different templates, you can subscribe to the documents at respective template level.
Client - search page:
Template.search.created = function () {
const template = this;
template.subscribe('task.index.search', state);
}
Template.search.rendered = function () {
console.log("Client search : " + TaskCollection.find().fetch().length);
}
Client - index page:
Template.index.created = function () {
const template = this;
template.subscribe('task.index');
}
Template.index.rendered = function () {
console.log(""Index : " + TaskCollection.find().fetch().length);
}
But, it is always advisable to filter the documents on the client as well.

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

Understanding skip on server vs client in Meteor

I just want to check because I don't see it in the docs. Maybe I'm just missing it.
As far as I can tell if I subscribe with skip then I don't skip on the client. Correct?
I'm using iron router. I have code like this
Router.route('/docs/:_page', {
template: 'doclist',
subscriptions: function() {
var page = parseInt(this.params._page) - 1;
var skip = page * 10;
var limit = 10;
return Meteor.subscribe("pages", skip, limit);
},
});
The corresponding publish is this
Meteor.publish("pages", function (skip, limit) {
return Docs.find({}, {skip: skip, limit: limit});
});
But now in the template helper I don't use the skip AFAICT because there's only limit results in the MiniMongo
Template.doclist.helpers({
docs: function () {
var route = Router.current();
var pageId = parseInt(route.params._page) || 1;
var page = pageId - 1;
var skip = page * 10;
return Docs.find({}, {
// skip: skip
limit: limit,
});
},
});
It seems to work. If I comment in the skip line then I get no results on page 2.
Is that correct or am I doing something wrong?
You are correct - the client does not require a skip in this case. Let's say you have 100 documents in the DB and you skip the first 20 with a limit of 10. Then only 10 documents will exist on the client. Whenever you find on the client (in your templates), you are querying the local database (in this case 10 documents), so a skip would be inappropriate.
I'll caution that all of this is predicated on the notion that you have only one subscription for Docs. To extend the example above, if you had another 15 documents in the same collection on the client from another subscription, then you may need to do some additional filtering in order to show only the ones you are are interested in.

In Meteor, how can I query only the records of a given subscription?

I understand that a a subscription is a way to flow records into a client-side collection, from this post, and others...
However, per this post, You can have multiple subscriptions that flow into the same collection.
// server
Meteor.publish('posts-current-user', function publishFunction() {
return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});
// this.userId is provided by Meteor - http://docs.meteor.com/#publish_userId
}
Meteor.publish('posts-by-user', function publishFunction(who) {
return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10});
}
// client
Meteor.subscribe('posts-current-user');
Meteor.subscribe('posts-by-user', someUser);
Now - I obtained my records via two different subscriptions, can I use the subscription to get to the records that it pulled back? Or must I requery my collection? What is the best practice for sharing that query between client and server?
I hope I'm not missing something obvious here, but executing the Meteor.subscribe function only for its side-effects seems to be losing a very useful piece of information - namely which subscription a record came from. Presumably the names of publications and subscriptions are chosen to be meaningful - it would be nice if I could get to records associated with that name.
What you seem to want to do is maintain two separate collections of records, where each collection is populated by a different publication. If you read the DDP specification, you'll see that the server tells the client which collection (not publication) each record belongs to, and multiple publications can actually provide different fields to the same record.
However, Meteor actually lets you send records to any arbitrary collection name, and the client will see if it has that collection. For example:
if (Meteor.isServer) {
Posts = new Mongo.Collection('posts');
}
if (Meteor.isClient) {
MyPosts = new MongoCollection('my-posts');
OtherPosts = new MongoCollection('other-posts');
}
if (Meteor.isServer) {
Meteor.publish('my-posts', function() {
if (!this.userId) throw new Meteor.Error();
Mongo.Collection._publishCursor(Posts.find({
userId: this.UserId
}), this, 'my-posts');
this.ready();
});
Meteor.publish('other-posts', function() {
Mongo.Collection._publishCursor(Posts.find({
userId: {
$ne: this.userId
}
}), this, 'other-posts');
this.ready();
});
}
if (Meteor.isClient) {
Meteor.subscribe('my-posts', function() {
console.log(MyPosts.find().count());
});
Meteor.subscribe('other-posts', function() {
console.log(OtherPosts.find().count());
});
}
This is what's happening:
Say that your server-side BlogPosts Mongo collection contains 500 posts from 10 different users. You then subscribe to two different subscriptions on the client:
Meteor.subscribe('posts-current-user'); // say that this has 50 documents
Meteor.subscribe('posts-by-user', someUser); // say that this has 100 documents
Meteor will see Meteor.subscribe('posts-current-user'); and proceed to download the posts of the current user to the client-side Mini-Mongo's BlogPosts collection.
Meteor will then see Meteor.subscribe('posts-by-user', someUser); and proceed to download the posts of someuser to the client-side Mini-Mongo's BlogPosts collection.
So now the client-side Mini-Mongo BlogPosts collection has 150 documents, which is a subset of the 500 total documents in the server-side BlogPosts collection.
So if you did BlogPosts.find().fetch().count in your client (Chrome Console) the result would be 150.
Of course! It just depends on where you write your subscriptions. In a lot of cases you might be using Iron Router, in which case you would have a given route subscribe to just the data that you need. Then from within that route template's helper you can only query documents within that subscription.
But the general idea is that you hook up a particular subscription to a particular template.
Template.onePost.helpers({
post: function() {
Meteor.subscribe('just-one-post', <id of post>);
return Posts.findOne();
}
});
Template.allPosts.helpers({
posts: function() {
Meteor.subscribe('all-posts');
return Posts.find();
}
));

How to push data from server to all clients not using Collections?

I need to inform clients about changes on server side. In my case I am using different Collections on server and on client (more about it in this question: how would you build pinterest like page with meteor.js).
On the server I am getting new Products from external API. I would like to publish the number of new items to all clients that they could update their local variables needed for layout to work well.
How to do it?
It would be nice if I could publish/subscribe other kinds of data than Meteor.Collection. I found Meteor.deps, but what I understand it works only on client side.
To accomplish what you want you do need another collection - on the client. On the server, in a publish function, build a document from scratch assigning the current count of Products to an attribute. Using observe() and set, modify count when documents are added or removed from Products. Subscribe to the count "record set" on the client.
// Server
Meteor.publish('count', function () {
// Build a document from scratch
var self = this;
var uuid = Meteor.uuid();
var count = Products.find().count();
// Assign initial Products count to document attribute
self.set('count', uuid, {count: count});
// Observe Products for additions and removals
var handle = Products.find().observe({
added: function (doc, idx) {
count++;
self.set('counts', uuid, {count: count});
self.flush();
},
removed: function (doc, idx) {
count--;
self.set('counts', uuid, {count: count});
self.flush();
}
});
self.complete();
self.flush();
self.onStop(function () {
handle.stop();
});
});
// Client
Counts = new Meteor.Collection('count');
Meteor.subscribe('count');
console.log('Count: ' + Counts.findOne().count);
I must say the above solution showed me one way, but still, what if I need to publish to client data that are not connected with observe()? Or with any collection?
In my case I have i.e. 1000 products. To engage visitors I am "refreshig" the collection by updating the timestamp of random number of products, and displaying collection sorted by timestamp. Thank to this visitors have impression that something is happening.
My refresh method returns number of products (it is random). I need to pass that number to all clients. I did it, but using (I think) ugly workaround.
My refresh method sets Session.set('lastRandomNo', random). BTW: I didn't know that Session works on server side. refresh updates Products collection.
Then accoriding to above answer:
Meteor.publish 'refreshedProducts', ->
self = this
uuid = Meteor.uuid()
# create a new collection to pass ProductsMeta data
self.set('products_meta', uuid, { refreshedNo: 0 })
handle = Products.find().observe
changed: (newDocument, atIndex, oldDocument) ->
self.set('products_meta', uuid, { refreshedNo: Session.get('lastRandomNo') })
self.flush()
self.complete()
self.flush()
self.onStop ->
handle.stop()
and on client side:
ProductsMeta = new Meteor.Collection('products_meta')
# subscribe to server 'products_meta' collection that is generated by server
Meteor.subscribe('refreshedProducts')
ProductsMeta.find({}).observe
changed: (newDocument, atIndex, oldDocument) ->
# I have access to refreshedNo by
console.log ProductsMeta.findOne().refreshedNo
What do you think?

Resources