How to get all matching subdocuments in mongoose? - mongodb

In my mongodb (using Mongoose), I have story collection which has comments sub collection and I want to query the subdocument by client id, as
Story.find({ 'comments.client': id }, { title: 1, 'comments.$': 1 }, function (err, stories) {
...
})
})
The query works except that it only returns the first matched subdocument, but I want it to return all matching subdocuments. Did I miss an option?
EDIT:
On Blakes Seven's tip, I tried the answers from Retrieve only the queried element in an object array in MongoDB collection, but I couldn't make it work.
First tried this:
Story.find({'comments.client': id}, { title: 1, comments: {$elemMatch: { client: id } } }, function (err, stories) {
})
It also returns the first match only.
Then, I tried the accepted answer there:
Story.aggregate({$match: {'comments.client': id} }, {$unwind: '$comments'}, {$match : {'comments.client': id} }, function (err, stories) {
})
but this returns nothing. What is wrong here?
UPDATE:
My data structure looks like this:
{
"_id" : ObjectId("55e2185288fee5a433ceabf5"),
"title" : "test",
"comments" : [
{
"_id" : ObjectId("55e2184e88fee5a433ceaaf5"),
"client" : ObjectId("55e218446033de4e7db3f2a4"),
"time" : ISODate("2015-08-29T20:16:00.000Z")
}
]
}

Related

Mongodb- Query to check if a field in the json array exists or not

