$match operator for sub document field in MongoDb - mongodb

I am trying new pipeline query of MongoDB so i try to execute below query.
{
aggregate: 'Posts',
pipeline: [
{ $unwind: '$Comments'},
{ $match: {'$Comments.Owner': 'Harry' }},
{$group: {
'_id': '$Comments._id'
}
}
]
}
And nothing match to query so empty result returns. I guess problem can be on $match command . I am using dotted notation match comment Owner but not sure it is exactly true or not. Why this query does not return Ownders who is 'Harry' . I am sure it is exist in db.

You don't use the $ prefix for the $match field names.
Try this:
{
aggregate: 'Posts',
pipeline: [
{ $unwind: '$Comments'},
{ $match: {'Comments.Owner': 'Harry' }},
{ $group: {
'_id': '$Comments._id'
}}
]
}

I encounter the same problem with aggregation framework with MongoDB 2.2.
$match didn't work for me for subdocument (but I am just learning MongoDB, so I could do something wrong).
I added extra projection to remove subdocument (Comments in this case):
{
aggregate: 'Posts',
pipeline: [
{ $unwind: '$Comments'},
{ $project: {
comment_id: "$Comments._id",
comment_owner: "$Comments.Owner"
}},
{ $match: {'$comment_Owner': 'Harry' }},
{$group: {
'_id': '$comment_id'
}
}
]
}

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

Indexing an aggregation slow query on mongo db

I got a slow query on mongo about around 50k documents in a collection
How can I index it?
I tried to add the following index but it does not solve the issue
db.getCollection("events").createIndex({ "area.area_id": 1, "execute_time": -1 })
"Slow query","attr":{"type":"command","ns":"events.events",
"command":{"aggregate":"events","pipeline":
[
{"$facet":{"1":[{"$match":{"area.area_id":"1"}},
{"$sort":{"execute_time":-1}},{"$limit":30}
],
"2":
[
{"$match":{"area.area_id":"2"}},
{"$sort":{"execute_time":-1}},{"$limit":30}]}}
]
,"cursor":{},
"lsid":
{"id":{"$uuid":"2be3c461-dfc7-4591-adaf-da9454b9615c"}},"$db":"events"},
"planSummary":"COLLSCAN","keysExamined":0,"docsExamined":37973,"cursorExhausted":true,"numYields":37,"nreturned":1,
"reslen":118011,"locks":{"ReplicationStateTransition":{"acquireCount":{"w":61}},"Global":{"acquireCount":{"r":61}},
"Database":{"acquireCount":{"r":61}},"Collection":{"acquireCount":{"r":61}},"Mutex":{"acquireCount":{"r":24}}},"storage":{},"protocol":"op_msg","durationMillis":262}}
my query:
this.collection.aggregate([
{$facet: facetObj }])
each facet obj is something like:
facet[x] = [
{$match: {'area.area_id': x}},
{$sort: { execution_time: -1 }},
{$limit: limit}
]
You cannot use indexes in the $facet stage.
From the MongoDB documentation:
The $facet stage, and its sub-pipelines, cannot make use of indexes, even if its sub-pipelines use $match or if $facet is the first stage in the pipeline. The $facet stage will always perform a COLLSCAN during execution.
You did not show any input data nor the expected result. However from what I see, one approach could be this one:
db.collection.aggregate([
{ $match: { area_id: { $in: [ 1, 2 ] } } },
{ $sort: { execute_time: -1 } },
{
$group: {
_id: "$area_id",
execute_time: { $push: "$execute_time" }
}
},
{
$set: {
execute_time: { $slice: [ "$execute_time", 30 ] }
}
}
])
Mongo playground

MongoDB - $setIsSubset operator not working with $match stage

I constructed a query like this:
db.test.aggregate([{$match: {$setIsSubset: [['hello', 'you'], '$words']}}])
I want to return all documents where the array of the field 'words' contains the strings ['hello', 'you'].
Executing this query i get this error:
"errmsg" : "unknown top level operator: $setIsSubset"
What am i doing wrong?
Thanks for your help!
You should use $expr if you want to use $setIsSubset (which is an expresion) inside $match:
db.test.aggregate([
{
$match: {
$expr: {
$setIsSubset: [["hello", "you"], "$words"]}
}
}
])
For MongoDB versions lower than 3.6 you can use $redact:
db.test.aggregate([
{
$redact: {
$cond: {
if: { $eq: [ { $setIsSubset: [["hello", "you"], "$words"]}, true ] },
then: "$$KEEP",
else: "$$PRUNE"
}
}
}
])
This works:
db.test.aggregate([{$match: {'words': {'$all': ["hello", "you"] }}}])

Encapsulate/Transform an Array into an Array of Objects

I am currently in the process of modifying a schema and I need to do a relatively trivial transform using the aggregation framework and a bulkWrite.
I want to be able to take this array:
{
...,
"images" : [
"http://example.com/...",
"http://example.com/...",
"http://example.com/..."
]
}
and aggregate to a similar array where the original value is encapsulated:
{
...,
"images" : [
{url: "http://example.com/..."},
{url: "http://example.com/..."},
{url: "http://example.com/..."}
]
}
This slow query works, but it is ridiculously expensive to unwind an entire collection.
[
{
$match: {}
},
{
$unwind: {
path : "$images",
}
},
{
$group: {
_id: "$_id",
images_2: {$addToSet: {url: "$images"}}
}
},
]
How can this be achieved with project or some other cheaper aggregation?
$map expression should do the job, try this:
db.col.aggregate([
{
$project: {
images: {
$map: {
input: '$images',
as: 'url',
in: {
url: '$$url'
}
}
}
}
}
]);
You don't need to use the bulkWrite() method for this.
You can use the $map aggregation array operator to apply an expression to each element element in your array.
Here, the expression simply create a new object where the value is the item in the array.
let mapExpr = {
"$map": {
"input": "$images",
"as": "imageUrl",
"in": { "url": "$$imageUrl }
}
};
Finally you can use the $out aggregation pipeline operator to overwrite your collection or write the result into a different collection.
Of course $map is not an aggregation pipeline operator so which means that the $map expression must be use in a pipeline stage.
The way you do this depends on your MongoDB version.
The best way is in MongoDB 3.4 using $addFields to change the value of the "images" field in your document.
db.collection.aggregate([
{ "$addFields": { "images": mapExpr }},
{ "$out": "collection }
])
From MongoDB 3.2 backwards, you need to use the $project pipeline stage but you also need to include all the other fields manually in your document
db.collection.aggregate([
{ "$project": { "images": mapExpr } },
{ "$out": "collection }
])

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