MongoDb/Mongoskin - CLEANLY Update entire document w/o specifying properties - mongodb

All the examples I have seen for MongoDb & Mongoskin for update, have individual properties being updated, like so:
// this works when I specify the properties
db.collection('User').update({_id: mongoskin.helper.toObjectID(user._id)},
{'$set':{displayName:user.displayName}}, function(err, result) {
if (err) throw err;
if (result){ res.send(result)}
});
But what if I wanted the whole object/document to be updated instead:
// this does not appear to work
db.collection('User').update({_id: mongoskin.helper.toObjectID(user._id)}, {'$set':user},
function(err, result){
// do something
}
It returns the error:
// It appears Mongo does not like the _id as part of the update
MongoError: After applying the update to the document {_id: ObjectId('.....
To overcome this issue, this is what I had to do to make things work:
function (req, res) {
var userId = req.body.user._id
var user = req.body.user;
delete user._id;
db.collection('User').update({_id: mongoskin.helper.toObjectID(userId)},
{'$set':user}, function(err, result) {
if (err) throw err;
console.log('result: ' + result)
if (result){ res.send(result)}
});
})
It there a more elegant way of updating the whole document, instead of hacking it with:
delete user._id

If you want to update the whole object, you do not need a $set. I am not aware of mongoskin, but in shell you would do something like:
var userObj = {
_id: <something>
...
};
db.user.update({_id: user._id}, user);
Which I think can be translated in your mongoskin in the following way.
db.collection('User').update({_id: user._id}, user, function(){...})
But here is the problem. You can not update _id of the element in Mongo. And this is what your error tells you. So you can remove the _id from your user object and have it separately. Search by this separate _id and update with a user object without _id.

Related

Mongoose findOneAndUpdate() only updates if document already exists, doesn't create new doc if not?

I ran into a problem with my javascript bot, my custom prefixes don't get saved if there isn't yet a custom prefix for that server, if there is though, it does get updated correctly.
await mongo().then(async (mongoose) => {
try {
let newprefix = content.replace(`${prefix}setprefix `, '')
await prefixSchema.findOneAndUpdate({_id: guild.id}, {_id: guild.id, prefix: newprefix})
.then(async () => {
console.log(`updated prefix for guild: ${guild.id}`)
await channel.send(`Succesfully updated prefix for this server to '${newprefix}'`)
message.guild.me.setNickname(`[${newprefix}] - Helix`)
})
.catch(async (err) => {
console.error(`failed to update prefix for guild: ${guild.id}\n${err}`)
await channel.send(`Failed to update prefix.`)
})
console.log("saved to db")
} catch {
console.log("Something went wrong while saving new prefix for a server.")
} finally {
mongoose.connection.close()
}
The bot does print and send that it succesfully updated the prefix, but if there isn't already a document for the guild.id, nothing is saved. What did I do wrong and how can I solve it?
Thanks for reading!
Model.updateOne()
Parameters
[options.upsert=false] «Boolean» if true, and no documents found, insert a new document
MongoDB will update only the first document that matches filter regardless of the value of the multi option.
Use replaceOne() if you want to overwrite an entire document rather than using atomic operators like $set.
Example:
const res = await Person.updateOne({ name: 'Jean-Luc Picard' }, { ship: 'USS Enterprise' });
res.n; // Number of documents matched
res.nModified; // Number of documents modified
please visit https://mongoosejs.com/docs/api.html#model_Model.updateOne for more information.

How do you delete a certain id from a document in mongoose? Edit: Why didn't this work (see edit)

So I have this kind-a-like schema at the moment
user:{ _id: string,
shifts:[_id:string],
name: ... ,
...
}
And now I want to delete a shift._id from all my users who have this.
I allready have an array of all the users their id's who have this shift._id.
I've tried this, with shift_id as the id of the shift i want to delete:
userIdArray.forEach(user_id => {
UserSchema.update({_id: user_id}, {$pull: {shifts: shift_id} });
});
and got the error:
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:535:11)
Can somebody explain me what I did wrong?
Edit:
So what i did was, i called a function named:
function deleteShiftIdInUsers(users, shift_id){
users.forEach(user_id => {
UserSchema.update({_id: user_id}, {$pull: {shifts: shift_id} });
});}
and called this function in my async (req, res, next) route.
Now i just execute this code within the async function instead doing it like code...;
deleteShiftIdInUsers(users, shift_id);
res.status(200).json(...);
still new to js, so what did i do wrong?
I think, You need to call the mongoose function with async/await, you should use updateMany with $in instead of looping for user_id
var ObjectId = require("mongoose").Types.ObjectId
async function deleteShiftIdInUsers(users, shift_id){
users = users.map(user_id => ObjectId(user_id))
return await UserSchema.updateMany({"_id": {"$in": users}}, {"$pull": {"shifts": ObjectId(shift_id)} });
}
when you are calling this function
await deleteShiftIdInUsers(users, shift_id);
res.status(200).json(...);

mongodb difference remove() vs findOneAndDelete() vs deleteOne()

In express and mongodb I want delete document by id
findOneAndDelete() Can not delete by _id, can only delete by field ! why ?
db.collection('quotes').findOneAndDelete({name: req.body.name}, (err, result) => {
if (err) return res.send(500, err)
})
var ObjectId = require('mongodb').ObjectId;
var collection = db.collection('quotes');
collection.remove({_id: new ObjectId(req.body.id)}, function(err, result) {
if (err) {
console.log(err);
} else {
res.send('A darth vadar quote got deleted')
}
});
var mongodb = require('mongodb');
db.collection('quotes', function(err, collection) {
collection.deleteOne({_id: new mongodb.ObjectID(req.body.id)});
});
Difference of three functions?
In short:
findOneAndDelete() returns the deleted document after having deleted it (in case you need its contents after the delete operation);
deleteOne() is used to delete a single document
remove() is a deprecated function and has been replaced by deleteOne() (to delete a single document) and deleteMany() (to delete multiple documents)
findOneAndDelete() should be able to delete on _id.

Unable to enter data in mongo database in express

router.get('/wiki/:topicname', function(req, res, next) {
var topicname = req.params.topicname;
console.log(topicname);
summary.wikitext(topicname, function(err, result) {
if (err) {
return res.send(err);
}
if (!result) {
return res.send('No article found');
}
$ = cheerio.load(result);
var db = req.db;
var collection = db.get('try1');
collection.insert({ "topicname" : topicname, "content": result }, function (err, doc){
if (err) {
// If it failed, return error
res.send("There was a problem adding the information to the database.");
}
else {
// And forward to success page
res.send("Added succesfully");
}
});
});
Using this code, I am trying to add the fetched content from Wikipedia in to the collection try1. The message "Added succesfully" is displayed. But the collection seems to be empty. The data is not inserted in the database
The data must be there, mongodb has { w: 1, j: true } write concern options by default so its only returns without an error if the document is truly inserted if there were any document to insert.
Things you should consider:
-Do NOT use insert function, its depricated use insertOne, insertMany or bulkWrite. ref.: http://mongodb.github.io/node-mongodb-native/2.1/api/Collection.html#insert
-The insert methods callback has two parameters. Error if there was an error, and result. The result object has several properties with could be used for after insert result testing like: result.insertedCount will return the number of inserted documents.
So according to these in your code you only test for error but you can insert zero documents without an error.
Also its not clear to me where do you get your database name from. Is the following correct in your code? Are you sure you are connected to the database you want to use?
var db = req.db;
Also you don't have to enclose your property names with " in your insert method. The insert should look something like this:
col.insertOne({topicname : topicname, content: result}, function(err, r) {
if (err){
console.log(err);
} else {
console.log(r.insertedCount);
}
});
Start your mongod server in a correct path,i.e, same path as that of what you are using to check the contents of collection.
sudo mongod --dbpath <actual-path>

Delete a key from a MongoDB document using Mongoose

I'm using the Mongoose Library for accessing MongoDB with node.js
Is there a way to remove a key from a document? i.e. not just set the value to null, but remove it?
User.findOne({}, function(err, user){
//correctly sets the key to null... but it's still present in the document
user.key_to_delete = null;
// doesn't seem to have any effect
delete user.key_to_delete;
user.save();
});
In early versions, you would have needed to drop down the node-mongodb-native driver. Each model has a collection object that contains all the methods that node-mongodb-native offers. So you can do the action in question by this:
User.collection.update({_id: user._id}, {$unset: {field: 1 }});
Since version 2.0 you can do:
User.update({_id: user._id}, {$unset: {field: 1 }}, callback);
And since version 2.4, if you have an instance of a model already you can do:
doc.field = undefined;
doc.save(callback);
You'll want to do this:
User.findOne({}, function(err, user){
user.key_to_delete = undefined;
user.save();
});
I use mongoose and using any of the above functions did me the requirement. The function compiles error free but the field would still remain.
user.set('key_to_delete', undefined, {strict: false} );
did the trick for me.
At mongo syntax to delete some key you need do following:
{ $unset : { field : 1} }
Seems at Mongoose the same.
Edit
Check this example.
Try:
User.findOne({}, function(err, user){
// user.key_to_delete = null; X
`user.key_to_delete = undefined;`
delete user.key_to_delete;
user.save();
});
if you want to remove a key from collection try this method.
db.getCollection('myDatabaseTestCollectionName').update({"FieldToDelete": {$exists: true}}, {$unset:{"FieldToDelete":1}}, false, true);
Could this be a side problem like using
function (user)
instead of
function(err, user)
for the find's callback ? Just trying to help with this as I already had the case.
Mongoose document is NOT a plain javascript object and that's why you can't use delete operator.(Or unset from 'lodash' library).
Your options are to set doc.path = null || undefined or to use Document.toObject() method to turn mongoose doc to plain object and from there use it as usual.
Read more in mongoose api-ref:
http://mongoosejs.com/docs/api.html#document_Document-toObject
Example would look something like this:
User.findById(id, function(err, user) {
if (err) return next(err);
let userObject = user.toObject();
// userObject is plain object
});
the problem with all of these answers is that they work for one field. for example let's say i want delete all fields from my Document if they were an empty string "".
First you should check if field is empty string put it to $unset :
function unsetEmptyFields(updateData) {
const $unset = {};
Object.keys(updatedData).forEach((key) => {
if (!updatedData[key]) {
$unset[key] = 1;
delete updatedData[key];
}
});
updatedData.$unset = $unset;
if (isEmpty(updatedData.$unset)) { delete updatedData.$unset; }
return updatedData;
}
function updateUserModel(data){
const updatedData = UnsetEmptyFiled(data);
const Id = "";
User.findOneAndUpdate(
{ _id: Id },
updatedData, { new: true },
);
}
I believe that, if you desire remove a specific field into a collection, you should do this:
User.remove ({ key_to_delete: req.params.user.key_to_delete});
you can use
delete user._doc.key