Querying Mongo returns empty array in Template.foo.onCreate in Meteor app - mongodb

I get this code in my Meteor project, in a client/main.js file
Template.panel.onCreated(function loginOnCreated() {
var profile = Session.get('profile');
this.myvar = new ReactiveVar(User.find({}).fetch());
});
And the result of User.find({}) is empty. If I Query this anywhere else (including meteor mongo) I get an Array of users.
So I wonder if it is a problem with the fact that this code is running in client side. In this same file I get this query working in other places, but probably in the server context.
How can I populate this ReactiveVar with the Mongo result as soon as the Template/page is loaded?
If I do something like in Meteor.startup() at Server side:
console.log(User.find({}).count());
It gives me the correct number of Users. Immediately.
#edit
If I just add a setTimeout of a few seconds (it can't be jsut 1 second, it needs a longet time), it works in this very same place.
Template.panel.onCreated(function loginOnCreated() {
//...
setTimeout(function(){
template.timeline.set(User.find({}).fetch());
console.log(timeline)
},3000);
});
So, anyone knows why it takes so long to allow me to do this operation? Any workaround?

User.find({}).fetch() will give list of users on server side only.
You can probably write a meteor method for fetching the user list on server side and give it call using meteor.call.
In the callback function to this call you can assign the result to desired variable.

Related

Meteor Blaze Templates Data Context in onCreated

I am reading in several places that I should be able to get the data context of the current template with Template.currentData();
I found that it seems to only work within an autorun. But after logging the data as a variable there, first it logs null, then it logs the data in the console.
When I try to use the data, like for example trying to pass data._id into a subscription, I get a TypeError in the console. TypeError: Cannot read property '_id' of null. So for some reason, the data is null and I am struggling to find out why.
I have the data context set within my routes using Iron Router:
Router.route('/stock/:stockNumber', {
name: 'stock.detail',
template: 'StockDetail',
data: function () {
return Stock.findOne({
stockNumber: this.params.stockNumber*1
});
}
});
What I am trying to do is get access to the data context so that I can pass some things from it, such as the '_id' into some other subscriptions. What am I doing wrong?
The template is otherwise correctly displaying the data on the page as expected, and I can use Spacebars to show things like {{_id}} for example. But again, I seem to be unable to get access to the data context in Template.StockDetail.onCreated
Ok, so here's what I ended up doing...
Apparently the data context is just simply not available in the onCreated, period. What I had to do was do a Collection.findOne() within the autorun to find the stockItem and set the result to a variable, then use the stockItem._id as the parameter in the new subscription IF the item was found. With both of these things, it seems to work just fine.
Template.StockDetail.onCreated(function () {
let instance = this;
instance.autorun(function () {
instance.subscribe('stock_item', Router.current().params.stockNumber);
let stockItem = Stock.findOne({ // This is what was needed for some reason...
stockNumber: Router.current().params.stockNumber*1
});
if (stockItem) { // ...and, also, this was needed
instance.subscribe('stock_item_scan_log', stockItem._id);
}
});
});
I just don't understand why I can't just easily get the _id some other way. This way just feels incorrect and I don't like it.

Meteor.subscribe on server side

I want to create a backend service which monitors a mongodb collection for new entries. As those are being created, I wish to run processing and update them.
I thought doing so with a Meteor service/app would be a wise idea because Meteor uses 'oplog tailing' which seems ideal for this purpose (I'd rather avoid polling if possible).
As such, I figured creating a minimal server-side-only app should solve it.
So basically, I need something along these lines:
if (Meteor.isServer) {
MyCollection = new Mongo.Collection('myCollection');
Meteor.publish('myCollectionPub', function () {
return MyCollection.find({ some: criteria... });
}
// is there such a thing?
Meteor.serverSideSubscribe('MyCollectionPub',
function (newDocs) {
// process/update newDocs
});
}
According to the Meteor docs, I cannot use Meteor.subscribe() on the server (and indeed it crashes if I try).
Question is:
Are there ways of 'subscribing' to collection updates on the server?
The PeerLibrary server-autorun package (along with it's dependant, reactive-mongo) will provide you with easy server-side observation of collections.
An alternative to #tarmes suggestion is the collection-hooks package, however as pointed out by David Weldon, it will only trigger in instance it is run in:
https://github.com/matb33/meteor-collection-hooks
MyCollection.after.insert(function (userId, doc) {
// ...
});
If you need it to run even when another instance makes a change in the mongo database, you can observe a cursor that is returned from your collection:
MyCollection.find({created_at : {$gt: some_current_time}}).observe({
added: function(item) {
// Alert code
}
});

Add single record to mongo collection with meteor

I am a new user to JavaScript and the meteor framework trying to understand the basic concepts. First of all I want to add a single document to a collection without duplicate entries.
this.addRole = function(roleName){
console.log(MongoRoles.find({name: roleName}).count());
if(!MongoRoles.find({name: roleName}).count())
MongoRoles.insert({name: roleName});
}
This code is called on the server as well as on the client. The log message on the client tells me there are no entries in the collection. Even if I refresh the page several times.
On the server duplicate entries get entered into the collection. I don't know why. Probably I did not understand the key concept. Could someone point it out to me, please?
Edit-1:
No, autopublish and insecure are not installed anymore. But I already published the MongoRoles collection (server side) and subscribed to it (client side). Furthermore I created a allow rule for inserts (client side).
Edit-2:
Thanks a lot for showing me the meteor method way but I want to get the point doing it without server side only methods involved. Let us say for academic purposes. ;-)
Just wrote a small example:
Client:
Posts = new Mongo.Collection("posts");
Posts.insert({title: "title-1"});
console.log(Posts.find().count());
Server:
Posts = new Mongo.Collection("posts");
Meteor.publish(null, function () {
return Posts.find()
})
Posts.allow({
insert: function(){return true}
})
If I check the server database via 'meteor mongo' it tells me every insert of my client code is saved there.
The log on the client tells me '1 count' every time I refresh the page. But I expected both the same. What am I doing wrong?
Edit-3:
I am back on my original role example (sorry for that). Just thought I got the point but I am still clueless. If I check the variable 'roleCount', 0 is responded all the time. How can I load the correct value into my variable? What is the best way to check if a document exists before the insertion into a collection? Guess the .find() is asynchronous as well? If so, how can I do it synchronous? If I got it right I have to wait for the value (synchronous) because I really relay on it.
Shared environment (client and server):
Roles = new Mongo.Collection("jaqua_roles");
Roles.allow({
insert: function(){return true}
})
var Role = function(){
this.addRole = function(roleName){
var roleCount = Roles.find({name: roleName}).count();
console.log(roleCount);
if(roleCount === 0){
Roles.insert({name: roleName}, function(error, result){
try{
console.log("Success: " + result);
var roleCount = Roles.find({name: roleName}).count();
console.log(roleCount);
} catch(error){
}
});
}
};
this.deleteRole = function(){
};
}
role = new Role();
role.addRole('test-role');
Server only:
Meteor.publish(null, function () {
return Roles.find()
})
Meteor's insert/update/remove methods (client-side) are not a great idea to use. Too many potential security pitfalls, and it takes a lot of thought and time to really patch up any holes. Further reading here.
I'm also wondering where you're calling addRole from. Assuming it's being triggered from client-side only, I would do this:
Client-side Code:
this.addRole = function(roleName){
var roleCount = MongoRoles.find({name: roleName}).count();
console.log(roleCount);
if (roleCount === 0) {
Meteor.call('insertRole', roleName, function (error, result) {
if (error) {
// check error.error and error.reason (if I'm remembering right)
} else {
// Success!
}
});
}
}
How I've modified this code and why:
I made a roleCount variable so that you can avoid calling MongoRoles.find() twice like that, which is inefficient and consumes unneeded resources (CPU, disk I/O, etc). Store it once, then reference the variable instead, much better.
When checking numbers, try to avoid doing things like if (!count). Using if (count === 0) is clearer, and shows that you're referencing a number. Statements like if (!xyz) would make one think this is a boolean (true/false) value.
Always use === in JavaScript, unless you want to intentionally do a loose equality operation. Read more on this.
Always use open/closed curly braces for if and other blocks, even if it contains just a single line of code. This is just good practice so that if you decide to add another line later, you don't have to then wrap it in braces. Just a good practice thing.
Changed your database insert into a Meteor method (see below).
Side note: I've used JavaScript (ES5), but since you're new to JavaScript, I think you should jump right into ES6. ES is short for ECMAScript (which is what JS is based on). ES6 (or ECMAScript 2015) is the most recent stable version which includes all kinds of new awesomeness that JavaScript didn't previously have.
Server-side Code:
Meteor.method('insertRole', function (roleName) {
check(roleName, String);
try {
// Any security checks, such as logged-in user, validating roleName, etc
MongoRoles.insert({name: roleName});
} catch (error) {
// error handling. just throw an error from here and handle it on client
if (badThing) {
throw new Meteor.Error('bad-thing', 'A bad thing happened.');
}
}
});
Hope this helps. This is all off the top of my head with no testing at all. But it should give you a better idea of an improved structure when it comes to database operations.
Addressing your edits
Your code looks good, except a couple issues:
You're defining Posts twice, don't do that. Make a file, for example, /lib/collections/posts.js and put the declaration and instantiation of Mongo.Collection in there. Then it will be executed on both client and server.
Your console.log would probably return an error, or zero, because Posts.insert is asynchronous on the client side. Try the below instead:
.
Posts.insert({title: "title-1"}, function (error, result) {
console.log(Posts.find().count());
});

meteor: database is undefined

I'm having some troubles understanding, what i believe is trivial but i can't seem to get my head around it.
I have this publish function in server.js (server only)
Meteor.publish("tikiMainFind", function(){
return tikiDB.find()
})
In app.js (server + client) i'm declaring this mongo collection:
tikiDB = new Mongo.Collection("tiki")
Why is it that this doesn't work in client.js
console.log(tikiDB.find())
//ReferenceError: tikiDB is not defined
Without any idea how you have your app structured, I agree with David Weldon's answer. Check File Load Order to see what order your files are getting loaded.

What's the easiest way to store a large object in Meteor using GridFS?

I'm trying to backup a Lunr Index to Mongo (daily), and because it's running around 13MB, I'm triggering MongoError: document is larger than capped size errors. I'd like to use GridFS to get around the problem, but I'm having a heck of a time getting it to click.
In the simplest terms: Within Meteor, I'd like to save a 13MB JSON object to MongoDB using GridFS, and then be able to retrieve it when necessary -- all only on the server.
I've gone through the File-Collection and CollectionFS docs, and they seem far too complicated for what I'm trying to accomplish, and don't seem to address simply storing the contents of a variable. (Or, more likely, they do, and I'm just missing it.)
Here's what I'd like to do, in pseudo-code:
Backup = new GridFSCollection('backup');
var backupdata = (serialized search index data object);
Backup.insert({name:'searchindex', data:backupdata, date:new Date().getTime()});
var retrieved = Backup.findOne({name:'searchindex'});
Any suggestions?
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var GridStore = MongoInternals.NpmModule.GridStore;
var largedata = new GridStore(db,'name','w'); //to write
largedata.open(function(error,gs){
if (error) return;
gs.write('somebigdata',function(error,done){
if (error) return;
largedata.close();
})
});
var largedata = new GridStore(db,'name','r'); //to read data
largedata.open(function(error,gs){
if (error) return;
gs.read(function(error,result){
if (error) return;
//then do something with the result
largedata.close();
})
});
explanation
First you need the db object, which can be exposed via the MongoInternals https://github.com/meteor/meteor/tree/devel/packages/mongo
Second, you need the GridStore object, you get it from the MongoInternals as well
Then you can create a write or read object and follow the API http://mongodb.github.io/node-mongodb-native/1.4/markdown-docs/gridfs.html
The mongo used by Meteor is 1.3.x whereas the latest node mongodb native driver is 2.x.x http://mongodb.github.io/node-mongodb-native/2.0/api-docs/
So the API may change in the future. Currently, you need to do open and close and the callback are all async, you may want to wrap them with Meteor.wrapAsync (may not work on largedata.open), Future or Fiber