Use Mongoose aggregate to fetch object inside of an array - node.js

Here is my MongoDB schema:
{
"_id": "603f23ff6c1d862e5ced9e35",
"reviews": [
{
"like": 0,
"dislike": 0,
"_id": "603f23ff6c1d862e5ced9e34",
"userID": "5fd864abb53d452e0cbb5ef0",
"comment": "Not so good",
},
{
"like": 0,
"dislike": 0,
"_id": "603f242a6c1d862e5ced9e36",
"userID": "5fd864abb53d452e0cbb5ef0",
"comment": "Not so good",
}
]
productID:"hdy6nch99dndn"
}
I want to use aggregate to get the review object of a particular id. I tried but not with any success.
Here is my code:
ProductReview.aggregate([
{ $match: { productID: productID } }
])

$match
$unwind
db.collection.aggregate([
{
$match: {
productID: 1
}
},
{
$unwind: "$reviews"
},
{
$match: {
"reviews._id": 2
}
}
])
Output:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"productID": 1,
"reviews": {
"_id": 2,
"comment": "second comment",
"dislikes": [
{
"userID": 3
},
{
"userID": 4
}
],
"likes": [
{
"userID": 1
},
{
"userID": 2
}
]
}
}
]
Mongo Playground: https://mongoplayground.net/p/qfWS1rCuMfc

Related

MongoDB aggregation group by ID then code

I have this collection:
[{
"_id": "5c378eecd11e570240a9b0ac",
"userID": "1",
"isActive": "Active",
"areaCode": "A-1",
"__v": 0
},
{
"_id": "5c378eecd11e570240a9b0bb",
"userID": "1",
"isActive": "Active",
"areaCode": "A-2",
"__v": 0
},
{
"_id": "5c378eecd11e570240a9b0c5",
"userID": "2",
"isActive": "Active",
"areaCode": "A-1",
"__v": 0
}]
Need help in grouping the results by user ID then the area code using aggregation but I'm not getting the desired output. Here's what I've tried:
AreaCodes.aggregate([
{
'$match': { '$and': [
{ 'isActive': 'Active' },
{ 'userID': { '$exists': true } }
]
}
},
{
'$group': {
'_id': {
'userID': '$userID'
},
'entries': {
'$push': {
'areaCode': '$areaCode'
}
}
}
},
{
'$group': {
'_id': '$_id.userID',
'areaCodes': {
'$push': {
'areaCode': '$entries'
}
}
}
},
{
'$project': {
'_id': 0,
'userID': '$_id',
'areaCodes': '$areaCodes'
}
}
])
Which returns the following:
[
{
"userID": "1",
"areaCodes": [
{
"areaCode": [
{
"areaCode": "A-1"
},
{
"areaCode": "A-2"
}
]
}
]
},
{
"userID": "2",
"areaCodes": [
{
"areaCode": [
{
"areaCode": "A-1"
}
]
}
]
}
]
My desired output would be to remove the excess areaCode objects and group them inside an array for each user like:
[
{
"userID": "1",
"areaCodes": ["A-1", "A-2"]
},
{
"userID": "2",
"areaCodes": ["A-1"]
}
]
How to achieve this format? Thanks.
How about:
db.collection.aggregate([
{
$match: {
$and: [{ "isActive": "Active" }, {"userID": {"$exists": true}}]
}
},
{
$group: {
_id: '$userID',
areaCodes: {$addToSet: "$areaCode"}
}
},
{
$project: {
_id: 0,
userID: "$_id",
areaCodes: 1
}
}
])
As you can see on this playgeound example.
If you just want the matching areaCodes, you can simply use $addToSet.

Mongo Query to get a result

