For some reason after upgrading to 0.5.9 I'm having the issue where server side seems to send everything correctly, however client side says it has received nothing.
//server:
Meteor.publish("orders", function (ordersQueryParams) {
console.log("orders publish: " + JSON.stringify(ordersQueryParams));
if (this.userId && ordersQueryParams){
console.log("orders collection: " + Orders.find({"customer._id": this.userId}, ordersQueryParams).count());
return Orders.find({"customer._id": this.userId}, ordersQueryParams);
}
});
//client:
var ordersPreferences = {
table: {
size: 10
},
query: {
sort: {createdDate:-1},
skip : 0,
limit : 10
}
};
Session.set("ordersPreferences", ordersPreferences);
Meteor.autorun(function(){
var ordersPreferences = Session.get("ordersPreferences");
console.log('subscribing to orders');
Meteor.subscribe("orders", ordersPreferences.query);
}
//both:
Orders = new Meteor.Collection("orders");
Deps.autorun(function(){
if (Meteor.isServer)
console.log("on server orders count is " + Orders.find().count());
if (Meteor.isClient)
console.log("on client orders count is " + Orders.find().count());
});
Server log:
on server orders count is 26
orders publish: {"sort":{"createdDate":-1},"skip":0,"limit":10}
orders collection: 26
Client log:
subscribing to orders
on client orders count is 0
Why server says there are 26 docs, however client insists on 0?
It's driving me nuts :(
I found the problem:
I was "waiting" for my Meteor.user() to become available and had this autorun:
Meteor.autorun(function(handle){
var ordersPage = new OrdersPage();
if (Meteor.user()) {
ordersPage.init();
ordersPage.autorun();
handle.stop();
}
});
if (Meteor.user()) {
return "orders";
}
Once the Meteor.user() was found this function does not need to run, hence I had handle.stop().
Apparently from 0.5.9 handle.stop() stops not only the immediate autorun but everything underneath as well (including the collections).
Might be a bug introduced in Meteor... or might be a new feature.
Related
I have this piece of code in client side:
Tracker.autorun(function () {
if (params && params._id) {
const dept = Department.findOne({ _id: params._id }) || Department.findOne({ name: params._id });
if (dept) {
}
}
});
params will be passed into the url. So, initially we won't have the department data and the findOne method will return null, and then later on, when data arrives, we can find the department object.
But if user enters an invalid id, we need to return them 404. Using tracker autorun, how can I distinguish between 2 cases:
a. Data is not there yet, so findOne returns null
b. There is no such data, even in server's mongodb, so findOne will also returns null.
For case a, tracker autorun will work fine, but for case b, I need to know to return 404
I would suggest you to subscribe to data inside template, like below so you know when subscriptions are ready, then you can check data exists or not
Template.myTemplate.onCreated(function onCreated() {
const self = this;
const id = FlowRouter.getParam('_id');
self.subscribe('department', id);
});
Template.myTemplate.onRendered(function onRendered() {
const self = this;
// this will run after subscribe completes sending records to client
if (self.subscriptionsReady()) {
const id = FlowRouter.getParam('_id');
const dept = Department.findOne({ _id: params._id }) || Department.findOne({ name: params._id });
if (dept) {
// found data in db
} else {
// 404 - no department found in db
}
}
});
If you are using Iron-Router, you may try this hack.
Router.route('/stores', function() {
this.render('stores', {});
}, {
waitOn: function() {
return [
Meteor.subscribe('stores_db')
];
}
});
The sample code above will wait for the subscription "stores_db" to complete, before rendering anyhing. Then you can use your findOne logic no problems, ensuring that all documents are availble. This suits your situation.
This is what I used to do before I completely understand MeteorJS publications and subscriptions. I do not recommend my solution, it is very bad to user experience. Users will see the page loading forever while the documents are being download. #Sasikanth gave the correct implementation.
I'm using SimpleSchema and Collection2.
Before pushing a new version of my app, I'd like to check if any documents would be invalid with the new schema. Is there an automated way to do this?
Or a command line utility?
How do you release new versions with altered schemas?
Thanks!
Mike
From your meteor app on either the client or server (assuming you have access to all the documents you want to check on the client):
MyCollection.find().forEach(function(doc){
check(doc,MyCollectionSchema);
});
You'll probably also want to log the doc and its _id on failures so you can go fix them.
Both SimpleSchema and Collection2 have validating methods.
For SimpleSchema, here are their example codes: https://github.com/aldeed/meteor-simple-schema#validating-data
For Collection2, check the sections starting from Validation Contexts https://github.com/aldeed/meteor-collection2#validation-contexts
I followed #michel's advice and made this server-side route for checking collections.
This is pretty dangerous, because it's cleaning and saving your data. That means it's erasing fields. Probably you didn't want those fields which is why you removed them from your schema, but also, it may be a bug. So caveat emptor cowboy. Obviously, this shouldn't be on a production server.
They can be checked using an url like this:
http://127.0.0.1:4000/v/users
Response:
{
"ok": 1
}
The route:
skipUpdatedAt = true;
Router.route('validate', {
path: '/v/:collection',
where: 'server',
action: function() {
var res = this.response;
var collections = {
users: Meteor.users,
puppies: Puppies,
unicorns: Unicorns,
rainbows: Rainbows,
};
var c = collections[this.params.collection];
if (!c) {
res.statusCode = 404;
return res.end('not found');
}
var s = c.simpleSchema();
var ret = { ok: 0 };
c.find().forEach(function(doc){
var id = doc._id;
try {
s.clean(doc);
check(doc,s);
c.update(id, doc, {validate: false});
ret.ok += 1;
} catch (e) {
ret[id] = {
doc: doc,
error: e.toString(),
};
console.log('=============ERROR');
console.log(doc);
console.log('-------------');
console.log(e);
}
});
res.statusCode = 200;
if (Object.keys(ret).length) res.statusCode = 500;
res.end(JSON.stringify(ret, null, 2));
}
});
I'm trying to upload over 5,000 comments from a CSV and then insert them into a collection.
I get the following:
all done dfae22fc33f08cde515ac7452729cf4921d63ebe.js:24
insert failed: MongoError: E11000 duplicate key error index: ag5Uriwu.comments.$_id_ dup key: { : "SuvPB3frrkLs8nErv" } dfae22fc33f08cde515ac7452729cf4921d63ebe.js:1
Connection timeout. No DDP heartbeat received.
The script at hand:
'click .importComments': function(e) {
var $self = $(e.target);
$self.text("Importing...");
$("#commentsCSV").parse({
worker: true,
config: {
step: function(row) {
var data = row.data;
for (var key in data) {
var obj = data[key];
post = Posts.findOne({legacyId: obj[1]});
var comment = {
// attributes here
};
Comments.insert(comment);
Posts.update(comment.postId, {
$inc: { commentsCount: 1 },
});
}
$self.text("Import Comments");
},
complete: function(results, file) {
console.log("all done");
}
}
});
}
How can I make this work without blowing up with the connection timeout errors?
Locally it seems to work decently but on production (modulus.io) it ends pretty abruptly.
I think the problem here is not to do with DDP but with MongoDB. The DDP connection is timing out due to the MongoDB error.
You're getting a duplicate key error on the _id field. The _id field is automatically indexed by MongoDB and it is a unique index so the same value cannot appear twice in the same collection.
The CSV you're uploading likely has its own _id fields in it meaning Mongo is not generating its own binary fields (which guarantee uniqueness).
So I'd recommend removing the _id field from the CSV if it exists.
You can also try using the following package: http://atmospherejs.com/package/csv-to-collection
I am having an issue with a collection.
It is empty only on the client side. See the following which has been stripped back:
MyCollection = new Meteor.Collection("mycollection");
if (Meteor.isServer) {
var result = MyCollection.find({name: 'MyName'}, {limit: 25}).fetch();
console.log(result);
}
if (Meteor.isClient) {
var result = MyCollection.find({name: 'MyName'}, {limit: 25}).fetch();
console.log(result);
}
I can see the result correct from the server code but not the client. What am i missing?
Assuming you haven't removed autopublish or you're correctly publishing and subscribing, you're probably running the client code before it has received the data from the server. Try this:
if (Meteor.isClient) {
Deps.autorun(function() {
var result = MyCollection.find({name: 'MyName'}, {limit: 25}).fetch();
console.log(result);
});
}
You may get one empty result logged on the client, shortly followed by the correct result (after the client receives the data and reruns the autorun function).
On the introducing article of DDP, I read that anything can be published, but I've read somewhere ( for example in this Stackoverflow comment Publish arbitrary data and automatically update HTML ) that only Collections can be published.
So where is the truth? If we can publish other things than Collections, I would liek to see an example as I can't find one so far.
From the docs: http://docs.meteor.com/#meteor_publish
Publish functions can return a Collection.Cursor, in which case Meteor will publish that cursor's documents. You can also return an array of Collection.Cursors, in which case Meteor will publish all of the cursors.
So at the moment you can only return a Collection via a cursor (result of a Collection.find()).
To return other data you need to hack into the sockjs stream (the socket library meteor uses to communicate to the server). Be aware this does not guarantee compatibility with future versions of meteor. Sockjs is the library used for meteor to communicate between the server (the wire)
from Publish arbitrary data and automatically update HTML*
client side js
sc = new Meteor._Stream('/sockjs');
sc.on('message', function(payload) {
var msg = JSON.parse(payload);
Session.set('a_random_message', JSON.stringify(msg.data));
});
Template.hello.greeting = function () {
return Session.get('a_random_message');
};
server side js
ss = new Meteor._StreamServer();
ss.register(function (socket) {
var data = {socket: socket.id, connected: new Date()}
var msg = {msg: 'data', data: data};
// Send message to all sockets (which will be set in the Session a_random_message of the client
_.each(ss.all_sockets(), function(socket) {
socket.send(JSON.stringify(msg));
});
});
You can also look at Meteor Streams too. See below.
assume you have added meteor streams via atmosphere - mrt add streams
sc = new Meteor.Stream('hello');
if(Meteor.isServer) {
Meteor.setInterval(function() {
sc.emit('a_random_message', 'Random Message: ' + Random.id());
}, 2000);
Meteor.permissions.read(function() { return true });
}
if(Meteor.isClient) {
sc.on('a_random_message', function(message) {
Session.set('a_random_message', message);
});
Template.hello.greeting = function () {
return Session.get('a_random_message');
};
}