MongoDb Access array of objects with certain property - mongodb

I have one document as follows:
{
user: 'hvt07',
photos: [
{
link: 'http://link.to.com/image1.jpg',
isPrivate: true
},
{
link: 'http://link.to.com/image2.jpg',
isPrivate: false
}
]
}
I want to get all photos which are with:
isPrivate: false
I am using the following query:
db.collection_name.find({ photos:{ $elemMatch:{isPrivate: false} } }).pretty()
I have also tried:
db.collection_name.find({'photos.isPrivate': true}).pretty()
But both return all elements in the array even ones that are set as :
isPrivate: true
Please suggest.

Aggregation is the solution.
You need to deconstruct the photos array using the $unwind operator. Next use the $match to select documents where isPrivate: false. The $group you can regroup your documents by _id and reconstruct your photos array using the $push operator
db.collection_name.aggregate(
[
{$unwind: "$photos"},
{$match: {"photos.isPrivate": false}},
{$group: {"_id": {"id": "$_id", "user": "$user"}, photos: {$push: "$photos"}}}
{$project: {"_id": "$_id.id", "user": "$_id.user", "photos": 1, "_id": 0 }}
]
)

You can use $elemMatch to the result projection like this
db.collection_name.find(
{ photos:{ $elemMatch:{isPrivate: false} } }, //1
{photos:{$elemMatch:{isPrivate: false}}}) //2
Find all documents that have at least a photo which is not private
Select only photos that are not private for the documents found.

Related

How to filter array (of objects) inside one document in mongo db based on some condition

I have the below docs collection structure.
I'm able to filter the documnents with various approaches, but not able to filter the array inside the documents.
{
"_id": "",
"employee": {
"EmployeeAttributeValues": {
"EmployeeAttributeValue": [
{.....
},
{.....
},
{.....
},
{.....
}
]
}
}
}
Kindly help me on how to filter the MemberAttributeValue array based on some condition.
you can use $where operator for custom filtering
https://docs.mongodb.com/v4.2/reference/operator/query/where/
db.test.aggregate([
{ $match: {_id: <ID>}},
{ $unwind: '$<ARRAY>'},
{ $match: {'<ARRAY>.a': {$gt: 3}}},
{ $group: {_id: '$_id', list: {$push: '$<ARRAY>.a'}}}
])

MongoDB - Select all documents by the count of an array field

In my current project I have a structure like this:
"squad": {
"members": [
{
"name": "xyz",
"empty": true
},
{
"name": "xyz",
"empty": true
},
{
"name": "xyz",
"empty": true
}
]
}
Now I want to query every squad with mongodb which have at least, lets say 3 empty member slots. I've googled and only found aggregate and $size, which seem to only select an array count not something per field.
Any idea how to do it?
You can try this query :
db.getCollection('collectionName').aggregate([
{$unwind:"$squad.members"},
{$group:{_id:"$_id",count:{$sum:{$cond: [{$eq: ['$squad.members.empty', true]}, 1, 0]}}}},
{$match: {count: {$gte: 3}}}
])
In this query applied conditional sum and then check the count is greater than or equal 3
It will return all documents will empty slots greater than 3
db.squad.aggregate([
{$unwind:"$squad.members"},
{$match:{"squad.members.empty": true}},
{$group:{_id:"$_id",count:{$sum:1}}},
{$match: {count: {$gt: 3}}}
])

Mongodb 3.2 and 3.0 $unwind aggregation

I have created a query and check it in robomongo and it's working fine for me in mongodb 3.2
db.post.aggregate([
{$unwind: {path: "$page_groups", preserveNullAndEmptyArrays: true}},
{$group: {_id: "$page_groups",
page_names: {$addToSet: "$page_name"}}},
])
But unfortunantly I need to get same data in mongodb 3.0
Can anyone tell me how to get data with empty array in mongo 3.0 and get results by array key?
Without $unwind I get objects where pages have two or more groups and I don't need it.
Thank you for answere, I wanted to use $project at first, but I think I have found easier way using $match and array $size to ignore results where array gets more than one element:
db.post_summary.aggregate([
{$match: {$or:
[{page_groups: {$size: 1}}, {page_groups: {$size: 0}}]}},
{$group: {
_id: "$page_groups",
page_names: { "$addToSet": "$page_name" }
}},
])
In my case "page_groups" have this structure:
page_groups:[
0 =>[_id, group_name]
1 =>[_id, group_name]
]
To mimick the preserveNullAndEmptyArrays $unwind option in 3.2 for 3.0 aggregation pipeline operations, generate an initial $project pipeline stage that creates the array field if it's null or empty (using the $ifNull operator):
var pipeline = [
{
"$project": {
"pg": {
"$ifNull": [
"$page_groups",
["Unspecified"]
]
},
"page_name": 1
}
},
{ "$unwind": "$page_groups" },
{
"$group": {
"_id": "$page_groups",
"page_names": { "$addToSet": "$page_name" }
}
}
];
db.collection.aggregate(pipeline);