My mongo collection name tests and whose having the following documents in it.
[
{
"title": "One",
"uid": "1",
"_metadata": {
"references": [
{
"uid": "2"
},
{
"asssetuid": 10
}
]
}
},
{
"title": "Two",
"uid": "2",
"_metadata": {
"references": [
{
"uid": "3"
},
{
"asssetuid": 11
}
]
}
},
{
"title": "Three",
"uid": "3",
"_metadata": {
"references": []
}
}
]
And I want the result in the following format (for uid:1)
[
{
"title": "One",
"uid": 1,
"_metadata": {
"references": [
{
"asssetuid": 10
},
{
"asssetuid": 11
},
{
"title": "Two",
"uid": "2",
"_metadata": {
"references": [
{
"title": "Three",
"uid": "3"
}
]
}
}
]
}
}
]
for uid:2 I want the following result
[
{
"title": "Two",
"uid": 2,
"_metadata": {
"references": [
{
"asssetuid": 11
},
{
"title": "Three",
"uid": "3"
}
]
}
}
]
Which query I used here to get a respected result. according to its uid. here I want the result in the parent-child relationship. is this possible using MongoDB graph lookup query or any other query that we can use to get the result. Please help me with this.
New Type Output
[{
"title": "One",
"uid": 1,
"_metadata": {
"assets": [{
"asssetuid": 10,
"parent": 1
}, {
"asssetuid": 11,
"parent": 2
}],
"entries": [{
"title": "Two",
"uid": "2",
"parent": 1
}, {
"title": "Three",
"uid": "3",
"parent": 2
}]
}
}]
Mongo supports the automatic reference resolution using $ref but for that, you need to change your schema a little and resolve resolution is only supported by some drivers.
You need to store your data in this format:
[
...
{
"_id": ObjectId("5a934e000102030405000000"),
"_metadata": {
"references": [
{
"$ref": "collection",
"$id": ObjectId("5a934e000102030405000001"),
"$db": "database"
},
{
"asssetuid": 10
}
]
},
"title": "One",
"uid": "1"
},
....
]
For more details on $ref refer to official documentation: label-document-references
OR
you can resolve the reference using the $graphLookup but the only problem with the $graphlookup is that you will lose the assetuid. Here is the query and it will resolve references and give output in flat map
db.collection.aggregate([
{
$match: {
uid: "1"
}
},
{
$graphLookup: {
from: "collection",
startWith: "$_metadata.references.uid",
connectFromField: "_metadata.references.uid",
connectToField: "uid",
depthField: "depth",
as: "resolved"
}
},
{
"$addFields": {
"references": "$resolved",
"metadata": [
{
"_metadata": "$_metadata"
}
]
}
},
{
"$project": {
"references._metadata": 0,
}
},
{
"$project": {
"references": "$references",
"merged": {
"$concatArrays": [
"$metadata",
"$resolved"
]
}
}
},
{
"$project": {
results: [
{
merged: "$merged"
},
{
references: "$references"
}
]
}
},
{
"$unwind": "$results"
},
{
"$facet": {
"assest": [
{
"$match": {
"results.merged": {
"$exists": true
}
}
},
{
"$unwind": "$results.merged"
},
{
"$unwind": "$results.merged._metadata.references"
},
{
"$match": {
"results.merged._metadata.references.asssetuid": {
"$exists": true
}
}
},
{
"$project": {
_id: 0,
"asssetuid": "$results.merged._metadata.references.asssetuid"
}
}
],
"uid": [
{
"$match": {
"results.references": {
"$exists": true
}
}
},
{
"$unwind": "$results.references"
},
{
$replaceRoot: {
newRoot: "$results.references"
}
}
]
}
},
{
"$project": {
"references": {
"$concatArrays": [
"$assest",
"$uid"
]
}
}
}
])
Here is the link to the playground to test it: Mongo Playground

I need to push into nested array in node mongoose

