Delete a key from a MongoDB document using Mongoose - mongodb

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

Related

FindAndModify: get newDocument along with info whether doc was inserted or updated

Using findAndModify:
I need to get the new resulting document
I need to know if an insert or update was done
var newUpdate = {
$set : newData,
$setOnInsert: {created_at: new Date()}
};
var options = {
upsert :true,
new: true,
};
collectionDriver.findAndModify(colName, query, newUpdate, options, function(err,resultDoc)
{
if (err) {
} else {
}
});
I get the new document, how can I know if an insert/update was happened?
One possible, though not the most efficient way, can be to check for presence of record before issuing the modify command because if you need the new record, then you cannot check if it previously existed.

MongoDB check if an embedded document is empty?

I have the following document and I need to return name who has no votes
user:
{
'user_id': (id),
'name': (name),
'votes': {
(vote_type): (No.of votes of this type)
},
}
I tried with db.user.find({votes: null},{name:true}).pretty(). No results show up. How do I fix this. Appreciate your help!
By default in MongoDB if a field is unpopulated MongoDB won't create the property for that document so you should be able to test if it $exists:
db.user.find({votes: {$exists: false}},{name: true}).pretty()
If you are setting it to null though you should be able to query for it as you described with checking for null:
db.user.find({votes: null},{name: true}).pretty()
If neither works you might be storing an empty object {} by default and will need to check for that:
db.user.find({votes: {}},{name: true}).pretty()
You can iterate over all the elements and add to those some new field f.e. "withNoVotes".
db.user.find().forEach(function(doc) {
if(doc.votes) {
for(var voteType in votes) {
if(votes[voteType] && votes[voteType] > 0) {
db.user.update({_id: doc._id}, {$set: {withNoVotes: true}});
break;
}
}
}
});
Now find them using usual query to find by this new field
db.user.find({withNoVotes: true});

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

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.

MongoDB - I want to specify a primary key and operate it with name other than `_id`

I had a Tag schema (defined with mongoose):
var Tag = new Schema({
_id: String // Not ObjectId but the name of the tag.
});
I want to use the tag name as its _id, but I don't want to operate this field with name _id. For example, I would like to add a new tag with code new Tag({name: 'tagA'}) instead of new Tag({_id: 'tagA'}). Since the code is more expressive in this way.
So I need to change name to _id. One method would be using the pre-save hook.
Tag.pre('save', function(next) {
if (!this._id && this.name) this._id = this.name;
next();
});
Are there ways better than this one?
This seems to be the best option I found with mongoose for implementing custom primary keys.
<schemaToHook>.pre('save', true, function(next, done) {
// trigger next middleware in parallel
next();
if (!this._id && this.name) {
this._id = this.name;
}
done();
});
I am using a parallel middleware and expecting better performance. Also, while using the above implementation you might want to consider using findOneAndUpdate with upsert = true for INSERT or REPLACE equivalent implementation.
MyModel.findOneAndUpdate(
{foo: 'bar'}, // find a document with that filter
modelDoc, // document to insert when nothing was found
{upsert: true, new: true, runValidators: true}, // options
function (err, doc) { // callback
if (err) {
// handle error
} else {
// handle document
}
}
);

Mongo: find items that don't have a certain field

How to search for documents in a collection that are missing a certain field in MongoDB?
Yeah, it's possible using $exists:
db.things.find( { a : { $exists : false } } ); // return if a is missing
When is true, $exists matches the documents that contain the field, including documents where the field value is null. If is false, the query returns only the documents that do not contain the field.
If you don't care if the field is missing or null (or if it's never null) then you can use the slightly shorter and safer:
db.things.find( { a : null } ); // return if a is missing or null
It's safer because $exists will return true even if the field is null, which often is not the desired result and can lead to an NPE.
just for the reference here, for those of you using mongoose (v6) and trying to use the $exists to find a field that is not defined in your mongoose schema, mongoose v6 will escape it.
see here https://mongoosejs.com/docs/migrating_to_6.html#strictquery-is-removed-and-replaced-by-strict
for example:
const userSchema = new Schema({ name: String });
const User = mongoose.model('User', userSchema);
// By default, this is equivalent to `User.find()` because Mongoose filters out `notInSchema`
await User.find({ notInSchema: 1 });
// Set `strictQuery: false` to opt in to filtering by properties that aren't in the schema
await User.find({ notInSchema: 1 }, null, { strictQuery: false });
// equivalent:
await User.find({ notInSchema: 1 }).setOptions({ strictQuery: false });