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

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']
}}
]
})

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.

Filter nested $elemMatch in mongoDB

I have this dataModel
{
"_id": ObjectId("5f0a9c07b001406068c073c1"),
"EmailData" : [
{
"Attachments" : {
"Files" : [
{
"Name" : "a.txt"
},
{
"Name" : "b.txt"
},
{
"Name" : "c.txt"
}
]
}
}
]
}
I want to filter those documents that their name elements insides the Files array be exactly the same as a specific array.
Consider I have this array :{"a.txt", "b.txt", "c.txt"}. I want to write a query to compare what is inside the Files element with this array. In my example condition is met but if the array is like:
{"a.txt", "b.txt"}
It does not meet. I know I have to use multiple $elemMatch but it does not work. Is there any ways to write it without aggregate?
You are describing an exact comparison, try for example:
{'EmailData.Attachments.Files': [{Name: 'a.txt'},{Name: 'b.txt'},{Name:'c.txt'}]}
Or to allow matching in any order:
{'EmailData.Attachments.Files': {$all: [{Name: 'a.txt'},{Name: 'b.txt'},{Name:'c.txt'}], $size: 3}}

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 to get all matching subdocuments in mongoose?

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")
}
]
}

Meteor upsert - using addToSet and each

I have a Groups collection that contains an eventId and an array of guest ids. I am trying to use upsert to check if an eventId and current guest id already exist, and if so update the collection with the new guest id pushed to the guests array, otherwise insert the new collection with both the current guest and new guest. Here's my upsert code:
var groupId = Groups.upsert({
$and:[
{eventId:groupAttributes.eventId}, // find all the Groups associated w/ this event
{ "guests": {$in: [guest]}}, // and include this guest
]
},{$set: group,
$addToSet: {guests: {$each: [ guest, groupAttributes.guest ]} }
});
What actually happens is a new Group is created everytime, and it's because the $each modifier isn't allowing $addToSet to add multiple values to an array as described here http://docs.mongodb.org/manual/reference/operator/update/addToSet/#up._S_addToSet
Instead what happens is the guests array is added with an "$each" array of size 2.
[{"$each":["yQZfEXfs7J9E4Nbqf","s2rk5KAxq4dYtDFNG"]}]
Two questions. 1) Is there a better way to do what I am trying to do, and if not 2) What am I doing incorrectly? Thanks.
Edit: Trying the following code in meteor mongo
db.groups.update({
$and:[
{eventId:"wGMhaP7t4nsiTNHt5"},
{ "guests": {$in: ["yQZfEXfs7J9E4Nbqf"]}},
]
},{$set: { userId: 'gBuR448nsJMcpwjsT',
author: 'Martin W',
weddingId: 'rz9xjtDm3bFAeiCxM',
eventId: 'wGMhaP7t4nsiTNHt5' },
$addToSet: {guests: {$each: [ "yQZfEXfs7J9E4Nbqf","s2rk5KAxq4dYtDFNG" ]} }
}, {upsert: true})
The result is the following
{ "_id" : ObjectId("528fa31a098141f336688d96"), "author" : "Martin W", "eventId"
: "wGMhaP7t4nsiTNHt5", "guests" : [ "yQZfEXfs7J9E4Nbqf", "s2rk5KAxq4dYtDFNG"
], "userId" : "gBuR448nsJMcpwjsT", "weddingId" : "rz9xjtDm3bFAeiCxM" }
Now I try the same code in Meteor
var groupId = Groups.update({
$and:[
{eventId:"wGMhaP7t4nsiTNHt5"},
{ "guests": {$in: ["yQZfEXfs7J9E4Nbqf"]}},
]
},{$set: { userId: 'gBuR448nsJMcpwjsT',
author: 'Martin W',
weddingId: 'rz9xjtDm3bFAeiCxM',
eventId: 'wGMhaP7t4nsiTNHt5' },
$addToSet: {guests: {$each: [ "yQZfEXfs7J9E4Nbqf","s2rk5KAxq4dYtDFNG" ]} }
}, {upsert: true})
And the result is different (and undesirable)
{ "_id" : "4XgaF6pBGEohQR6pa", "userId" : "gBuR448nsJMcpwjsT", "author" : "Marti
n W", "weddingId" : "rz9xjtDm3bFAeiCxM", "eventId" : "wGMhaP7t4nsiTNHt5", "guest
s" : [ { "$each" : [ "yQZfEXfs7J9E4Nbqf", "s2rk5KAxq4dYtDFNG" ] } ] }
I am a mongo newb so am not sure what to make of it, but I notice how the _id is an ObjectId in the first result and not in the second. And the guests array is the way I would expect using meteor mongo, but using $each as a value in the second result set. Any ideas?