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

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 });

Related

Insert if data doesn’t exist in the document – How to do insert new field and data with Mongoose?

I want to insert to new field and data in existing document, if there is no data in it.
if (repoData.detailViewCounter == undefined) {
console.log('create 0')
Repo.findOneAndUpdate({
'owner.login': userId,
'name': pageId
}, {
detailViewCounter: 0
},
{new: true, upsert: true})
}
So When condition like this, and there is no detailViewCounter field, insert new field with number 0.
{
'owner.login': userId,
'name': pageId
}
But When I run this code and check the MongoDB Compass, there is still no data.
And I also create repo schema detailViewCounter with type:Number
change in option upsert: false
one major problem is field name "owner.login" this is causing the issue, this will search in object owner: { login: userId } but in actual we have string "owner.login", so this is not able to find any matching document, and this will not update record.
you can check after removing condition on field owner.login, it will work
Repo.findOneAndUpdate({
'name': pageId
},
{ detailViewCounter: 0 },
{ new: true, upsert: false }
)
Look at MongoDB restriction on field names, and good question in SO.

How to create in mongoose if not exists but not update

I've only found ways to create OR update a document. I need to create if not exists but do nothing if it already exists. How to do it in mongoose?
Please note that findOneAndUpdate won't work, because if it fins the document, it updates it! I don't want that.
UPDATE:
I just want to create a variable called order_number that can be incremented. However, to increment, I must make sure the document exists. However, I cannot update its value in case it exists.
I tried:
let r = await OrderNumber.findOneAndUpdate({ unique: 0 }, { unique: 0, order_number: 0 }, { upsert: true });
It successfully creates when it does not exist, but always updates order_number to 0. I don't want this to happen.
As only way I've found it via two DB calls as in general inserts doesn't have any filters unless findOneAnd* or upsert on updates are used - which we're not looking at. Then only option is to make two calls, here is basic code, please wrap those in proper function & try/catch to add error handling :
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const orderNumberSchema = new Schema({
any: {}
}, {
strict: false
});
const OrderNumber = mongoose.model('order_number', orderNumberSchema, 'order_number');
let resp = await OrderNumber.findOne({ unique: 0 });
if (!resp) {
let orderNumberObj = new OrderNumber({ unique: 0, order_number: 0 })
let r = await orderNumberObj.save();
}

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});

adding multiple same documents using addtoset command in mongoose

My schema is
var UserQuizSchema = mongoose.Schema({
uid:{type:ObjectId,required: true,index:true},
answer:[{content:String, qid:ObjectId ,time:Date }],
});
In this schema, 'uid' represents user identifier, while the 'answer' array stores the answers the student had answered. in each answer, qid relates to the question ID, and 'content' is the student's real answer, 'time' is the modified time stamp for the answer.
Here I use mongoose to upsert the new answers into the array
function updateAnswer(uid,question_id,answer,callback){
var options = { new: false };
var quiz_id = mongoose.Types.ObjectId(quiz_id);
var qid = mongoose.Types.ObjectId(question_id);
UserQuizModel.findOneAndUpdate({'uid':uid},{'$addToSet':{'answer':{'qid':qid, 'content':answer} } },options,function(err,ref){
if(err) {
console.log('update '.red,err);
callback(err, null);
}else{
console.log('update '.green+ref);
callback(null,ref);
}
})
}
In the common sense, by using addToSet command, the element in the answer array should be unique, but in my example, the answer array could have multiple same embedded documents only except each embedded document has one unique OjbectId _id
such as
answer:
[ { qid: 5175aecf0e5b061414000001, _id: 518a5e5895fc9ddc1e000003 },
{ qid: 5175aecf0e5b061414000001, _id: 518a5e5f95fc9ddc1e000004 } ] }
you see the qid of two embedded documents are the same, but _id are different.
Why there is a additional _id, I don't put it the schema design ??
You can disable the _id in your embedded objects by explicitly defining a schema for the elements with the _id option set to false:
var UserQuizSchema = mongoose.Schema({
uid:{type:ObjectId,required: true,index:true},
answer:[new Schema({content:String, qid:ObjectId, time:Date}, {_id:false})]
});

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