mongodb find matches based on count aggregation

I have a mongodb collection like this:
{"uid": "01370mask4",
"title": "hidden",
"post: "hidden",
"postTime": "01-23, 2016",
"unixPostTime": "1453538601",
"upvote": [2, 3]}
and I'd like to select post records from the users with more than 5 posts. The stucture should be the same, I just don't need the documents from users who don't have many posts.
db.collection.aggregate(
[
{ $group : { _id : "$uid", count: { $sum: 1 } } }
]
)
Now I'm stuck at how to use the count values to find. I searched but didn't find any methods to add the count values back to the same collection by uid. Saving the aggregation output and joining them together seems not supported by mongodb. Please advise, thanks!
Update:
Sorry that I didn't make it clear earlier. Thanks for your prompt answers! I want a subset of the original collection, with post text, post timestamp, etc. I don't want a subset of the aggregation output.
If there aren't millions of documents, then you can try a shortcut way to achieve what you are trying using one aggregate and another find query,
Aggregate query:
var users = db.collection.aggregate(
[
{$group:{_id:'$uid', count:{$sum:1}}},
{$match:{count:{$gt:5}}},
{$group:{_id:null,users:{$push:'$_id'}}}
]
).toArray()[0]['users']
Then it's a straight ahead query to find the particular users:
db.collection.find({uid: {$in: users}})
Just add the $match after your group with the correct query and it works :
db.collection.aggregate(
[
{ $group : { _id : "$uid", count: { $sum: 1 } } },
{ $match : { count : { $gt : 5 } }
]
)
Please try this one to select users with more than 5 posts. To keep the original fields through using $first, if the $uid is unique, please add the field as below.
db.collection.aggregate([
{$group: {
_id: '$uid',
title: {$first: '$title'},
post: {$first:'$post'},
postTime:{$first: '$postTime'},
unixPostTime:{$first: '$unixPostTime'},
upvote:{$first: '$upvote'},
count: {$sum: 1}
}},
{$match: {count: {$gte: 5}}}])
)
If there are multiple value for the same $uid, you should use $push to an array in the $group.
If you want to save the result to db, please try it as below
var cur = db.collection.aggregate(
[
{$group: {
_id: '$uid',
title: {$first: '$title'},
post: {$first:'$post'},
postTime:{$first: '$postTime'},
unixPostTime:{$first: '$unixPostTime'},
upvote:{$first: '$upvote'},
count: {$sum: 1}
}},
{$match: {count: {$gte: 5}}}
]
)
cur.forEach(function(doc) {
db.collectioin.update({_id: doc._id}, {/*the field should be updated */});
});

How to aggregate queries in mongodb

I have a document collection that look like the following:
{
name : "tester"
, activity: [
{
gear: "glasses"
where: "outside"
}
, {
gear: "hat"
, where: "inside"
}
, {
gear: "glasses"
, where: "car"
}
]
}
How do I query the collection to return only documents with multiple activities that contain the value of "gear":"glasses"?
Thanks!
I think it's possible to do without aggregation framework, if you need full document filtered by your condition:
db.collection.find({
"activity": {$elemMatch: {gear:"glasses"}},
"activity.1" : {$exists: 1}
})
This is going to be ugly with aggregation framework, but it can be done:
db.collection.aggregate(
{$match: {"activity.gear": "glasses"}},
{$unwind: "$activity"},
{$group: {
_id: {_id: "$_id", name: "$name"},
_count: {$sum: {$cond: [{$eq: ["glasses", "$activity.gear"]}, 1, 0]}}
}},
{$match: {_count: {$gt: 1}}}
)
When analyzing the above query, I would recommend walking through step. Start with just the "$match", the the "$match" and "$unwind". And so one. You will see how each step works.
The response is not the full document. If you are looking for the full document, include a $project step that passes through a dummy activity, and reconstruct the full document on the output.
You can also try this:
db.collection.find( { activity: { $elemMatch: { gear: "glasses" } } )