Mongoose Updating Certain Fields of a Document - node.js

I'm trying to implement an update method for our API and I'm kinda new to the Node so I didn't know what would be the best practice to carry out the task of updating some fields of a document. Let me elaborate, we have a user model which keeps basic info of a user like name, age, sex, school, bio, birthday etc. Our update method should work as this, the request of the method includes the new values of the fields provided such as {bio:'newBio'} or {school:'newSchool', name:'newName'} I must update the provided fields with the provided data and leave the rest as they are. I was wondering what the best approach to the problem at hand would be. Thanks in advance

The easiest approach what i can think of is to use $Set to perform the update operations.
an example would be :
var updatedUsers= function(db, callback) {
db.collection('users').updateMany(
{ "_id": "value"},
{
$set: { bio: "new bio" }
}
,
function(err, results) {
console.log(results);
callback();
});
};
and invoke your above function as :
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
updatedUsers(db, function() {
db.close();
});
});

Related

Mongoose nested reference population

I'm having trouble understanding some of the concepts behind mongoose's populate methods. I had an embedded approach working first, although, as I feared a big overhead of data and out-of-sync documents going around I tried changing the paradigm to ref other documents.
My Schema is similar to the following (removed irrelevant properties):
var userSchema = mongoose.Schema({
name: {type: String, default:''},
favorites:{
users:[{type: Schema.Types.ObjectId, ref: this}],
places:[{type: Schema.Types.ObjectId, ref: PlaceSchema}]
}
});
module.exports = mongoose.model('User', userSchema);
Now I'm trying to get a User's favorites like this:
User.findOne({_id: currentUser}).exec(function(err, user){
console.log(user);
if (err)
throw err;
if(!user){
console.log("can't find user");
}else{ // user found
user.populate('favorites.users');
user.populate('favorites.places');
// do something to the 'user.favorites' object
}
});
Although this doesn't work as intended, as both user.favorites.users and user.favorites.places come up undefined.
I thought that I could populate as above, but apparently, that's not the case. From what I read, I must be missing something indicating (maybe) the model of the ref'ed document? This flow is very new to me and I'm kinda lost.
Is there anyway I can get an array of users and places by populating my query result as above? I tried populate chained with exec and it doesn't work either. Is there a better way to achieve this result?
EDIT: In case it's needed, in the DB, a User document shows up as:
{
"_id": "56c36b330fbc51ba19cc83ff",
"name": "John Doe",
"favorites": {
"places": [],
"users": [
"56b9d9a45f1ada8e0c0dee27"
]
}
}
EDIT: A couple more details... I'm currently storing/removing the reference ObjectIds like this (note targetID is a String):
user.favorites.users.push({ _id: mongoose.Types.ObjectId(targetID)});
user.favorites.users.pull({ _id: mongoose.Types.ObjectId(targetID)});
Also, I need to populate the users and places with their respective documents aswell, I think that might not be clear in my original question.
Well, I figured out what I needed by paying proper attention to the docs (and also with #DJeanCar 's (+1'd) help/pointers).
By Mongoose's docs regarding Populating across multiple levels, I've reached this solution:
User.findOne({_id: currentUser})
.populate({path:"favorites.users", populate: "name"})
.populate({path:"favorites.places", populate: "name"})
.exec(function(err, user){
if(err)
throw err;
if(!user){
console.log("couldnt find source user");
}else{
// here, user.favorites is populated and I can do what I need with the data
}
});
Also, from what I could tell, you can also pass select: "field field field" in populate()'s options, should you need to filter the document fields you require after population.
Hope this helps someone with similar issues!
Try:
User
.findOne({_id: currentUser})
.populate('favorites.users')
.populate('favorites.places')
.exec( function (err, user) {
// user.favorites.users
// user.favorites.places
});

Update data in MongoDB with Mongojs using findAndModify()

Yet another first-timer problem here. This gets data from a database and displays it in some text fields (that part is not shown in the code below) and after the user edits it the data should be updated in the database via the findAndModify() method and I think this is where the issue lies. There are no errors, it just doesn't do anything. EDIT The following error is received: MongoError: Either an update or remove=true must be specified
server.js
MongoClient.connect("mongodb://user:secretPassword#aws-us-east-1-portal.7.dblayer.com:10712,aws-us-east-1-portal.10.dblayer.com:10316/database", function(err, db) {
if (err) throw err;
var contactList = db.collection("contactList");
app.put('/contactList/:id', function(req, res) {
var id = req.params.id;
console.log("edited: " + req.body.name); //works up until here
contactList.findAndModify({
query: {_id: mongojs.ObjectId(id)},
update: {$set: {name: req.body.name, email: req.body.email, number: req.body.number}},
new: true
}, function (err, doc) {
res.json(doc);
})
});
controller.js
$scope.update = function() {
$http.put('/contactList/' + $scope.contact._id, $scope.contact).success(function(response) {
refresh();
})
};
If this were me I would first do a couple of things:
Before your call to findAndModify just do a simple find using your query. Make sure you can actually find the object using your query. If that works you know that the 'find' part of the findAndModify is probably ok.
Do some console logging inside the callback handler of the findAndModify call. As it stands you do not do anything if an err is returned from the findAndModify call. It is possible your call is returning an error that you are just ignoring and it may provide some additional insight into your problem.
I would try these two first and see if it helps.
Update:
Example using native:
collection.findAndModify(
{ field: 'some value' },
[],
{ $set: { field2: 'some new value' } },
{ new:true },
function(err, doc) {
//handle err and doc
});

How do I update a doc in Cloudant using Cloudant Node.js

So, what I'm doing should be really simple, and maybe it is and I'm just doing something wrong. I want to update an existing document in my database but I'm having some issues, can someone please advise?
Nano's Documentation states the following for insert:
db.insert(doc, [params], [callback])
Therefore, I should surely be able to do the following:
var user = {
'firstname' : 'my',
'secondname' : 'name'
};
db.insert(user, {_rev: '2-cc5825485a9b2f66d79b8a849e162g2f'}, function(err, body) {});
However, whenever I try this, it creates an entirely new document. If I do the following then it will indeed update my document, but of course, with nothing in this document other than the _rev:
db.insert({_rev: '2-cc5825485a9b2f66d79b8a849e162g2f'}, function(err, body) {});
So the question is, how do I pass in my object and get it to update, rather than creating a new record?
var user = {
'firstname' : 'my',
'secondname' : 'name',
'_id': <id from prev object>,
'_rev': <rev from prev object>
};
db.insert(user, function(err, body) {});
the _id and _rev are both required in order for the update to work.
they should be in the object that you are sending also.
The first argument in the db.insert(...) command is the document which you want to create/update. If you pass in a doc with a ._rev attribute, then it will replace the document with that same _rev in Cloudant with the doc passed in as the first argument of your db.insert(...). If the doc does not include a ._rev attribute, then Cloudant will create an entirely new document.
This explains the behavior you were experiencing in both the scenarios you tried. In order to make an update to your doc, make sure to include ._id and ._rev attributes, along with the rest of your doc's attributes when you use it as the first argument to your db.insert(...) function.
Got it! Here's the solution:
db.get('user', { revs_info: true }, function(err, doc) {
if (!err) {
console.log(doc);
doc.firstname = 'my';
doc.secondname = 'name';
db.insert(doc, doc.id, function(err, doc) {
if(err) {
console.log('Error inserting data\n'+err);
return 500;
}
return 200;
});
}
});
First get the record id and rev id (_id,_rev)
const newData={email:"aftabfalak956#gmail.com",name:"Aftab Falak"}
cloudant.use("user").find({selector:{_id:"user_id"}}, (err, documents) => {
var revision = documents.docs[0]._rev;
const data={...documents.docs[0],...newData};
cloudant.use("user").insert(data,{_rev:revision},function(err){
if (!err) {
console.log('success', 'The record was updated successfully');
}
else {
console.log('failure', err);
}
});
});

How to save a modified object in mongodb using Node.JS Driver

I want to find, modify and afterwards save an object in MongoDB. It looks like that:
var message = req.body;
db.collection('user', function(err, collection) {
collection.findOne({'facebook_id':req.params.facebook_id}, function(err, item) {
if(item) {
item.messages.push({'value': message.value, 'date': message.date});
//save Object
}
});
});
How can I now save the changes I made to the database?
Or should I instead use .update()? The problem here is, that I don't want to swap the whole object, but much more insert something into an array of that object.
Thanks & Best,
Marc
collection.update({'facebook_id':req.params.facebook_id},
{$push: { messages: {'value': message.value, 'date': message.date} } }, function(err) {
});
Use the $push operator to add a value to an array directly in the database.
http://docs.mongodb.org/manual/reference/operator/update/push/
Note that this is much more efficient than updating the entire object, especially for large objects.
db.collection.update ({'facebook_id':req.params.facebook_id}, item, function (err) {
if (err) return next (err);
});

Updating a model instance

I want to understand what the correct way to update a model instance is, using mongoose, ie:
Having:
User = {
username: {
type: String,
indexed: true
},
email: {
type: String,
indexed: true
},
name: String,
.........
};
I'm sending the whole form through ajax to a controller.
So far, i know of two options:
app.put('/users/', function(req, res){
var id = ObjectId(req.body._id);
User.findOne({_id: id}, function(err, user){
user.name = req.body.name;
....
....
user.save();
});
});
or:
app.put('/users/', function(req, res){
var id = ObjectId(req.body._id);
delete req.body._id
User.update({_id: id}, req.body, function(err){
.....
};
});
Both ways have disadvantages:
In the first approach i have to map all properties one by one;
In the second approach i have to delete all properties that can't be changed;
there is a third possible approach that would make me send from client-side, only the changed properties, but i think that would be a big hassle to.
Is there a good, standardized way that i'm not seeing, to do this?
Thanks
This is the approach I typically use, it involves a small npm package called mongoose-mass-assignement:
https://github.com/bhelx/mongoose-mass-assignment
The docs are pretty self explanatory, easy to use. It basically does the delete for you. So you add protected:true to your model, and then it deletes those properties for you. I like this approach because it still allows me to use the req.body as is. You can use this for updates and inserts as well.
A variant of the first approach is to use underscore's extend method to apply all properties of the body to your model instance:
app.put('/users/', function(req, res){
var id = ObjectId(req.body._id);
User.findOne({_id: id}, function(err, user){
_.extend(user, req.body);
user.save();
});
});
However, be sure to delete any properties you don't want the user to be able to set from req.body first.

Resources