I have a JSON in MongoDB and I am trying to check if at least one of the items in the JSON doesn't contain a specific field.
{
"_id" : 12345,
"orderItems" : [
{
"itemId" : 45678,
"isAvailable" : true,
"isEligible" " false
},
{
"itemId" : 87653,
"isAvailable" : true
}
]
}
So in the above JSON, since the 2nd one under order items doesn't contain iseligible field, I need to get this _id.
I tried the below query so far, which didnt work:
db.getCollection('orders').find({"orderItems.iseligible":{$exists:false})
You can use $elemMatch to evaluate the presence of the nested key. Once that's accomplished, project out the _id value.
db.orders.find({
orderItems: {
$elemMatch: {
"isEligible": {
$exists: false
}
}
}
},
{
_id: 1
})
Here is a Mongo playground with the finished code, and a similar SO answer.

update nested object in MongoDB

I'm new in MongoDB.I have read almost all relative posts but cannot find a proper answer for my case. Suppose you have the document:
{ "_id" : "5ad1..",
"linkedin" : { "linkedinId" : "NB..J", "username" : "dolguldur",
"followers":[{name:"John",email:"john#example.com},{name:"Elsa",email:"elsa#example.com}]
}
}
If a follower changes his email, how can i update his record with the new one;
Use the $elemMatch operator to select your desired nested object :
db.collection.update({
followers: {
$elemMatch: {
name: _follower_name,
email: _follower_email
}
}
}, {
"followers.$.email": new_mail
});
The $ positional operator will select the first nested object that match the filter in $elemMatch and update it.

Query by two params with $and in mongoose?

I have a user model like this:
user : {
myArmy : {
mySoldiers : [
{
positioned : false,
soldierInfo : {
_id : s99212
}
},
{
positioned : true,
soldierInfo : {
_id : s99112
}
}
]
}
},
user : {
myArmy : {
mySoldiers : [
{
positioned : true,
soldierInfo : {
_id : s99212
}
},
{
positioned : false,
soldierInfo : {
_id : s99112
}
}
]
}
}
...
I have a query that i want to do to return the user(s) who have soldier id s99212 positioned (true): (could be thousands of those, and i need to read and retrieve them all)
This is my faulty query with mongoose:
var soldierId = s99212;
stream = User.find({
$and: [
{'myArmy.mySoldier.positioned': {$ne: null}},
{'myArmy.mySoldier.soldierInfo._id': soldierId}
]
}).lean(true).stream();
Nothing is returned by this query, should there be another way to do this $and stuff?
How exactly am i suppose to use $elemMatch if at all, should it be instead of the find? (If it worth to mention, i want to return the complete user object, not just parts of it...)
Tried this, crashed my app:
stream = User.find({
'$elemMatch': [
{'myArmy.mySoldiers.pos': {$ne: null}},
{'myArmy.mySoldiers.soldierInfo._id': soldierId}
]
}).lean(true).stream();
I know i have a small syntax problem, where is it?
Very simple and well documented. The $elemMatch operator acts as a "query in itself", where it applies "it's conditions" to members of the array specified:
var query = User.find({
"myArmy.mySoldiers": {
"$elemMatch": {
"positioned": { "$ne": null },
"soldierInfo._id": soldierId
}
}
});
Therefore for a document to "match" then the conditions specfied under $elemMatch must be present and valid for the "same" array element. Array on "left", arguments on "right".
Other "dot notation" forms only ever test that the values match "some element" in the array, and not necessarily the same one.

How can I write a Mongoose find query that uses another field as it's conditional?

Consider the following:
I have a Mongoose model called 'Person'. In the schema for the Person mode, each Person has two fields: 'children' and 'maximum_children'. Both fields are of type Number.
I would like to write a find query that returns Persons when that Persons 'children' value is less that it's 'maximum_children' value.
I have tried:
person_model.find({
children: {
$lt: maximum_children
}
}, function (error, persons) {
// DO SOMETHING ELSE
});
and
person_model.find({
children: {
$lt: 'maximum_children'
}
}, function (error, persons) {
// DO SOMETHING ELSE
});
I'm doing something wrong in trying to specify the field name that I want to compare 'children' against.
OK.
I found a solution, just after I posted this question.
The answer seems to be:
person_model.find({
$where: "children < maximum_children"}, function (error, persons)
}, {
// DO SOMETHING ELSE
});
Seems to work OK, although it seems messy.
$where must execute its JavaScript conditional against every doc so its performance can be quite poor. Instead, you can use aggregate to include a new field in a $project stage the indicates whether the doc matches or not and then filter on that:
person_model.aggregate([
{$project: {
isMatch: {$lt: ['$children', '$maximum_children']},
doc: '$$ROOT'
}},
{$match: {isMatch: true}},
{$project: {_id: 0, doc: 1}}
], function(err, results) {...});
This uses $$ROOT to include the original doc as the doc field of the projection, with a final $project used to remove the isMatch field that was added.
results looks like:
{
"doc" : {
"_id" : ObjectId("54d04591257efd80c6965ada"),
"children" : 5,
"maximum_children" : 10
}
},
{
"doc" : {
"_id" : ObjectId("54d04591257efd80c6965add"),
"children" : 5,
"maximum_children" : 6
}
}
If you want to remove the added doc level of the objects you can use Array#map on results like so:
results = results.map(function(item) { return item.doc; });
Which reshapes results to put them back into their original form:
{
"_id" : ObjectId("54d04591257efd80c6965ada"),
"children" : 5,
"maximum_children" : 10
},
{
"_id" : ObjectId("54d04591257efd80c6965add"),
"children" : 5,
"maximum_children" : 6
}

Mongodb: Trying to find all documents with specific subdocument field, why is my query not working?

Here is an example of a document from the collection I am querying
meteor:PRIMARY> db.research.findOne({_id: 'Z2zzA7dx6unkzKiSn'})
{
"_id" : "Z2zzA7dx6unkzKiSn",
"_userId" : "NtE3ANq2b2PbWSEqu",
"collaborators" : [
{
"userId" : "aTPzFad8DdFXxRrX4"
}
],
"name" : "new one",
"pending" : {
"collaborators" : [ ]
}
}
I want to find all documents within this collection with either _userId: 'aTPzFad8DdFXxRrX4' or from the collaborators array, userId: 'aTPzFad8DdFXxRrX4'
So I want to look though the collection and check if the _userId field is 'aTPzFad8DdFXxRrX4'. If not then check the collaborators array on the document and check if there is an object with userId: 'aTPzFad8DdFXxRrX4'.
Here is the query I am trying to use:
db.research.find({$or: [{_userId: 'aTPzFad8DdFXxRrX4'}, {collaborators: {$in: [{userId: 'aTPzFad8DdFXxRrX4'}]}}] })
It does not find the document and gives me a syntax error. What is my issue here? Thanks
The $in operator is basically a simplified version of $or but you really only have one argument here so you should not even need it. Use dot notation instead:
db.research.find({
'$or': [
{ '_userId': 'aTPzFad8DdFXxRrX4'},
{ 'collaborators.userId': 'aTPzFad8DdFXxRrX4'}
]
})
If you need more than one value then use $in:
db.research.find({
'$or': [
{ '_userId': 'aTPzFad8DdFXxRrX4'},
{ 'collaborators.userId': {
'$in': ['aTPzFad8DdFXxRrX4','aTPzFad8DdFXxRrX5']
}}
]
})