I used node mongoose.
I need to update this array push new item into Breackfast(mealList.foodList.breackfast || any),
I want to add new foodlist by time can you please give me suggestion for how to do,
{
"_id": "5fe43eb44cd6820963c98c32",
"name": "Monday Diet",
"userID": "5f225d7458b48d0fe897662e",
"day": "Monday",
"type": "private",
"mealList": [
{
"_id": "5fe43eb44cd6820963c98c33",
"time": "Breakfast",
"foodList": [
{
"_id": "5fe43eb44cd6820963c98c34",
"foodName": "Eggs",
"Qty": "2",
"calories": "calories",
"category": "category"
}
]
},
{
"_id": "5fe43eb44cd6820963c98c36",
"time": "Lunch",
"foodList": [
{
"_id": "5fe43eb44cd6820963c98c37",
"foodName": "food1",
"Qty": "100g"
},
]
}
],
"createdAt": "2020-12-24T07:09:40.141Z",
"updatedAt": "2020-12-24T07:09:40.141Z",
"__v": 0
}
I tried:
Diet.updateOne(
{ "Diet.mealList._id": req.body.mealId },
// { $push: { "Diet.0.mealList.$.foodList": req.body.foodList } },
{ $push: { foodList: req.body.foodList } }
)
Few Fixes:
convert your string _id to object type using mongoose.Types.ObjectId
remove Diet from first and push object in foodList
Diet.updateOne({
"mealList._id": mongoose.Types.ObjectId(req.body.mealId)
},
{
$push: {
"mealList.$.foodList": req.body.foodList
}
})
Playground

How to get most repeated string array in Pymongo?

How can I get most repeated value for gender and age respectively?
My data:
[{ "_id": ObjectId("5dff27c0ac2d1547d87a1fe7"), "time": "2019-12-20 21:09:53",
"object": [{"Id": 1,"gender": "female","age": "0-10"},
{"Id": 2,"gender": "female","age": "20-30"}]
},
{ "_id": ObjectId("5dff27c0ac2d1547d87a1fe8"), "time": "2019-12-20 21:09:53",
"object": [{"Id": 1,"gender": "male","age": "0-10"},
{"Id": 2,"gender": "female","age": "30-40"}]
} ,
{ "_id": ObjectId("5dff27c0ac2d1547d87a1fe9"), "time": "2019-12-20 21:09:53",
"object": [{"Id": 1,"gender": "male","age": "10-15"},
{"Id": 2,"gender": "female","age": "30-40"},
{"Id": 3,"gender": "male","age": "0-10"}]
},
{ "_id": ObjectId("5dff27c0ac2d1547d87a1fea"), "time": "2019-12-20 21:09:53",
"object": [{"Id": 2,"gender": "male","age": "40-50"},
{"Id": 3,"gender": "male","age": "0-10"},
{"Id": 4,"gender": "male","age": "0-10"}]
}]
I have written below query,
mongo.db.xyz.aggregate([
{ "$unwind" : "$object"},
{"$group" : {"_id" : "$object.Id","_gen":{"$push":"$object.gender"},"_age":{"$push":"$object.age"}}},
{ "$project": { "_id" : "$_id", "gender":"$_gen","age":"$_age"}}
])
Below is the result I am getting,
[{"_id": 3,"age": ["0-10","0-10"],"gender": ["male","male"]},
{"_id": 2,"age": ["20-30","30-40","30-40","40-50"],"gender": ["female","female","female","male"]},
{"_id": 4,"age": ["0-10"],"gender": ["male"]},
{"_id": 1,"age": ["0-10","0-10","10-15"],"gender": ["female","male","male"]}
]
But I want the output to be ,
[{"_id": 3,"age": "0-10","gender": "male"},
{"_id": 2,"age": "30-40","gender": "female"},
{"_id": 4,"age": "0-10","gender": "male"},
{"_id": 1,"age": "0-10","gender": "male"}
]
Thinking about this problem I realized it was not so simple to get the mode of some fields independently in an array of objects with just one db query. To solve that I created a query to do it generically, based on your sample data.
db.collection.aggregate([
{
$unwind: {
path: "$arr"
}
},
{
$project: {
arr: {
$objectToArray: "$arr"
}
}
},
{
$unwind: {
path: "$arr"
}
},
{
$group: {
_id: {
_id: "$_id",
k: "$arr.k",
v: "$arr.v"
},
count: {
$sum: 1
}
}
},
{
$sort: {
count: -1
}
},
{
$group: {
_id: {
_id: "$_id._id",
k: "$_id.k"
},
v: {
$first: "$_id.v"
},
count: {
$first: "$count"
}
}
},
{
$group: {
_id: {
_id: "$_id._id"
},
arr: {
$push: {
k: "$_id.k",
v: {
mode: "$v",
count: "$count"
}
}
}
}
},
{
$project: {
arr: {
$arrayToObject: "$arr"
}
}
}
])
The secret to do that is to use the $objectToArray and the $arrayToObject operations, along with the $unwind and the correct $group stages. If you need a more detailed response on any stage please ask in the comments.
The output of the sample data is:
[
{
"_id": {
"_id": ObjectId("5dff27c0ac2d1547d87a1fea")
},
"arr": {
"Id": {
"count": 1,
"mode": 3
},
"age": {
"count": 2,
"mode": "0-10"
},
"gender": {
"count": 3,
"mode": "male"
}
}
},
{
"_id": {
"_id": ObjectId("5dff27c0ac2d1547d87a1fe7")
},
"arr": {
"Id": {
"count": 1,
"mode": 2
},
"age": {
"count": 1,
"mode": "0-10"
},
"gender": {
"count": 2,
"mode": "female"
}
}
},
{
"_id": {
"_id": ObjectId("5dff27c0ac2d1547d87a1fe9")
},
"arr": {
"Id": {
"count": 1,
"mode": 2
},
"age": {
"count": 1,
"mode": "30-40"
},
"gender": {
"count": 2,
"mode": "male"
}
}
},
{
"_id": {
"_id": ObjectId("5dff27c0ac2d1547d87a1fe8")
},
"arr": {
"Id": {
"count": 1,
"mode": 2
},
"age": {
"count": 1,
"mode": "30-40"
},
"gender": {
"count": 1,
"mode": "female"
}
}
}
]
After running it for a collection that has an array of objects named "arr" (edit the query if in your collection it has a different name) it will return the mode value and the number of occurrences of that value for each field. Objects that has that field unset will be not considered, but the ones with "null" will.

