Get all collection records related to a model - javascript

// Note model
attributes: {
// Relations
notebook: {
model: 'Notebook'
},
}
and
// Notebook
attributes: {
// Relations
owner: {
model: 'User'
},
notes: {
collection: 'Note',
via: 'notebook'
}
}
in the controller:
Notebook.findOne({owner: user.id}, function (err, notebook) {
if (err || !notebook) {
return res.serverError(err);
}
// --> until here it goes all fine, finding the Notebook
Note.find().where({notebook: notebook.id}, function (err, notes) {
if (err || !notes) {
return res.serverError(err);
}
return res.json({notebook: notebook, notes: notes});
})
})
It is clear that I am trying to get all Notes related to the Notebook. When debugging, I get until the Note.find() and then I don't even enter the callback, so I don't get any results for Note. The err is null, so I wouldn't know if something's wrong.
I am betting that I have set up my model relations wrongly, but it seems correct to me, as from what I have read in tutorials.
P.S. I do have the records in the database, and the ER relations there are setup correctly, because inserting Note records works without problems.

The models relations seems to be fine.
I think the error come from the fact that there is no callback param in the where method.
Try this instead:
Note
.find()
.where({ notebook: notebook.id })
.exec(function (err, notes) {
...
});

Related

MeteorJS Infinite loop when using meteor call and meteor method

