Querying a mongo collection for objects without certain attributes using mongoose - node.js

I'm looking to write a find clause that will search for objects where a certain attribute exactly matches a condition or if the object doesn't have that attribute at all.
Currently I am using:
Card.find( { $or: [ { "playerClass": req.params.className }, { "playerClass": {'$eq': null } } ] }, function(err, docs) {
res.json(docs);
});
But this yields no results.
I tried $equals before as well to no avail -- as a newcomer to Mongo, what am I doing wrong?
Thanks

null counts for missing, so you could use $in:
Card.find({ "playerClass" : { "$in" : [null, req.params.className] } }, callback)

Related

How to remove object from array in Mongoose using updateMany?

I am trying to get an object removed from an array in MongoDB using Mongoose/Node and so far I have been unable to get it removed. I have read docs and SO posts to no luck. I'm hoping someone could give me specific advice on a solution not just a link to another post which is vaguely related
Mongo document example
{
managers: [{
userID: value,
// other fields and values
},
// Another object for another manager
]
}
Code I am trying to use
await Model.updateMany(
{'managers.userID': userID}, // This should be the query part
{$pullAll: {'managers': {'userID': userID}}} // This should be the update part
)
Using $pullAll you have to match the whole object.
Apart from the syntax is not correct in your query, check how in this example the id 1 is not removed but it is the id 2. That's because $pullAll expects to match the whole object. And this other example works removing both ids.
Then, to do your query you can use $pull and $in like this:
await Model.updateMany({
"managers.userID": userID
},
{
"$pull": {
"managers": {
"userID": {
"$in": userIDs
}
}
}
})
Example here
Note how $in expects an array. If you only want to remove based on one value you can avoid $in:
await Model.updateMany({
"managers.userID": userID
},
{
"$pull": {
"managers": {
"userID": userID
}
}
})
Example here