Return multiple objects with $group(aggregation)

I have a collection similar to this:
[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
I want to aggregate into something like this:
{
"breakfast":[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
}
],
"lunch":[
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
}
I have only managed to group them but I can't change the key meals to either breakfast or lunch depending on the meal.name(group name)
$group: {
_id: { meal: '$meal.name' },
meals: { $push: '$meal' },
}
Using the above code I have managed to produce the output below. My only challenge is changing the key meals to either breakfast or lunch as explained above in the subgroups.
{
"meals":[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
}
],
"meals":[
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
}
Here you can have your answer .
After "grouping" to add to an array you similarly $push all that content into array by the "name" grouping key and then convert into keys of a document in a $replaceRoot with $arrayToObject:
db.collection.aggregate([
{ "$group": {
"_id": "$name",
"data": { "$push": "$$ROOT" }
}},
{ "$group": {
"_id": null,
"data": {
"$push": {
"k": "$_id",
"v": "$data"
}
}
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$data" }
}}
])
OUTPUT
[
{
"breakfast": [
{
"_id": 1,
"name": "breakfast",
"time": "10.00"
},
{
"_id": 2,
"name": "breakfast",
"time": "10.10"
}
],
"lunch": [
{
"_id": 3,
"name": "lunch",
"time": "12.07"
},
{
"_id": 4,
"name": "lunch",
"time": "12.45"
}
]
}
]
You can check the result of above query in this LINK

Resources