I have a sample code that goes like this:
Client Helper:
getUsername: function (userId) {
Meteor.call("getUsername", userId, function (err, result) {
if(!err) {
Session.set("setUsername", result);
else {
console.log(err);
}
});
return Session.get("setUsername");
}
Server
Meteor.methods({
"getUsername": function (userId) {
var x = Meteor.users.find({_id: userId}, {fields: {username:1}}).fetch()[0];
return x.username;
}
});
The result of this code is an infinite loop of username passing to the client. Is there a way to stop the loop and pass only the data that is needed on the client? I believe the reactivity is causing the data to loop infinitely and I am not sure how to stop it. I tried using "reactive":false on my query in the server but it does not work.
If you want to access username everywhere in client templates (so thats why you put it into session), I would not set it in template helper. I would set it on startup and get username from session in template helpers (without calling server method)
If you need username just in one template, so you want to return its value from your template helper, do not put it into session, just return it in your server method callback.
Based on your sample code, I assume, you have a set of posts and you are retrieving user name based on user id for each post. Then instead of doing it this way, you should use publish composite package to publish related users as well.
Meteor.publishComposite('getPosts', function (postIds) {
return [{
find: function() {
return Posts.find({ _id: { $in: postIds }});
// you can also do -> return Posts.find();
// or -> return Posts.find({ /* or what ever your selector is to get the posts you need*/ });
},
children: [{
find: function(post) {
return Meteor.users.find({
id: post.userId //or the correct field in your post document to get user id
}, {
fields: {
"profile": 1
}
});
}
}}
}]
});
This way your publication will take care of publishing related users along with posts. You don't need to use methods and call them each time.

Unable to access writeresult from remove() in callback

I'm pretty new to Mongodb and have so far successfully used Find, Insert, Update methods. However, with Delete function I am not able to access WriteResult
Insert (Works)
productCollection.insert(newProduct, function (err, result) {
callBack(err, { message: result["insertedCount"] + ' product created successfully.' });
});
Find (Works)
productCollection.find({}).toArray(function (err, docs) {
callBack(err, { product: docs });
});
Delete (Has Issues)
productCollection.remove({ id: pId }, { justOne: 1 }, function (err, result) {
callBack(err, { message: result});
});
Here when I return {message: result} I get
{
"message": {
"ok": 1,
"n": 0
}
}
But I want to actually read "n" from Result to show no of documents deleted
Tried following
{ message: result["n"] }
{ message: result["nRemoved"] }
But in both cases it returns empty object {}.
According to the 2.0 version of Node.js MongoDB Driver API, remove() method is deprecated, you can use removeOne() method instead:
http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#remove
In order to receive number of documents that were removed, you need to use safe mode to ensure removal of documents. To do this, specify write concern by passing {w: 1} to removeOne() function:
productCollection.removeOne({ _id: pId }, { w:1 }, function(err, r) {
// number of records removed: r.result.n
callBack(err, { message: r });
});
Hope this helps.
Thanks Yelizaveta for pointing out the deprecated method. However in my case, following worked
productCollection.removeOne({ id: pId }, { w: 1 }, function (err, r) {
callBack(err, { message: r.result["n"]});
});
I could not get r.result.n working instead r.result["n"] worked, which I don't understand.

How to reference associated models in a one-to-many relationship with Sails 0.10.x

I'm using Sails.js version 0.10.x, and am just starting to try out it's associactions stuff.
In my scenario I have a User who has many Documents.
so in /api/models/User.js I have:
module.exports = {
// snipped out bcrypt stuff etc
attributes: {
email: {
type: 'string',
unique: true,
index: true,
required: true
},
documents: {
collection: 'document',
via: 'owner'
},
}
};
and in /api/models/Document.js I have:
module.exports = {
attributes: {
name: 'string',
owner: {
model: 'user'
}
}
};
In my DocumentController I have the following:
fileData = {
name: file.name,
owner: req.user
}
Document.create(fileData).exec(function(err, savedFile){
if (err) {
next(err);
} else {
results.push({
id: savedFile.id,
url: '/files/' + savedFile.name,
document: savedFile
});
next();
}
});
Looking in my local mongo database via the command line I can see that the documents have the owner field set as follows "owner" : ObjectId("xxxxxxxxxxxxxxxxxxxxxxxx") which is as expected.
However when I inspect the req.user object later in the DocumentController via sails.log.debug("user has documemts", req.user.documents); I see
debug: user has documents [ add: [Function: add], remove: [Function: remove] ]
And not an array of Document objects.
In my resulting slim template
if req.user.documents.length > 0
ul
for doc in req.user.documents
li= doc.toString()
else
p No Documents!
I always get "No Documents!"
I seem to be missing something obvious but I'm not sure what that is.
I worked this out by wading through the Waterline source code.
Firstly, as I hoped, both sides of the association are affected by the creation of the Document instance, and I simply needed to reload my user.
Within the controller this is as simple as User.findOne(req.user.id).populateAll().exec(...)
I also modified my passport service helper as follows
function findById(id, fn) {
User.findOne(id).populateAll().exec(function (err, user) {
if (err) return fn(null, null);
return fn(null, user);
});
}
function findByEmail(email, fn) {
User.findOne({email: email}).populateAll().exec(function (err, user) {
if (err) return fn(null, null);
return fn(null, user);
});
}
Now the user, and its associations, are loaded properly per request.
I had to dig through the source to find the populateAll() method as it's not actually documented anywhere I could find. I could also have used populate('documents') instead but I am about to add other associations to the User so need populateAll() to load all the relevant associations.
Waterline associations docs
Waterline /lib/waterline/query/deferred.js#populateAll

Is it possible to update an already open document in mongodb

In JS, Is there a better way of doing the following:
I'm finding a user, and then checking password, then I wish to update the same users' document.
Can I leverage the already open document (var doc) for updating? Or do as the code below does and re-search for name:name when updating.
user_collection.findOne({ name:name }, function(err, doc) {
if(err)
throw err;
if(doc) {
// verify doc.password etc
user_collection.update({ name:name }, {$set: { last_joined:last_joined }}, { upsert:true }, function(err, doc) {
if(err) {
// log error
}
});
}
});
Yes - use the save method.
The document you have in doc is an in-memory copy of the record in the database. If you want to modify it then save it to the database, you need to either use the update method as you do, or use save(modified_doc).
Note: as freakish said, you probably should use user_collection.update({ _id: doc._id }, ...) instead of searching for name again, since it may not be unique.

Allow collection to update from client in Meteor

Just hit an insanely frustrating roadblock in prototyping. I need to update and increment values an array inside of a collection. To do this, I'm accessing the collection using the MongoDB syntax like so:
Players.update({_id: Session.get('p1_id'), 'opponents.$.id' : Session.get('p2_id')},
{$inc: {
'games_played' : 1
}}
);
When this runs I get an error saying: Uncaught Error: Not permitted. Untrusted code may only update documents by ID. [403]
Now, I searched the hell out of this and I know that it came down in an update and why they only allow update by id's. But my problem is that I can't seem to find a way around it. I tried forcing it by adding this to if (Meteor.isServer):
Players.allow({
insert: function(userId, doc, fields, modifier){
return true;
},
update: function(userId, doc, fields, modifier){
return true;
},
remove: function(userId, doc, fields, modifier){
return true;
}
});
Nothing seems to work, and all the examples I find talk about using a Meteor method (not really sure what that is) or are doing userId validation (I dont have any users and don't want to add them right now). I'm just prototyping/sketching and I'm not concerned about security. How can I proceed here?
Here's how you can make this into a method:
Meteor.methods({
incrementGames: function (player1Id, player2Id) {
check(player1Id, Meteor.Collection.ObjectID);
check(player2Id, Meteor.Collection.ObjectID);
Players.update({
_id: player1Id,
'opponents.$.id': player2Id
}, {
$inc: {
'games_played' : 1
}
}, function(error, affectedDocs) {
if (error) {
throw new Meteor.Error(500, error.message);
} else {
return "Update Successful";
}
});
}
});
And on your client:
Meteor.call("incrementGames", Session.get('p1_id'), Session.get('p2_id'), function(error, affectedDocs) {
if (error) {
console.log(error.message);
} else {
// Do whatever
}
});
You just got the update wrong. The first parameter of the update method should be the id. the second parameter is an object containing the modifiers.
Players.update(playerId, {$inc:{games_played:1}});
Optionally you can add a callback containing error as the first parameter and response as the second parameter.

Categories