MongoDB return nested array of objects minus a property within the object (also .then() doesn't work with aggregation)

I'm very new to mongodb and am having difficulty getting to a solution for my use case. For example I have the following document:
{
_id : ObjectId('5rtgwr6gsrtbsr6hsfbsr6bdrfyb'),
uuid : 'something',
mainArray : [
{
id : 1,
title: 'A',
array: ['lots','off','stuff']
},
{
id : 2,
title: 'B',
array: ['even','more','stuff']
}
]
}
I'd like to have the following returned:
{
uuid : 'something',
mainArray : [
{
id : 1,
title: 'A'
},
{
id : 2,
title: 'B'
}
]
}
I've tried various combinations of using findOne() and aggregate() with $slice and $project. With findOne(), if it worked at all, the who document would be returned. I am unable to test whether attempts at aggregating work because .then((ret)=>{}) promises don't seem to work in node.js for me with it (no issues with findOne). Calling a function like so
return db.myCollection.aggregate([
{
$match: {
_id:ObjectId(mongo_id)
}
},
{
$project : {
mainArray: {
id:1,
title:1
}
}
}
],function(err,res){
console.log(res)
return res
})
logs the entire function and not the droids I'm looking for.
You're missing toArray() method to obtain the actual result set. Instead you're returning the aggregation cursor object. Try this.
return db.myCollection.aggregate([matchCode,projectCode]).toArray().then(
data => {
console.log(data);
return data;
},
error => { console.log(error)});
The documnetation on aggregation cursor for MongoDB NodeJS driver can
be found here
http://mongodb.github.io/node-mongodb-native/3.5/api/AggregationCursor.html#toArray
This is an alternative solution (to the solution mentioned in the comment by #v1shva)
Instead of using aggregation you can use projection option of .findOne() operation.
db.myCollection.findOne(matchCode, {
projection: { _id: false, 'mainArray.array': false } // or { _id: -1, 'mainArray.array': -1 }
})

mongoose find by nested object ids return the entire document [duplicate]

This question already has answers here:
Find in Double Nested Array MongoDB
(2 answers)
Closed 5 years ago.
This is my document structure:
{
"_id": "590dc8b17e52f139648b9b94",
"parent": [
{
"_id": "590dc8b17e52f139648b9b95",
"child": [
{
"_id": "590dc8b17e52f139648b9b8f"
},
{
"_id": "590dc8b17e52f139648b9b90"
}
]
},
{
"_id": "590dc8c57e52f139648b9b9b",
"child": [
{
"_id": "590dc8c57e52f139648b9b96"
}
]
}
]}
I'm trying to return only the information about the nested object which i'm looking for with its _id.
router.get('/child/:id', (req, res) => {
Project.findOne({ 'parent.child._id': req.params.id },
(err, results) => {
if (err)
res.status(500).send(err);
res.status(200).json(results.parent[0].child.id(req.params.id));
}
)});
the problem is that results contains the entire document which may contain multiple instances of parent
So obviously my code only works if the known child _id it's in the first parent.
How can i fix it?
many thanks
so apparently the unwind and match solution is good but for mongoose you need to explicitly say that the id is an ObjectId type.
{$match:
{ "parent.child._id": new mongoose.Types.ObjectId(id) }
}
found it here
Basically You can $unwind the parent first in an aggregate and then match with child id. This will make sure it gives you just the parent object and not entire document. Also if matching id need not be inside the first parent object but it can be anywhere inside parent object array. It will return the require object from parent array which contains search id in its child array.
db.project.aggregate([
{ $unwind : '$parent'},
{ $match :
{ "parent.child._id" : '590dc8b17e52f139648b9b8f' }
}
]);
Mongooese Syntax :
//mongoose
let Project = mongoose.model('project', yourProjectSchema);
Project.aggregate([
{ "$unwind" : '$parent'},
{ "$match" :
{ "parent.child._id" : req.params.id }
}
]);

Use mongoose / mongodb to $pull nested data from an array

say I have this array property ('articles') on a Mongoose schema:
articles: [
{
kind: 'bear',
hashtag: 'foo'
},
{
kind: 'llama',
hashtag: 'baz',
},
{
kind: 'sheep',
hashtag: 'bar',
}
]
how can I use
$pull https://docs.mongodb.org/manual/reference/operator/update/pull/
to remote objects from this array by checking the value of hashtag to see if it matches a pattern?
For example, if I want to remove an object in the articles array where hashtag='foo'.
My best guess is the following, but it doesn't seem to work:
var data = {
"$pull": {
"articles": {
"$elemMatch": {
"hashtag": "foo"
}
}
}
};
Model.update({},data); //doesn't quite work
this one seems to work:
var data = {
"$pull": {
"articles": {
"hashtag": 'foo'
}
}
};
Model.update({},data); //seems to work
if you have a better solution or if you can show an alternate solution please provide an answer thank you
The $pull operator is basically a "mini query" in itself, and as such operators like $elemMatch become irrelevant as the "query" is directly performed on each array member anyway.
As such:
Model.update(
{},
{ "$pull": { "articles": { "hashtag": "foo" }},
{ "multi": true },
function(err,numAffected) {
// handle result here
}
)
So it is looking for the matching properties within the ( correct ) specified array in all array documents, and then removing them where there is a match.
The .update() also just returns the number of affected documents, and is usually used with { "multi": true } when you expect more than one document to be updated.
If you are just looking for "one" document, or expect the modified document in response, then use .findOneAndUpdate() instead:
Model.findOneAndUpdate(
{},
{ "$pull": { "articles": { "hashtag": "foo" }},
{ "new": true },
function(err,numAffected) {
// handle result here
}
)
Though not really practical without any selection criteria, since this just updates the first document found in the collection.

Mongoose update first five documents

Hello I am trying to update only first five documents from my schema using mongoose. I found a way to update documents by giving condition but cant update only first five.
I got this code
mongoose.model('person').update( {active:false} , {multi: true} , function(err,docs) { ... });
Thank you
The key here is to get the first 5 _id values matching your condition and then pass those using $in to your update selection:
mongoose.model('person').find({ "active": { "$ne": false }}).limit(5)
.exec(function(err,docs) {
var ids = docs.map(function(doc) { return doc._id } );
mongoose.model('person').update(
{ "_id": { "$in": ids } },
{ "$set": { "active": false } },
{ "multi": true },
function(err,numAffected) {
}
);
});
Also notice the correct arguments to the update statement with a "query" and "update" block. You should also be using update operators such as $set, otherwise you are overwriting the existing document content with just the contents of the "update"block.
Also see the .update() method from the core documentation.

Resources