Related
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
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.
I want to combine separate collections to 1 json, but I have some problems in nested $lookup.
This is my example collections.
Collection Package_subscriptions:
[
{
"_id": "1",
"package_name": "Small",
"package_desc": "AAA",
"package_price": 10
},
{
"_id": "2",
"package_name": "Medium",
"package_desc": "BBB",
"package_price": 20
}
Collection Package_modules:
{
"_id" : 1,
"subscription_id" : 1,
"billing_model_id" : 1,
"module_id" : 1,
"created_at" : ISODate("2019-06-17T07:59:43.199Z"),
}
{
"_id" : 2,
"subscription_id" : 1,
"billing_model_id" : 3,
"module_id" : 2,
"created_at" : ISODate("2019-06-17T08:00:37.464Z"),
}
{
"_id" : 3,
"subscription_id" : 2,
"billing_model_id" : 2,
"module_id" : 1,
"created_at" : ISODate("2019-06-17T08:00:56.610Z"),
}
{
"_id" : 4,
"subscription_id" : 2,
"billing_model_id" : 4,
"module_id" : 2,
"created_at" : ISODate("2019-06-17T08:01:29.667Z"),
}
Collection Modules:
{
"_id" : 1,
"module_name" : "Call",
"status" : "Active",
}
{
"_id" : 2,
"module_name" : "SMS",
"status" : "Active",
}
Collection Billing_models
{
"_id" : 1,
"unit_count" : "Menit",
"counter" : 2000,
},
{
"_id" : 2,
"unit_count" : "Menit",
"counter" : 3000,
}
{
"_id" : 3,
"unit_count" : "SMS",
"counter" : 3000,
},
{
"_id" : 4,
"unit_count" : "SMS",
"counter" : 5000,
}
This is my code to try the issue, but there's not as expected.
Package_subscription
.aggregate([
{
$lookup: {
from: "o_package_modules",
localField: "_id",
foreignField: "subscription_id",
as: "package_modules"
}
},
{
$unwind: {
path: "$package_modules",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "o_modules",
localField: "package_modules.module_id",
foreignField: "_id",
as: 'module'
}
},
{
$lookup: {
from: "o_billing_models",
localField: "package_modules.billing_model_id",
foreignField: "_id",
as: 'billing_module'
}
}
])
.exec()
.then((pricing) => {
res.json(pricing)
})
.catch((err) => {
res.send(err)
})
I expect the output is:
[
{
"_id": "1",
"package_name": "Small",
"package_desc": "AAA",
"package_price": 10,
"package_modules": [
{
"_id": 1,
"subscription_id": 1,
"billing_model": {
"_id": 1,
"unit_count": "Menit",
"counter": 2000
},
"module": {
"_id": 1,
"module_name": "Call",
"status": "Active"
},
"created_at": "2019-06-17T07:59:43.199Z",
},
{
"_id": 2,
"subscription_id": 1,
"billing_model": {
"_id": 3,
"unit_count": "SMS",
"counter": 3000
},
"module": {
"_id": 2,
"module_name": "SMS",
"status": "Active"
},
"created_at": "2019-06-17T08:00:37.464Z",
}
]
},
{
"_id": 2,
"package_name": "Medium",
"package_desc": "BBB",
"package_price": 20,
"package_modules": [
{
"_id": 3,
"subscription_id": 2,
"billing_model": {
"_id": 2,
"unit_count": "Menit",
"counter": 3000
},
"module": {
"_id": "1",
"module_name": "Call",
"status": "Active"
},
"created_at": "2019-06-17T08:01:29.667Z",
},
{
"_id": 4,
"subscription_id": 2,
"billing_model": {
"_id": 4,
"unit_count": "SMS",
"counter": 5000
},
"module": {
"_id": 2,
"module_name": "SMS",
"status": "Active"
},
"created_at": "2019-06-17T08:01:50.285Z",
}
]
}
]
But the output from my code is:
[
{
"_id": "1",
"package_name": "Small",
"package_desc": "AAA",
"package_price": 10,
"package_modules": {
"_id": 1,
"subscription_id": 1,
"billing_model_id": 1
"module_id": 1
"created_at": "2019-06-17T07:59:43.199Z",
},
},
"billing_model": [
{
"_id": 1,
"unit_count": "Menit",
"counter": 2000
},
],
"module": [
{
"_id": 1,
"module_name": "Call",
"status": "Active"
}
]
},
{
"_id": "1",
"package_name": "Small",
"package_desc": "AAA",
"package_price": 10,
"package_modules": {
"_id": 2,
"subscription_id": 2,
"billing_model_id": 3
"module_id": 2
"created_at": "2019-06-17T07:59:43.199Z",
},
},
"billing_model": [
{
"_id": 3,
"unit_count": "SMS",
"counter": 3000
},
],
"module": [
{
"_id": 2,
"module_name": "SMS",
"status": "Active"
}
]
}
..........
// And medium where is loop again
]
I think there are multiple issue in your code.
In your $lookup stage, you need to setup the as field as the package_modules.billing_module and package_modules.module instead of billing_module and module, and so on, so that it becomes a field of package_module object instead of a separate object itself.
you need to unwind after each $lookup stage, as $lookup returns an array instead of an object.
You need to $group at the end of the aggregation pipeline to push all the package_modules of one subscription into one array.
After resolving all the above issues,
Your aggregation pipe should look like this:
Package_subscription
.aggregate([
{
$lookup: {
from: "o_package_modules",
localField: "_id",
foreignField: "subscription_id",
as: "package_modules"
}
},
{
$unwind: {
path: "$package_modules",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "o_modules",
localField: "package_modules.module_id",
foreignField: "_id",
as: 'package_modules.module'
}
},
{
$unwind: {
path: "$package_modules.module",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "o_billing_models",
localField: "package_modules.billing_model_id",
foreignField: "_id",
as: 'package_modules.billing_module'
}
},
{
$unwind: {
path: "$package_modules.billing_module",
preserveNullAndEmptyArrays: true
}
},
{
$group: {
_id: "$_id",
package_modules : {$push : "$package_modules"},
package_name: {$first . : "$package_name"},
package_desc: {$first . : "$package_desc"},
package_price: {$first . : "$package_price"}
}
}
])
.exec()
.then((pricing) => {
res.json(pricing)
})
.catch((err) => {
res.send(err)
})
The $push in the last $group stage will combine all package_modules into one array. And i think that is what you want at the end.
I hope this works for you.
thank you its working for me
var order_details_data = await OrderDetails.aggregate([{
'$match': {
`order_id`: ObjectId(val._id)
}
}, {
$sort: {
date: -1
}
}, {
$addFields: {
o_price: {
$toDouble: `$o_price`
}
}
}, {
$lookup: {
from: `products`,
localField: `product_id`,
foreignField: `_id`,
as: `product_id`,
},
}, {
$unwind: {
path: `$product_id`,
preserveNullAndEmptyArrays: true
}
}, {
$lookup: {
from: `productvarientvalues`,
localField: `product_id.material_id`,
foreignField: `_id`,
as: `product_id.material_id`,
},
}, {
$unwind: {
path: `$product_id.material_id`,
preserveNullAndEmptyArrays: true
}
}, {
$group: {
_id: `$order_id`,
date: {
$first: `$date`
},
totalAmount: {
$sum: `$totalAmount`
},
orderDetails: {
$push: `$$ROOT`
},
delivery_address: {
$first: delivery_address
},
ship_address_id: {
$first: ship_address_id
},
// material_id: { $push: `$product_id.material_id` }
product_id: {
$push: `$product_id`
},
}
}, {
$project: {
// test: `$test`,
totalAmount: {
$multiply: [`$qty`, `$o_price`]
},
o_price: `$o_price`,
product_id: `$product_id`,
order_id: `$order_id`,
user_id: `$user_id`,
product_id: {
$arrayElemAt: [`$product_id`, 0]
},
data: {
$arrayElemAt: [`$data`, 0]
},
qty: `$qty`,
date: {
$dateToString: {
format: `%M `,
date: `$date`
}
},
discount: `$discount`,
examTotal: {
$sum: [`$qty`, `$price`]
},
createdAt: {
$dateToString: {
format: `%Y-%m-%d %H:%M`,
date: `$createdAt`
}
},
}
}
I was stuck on mongodb aggregation to get count of rating. i have already trying to make myself pipeline. looks above
data (products collection)
{
"status": 200,
"message": null,
"data": {
"_id": "5cc570257631a313d489ba4a",
"media": [
"httpsdssd",
"dfdfd"
],
"comment": [
"5cc57f1053273c05cc60e707",
"5cc585bf6ff7a812e0e7d9d9",
"5cc5c654bc73b408787ffadc",
"5cc5c6e3bc73b408787ffadd"
],
"store": "5cc2c9710bc5d615781fcf8a",
"meta": {
"title": "Traveling Sumbar",
"price": "150000",
"max": 5,
"duration": 6,
"description": "fdf fdnf jdnf dfnkdknfkkd",
"location": {
"province": "Sumbar",
"city": "Padang"
}
},
"option": {
"is_promo": false,
"auto_delete": null
},
"created_at": "2019-04-28T09:19:33.233Z",
"updated_at": "2019-04-28T15:29:39.921Z",
"__v": 0
}
}
comment data on (products_comment)
{
"helped": [],
"deleted_at": null,
"_id": "5cc3276e32940613506c3848",
"user": "5cc2c7fb0bc5d615781fcf86",
"rating": "4",
"body": "fdfdlfdlfkdlfkdlfkd",
"created_at": "2019-04-26T15:44:46.224Z",
"updated_at": "2019-04-28T16:00:48.400Z",
"__v": 0
},
{
"helped": [],
"deleted_at": null,
"_id": "5cc3276e32940613506c3848",
"user": "5cc2c7fb0bc5d615781fcf86",
"rating": "4",
"body": "fdfdlfdlfkdlfkdlfkd",
"created_at": "2019-04-26T15:44:46.224Z",
"updated_at": "2019-04-28T16:00:48.400Z",
"__v": 0
},
{
"helped": [],
"deleted_at": null,
"_id": "5cc3276e32940613506c3848",
"user": "5cc2c7fb0bc5d615781fcf86",
"rating": "3",
"body": "fdfdlfdlfkdlfkdlfkd",
"created_at": "2019-04-26T15:44:46.224Z",
"updated_at": "2019-04-28T16:00:48.400Z",
"__v": 0
},
I have already try make aggregation pipeline like this
{
$lookup: {
from: "stores",
localField: "store",
foreignField: "_id",
as: "store"
}
},
{
$lookup: {
from: "products_comment",
localField: "comment",
foreignField: "_id",
as: "comment"
}
},
{ $unwind: "$comment" },
{
$project: {
media: 1,
"store.type": 1,
"store.profile.address.city": 1,
"meta.title": 1,
"meta.price": 1,
"comment.rating": 1
}
}
but result unlike expectation, i want result like this
{
"_id": "5cc570257631a313d489ba4a",
"media": [
"httpsdssd",
"dfdfd"
],
"comment": {
1_rating: 0, <value of rating: count of value>
2_rating: 3,
3_rating: 5,
....,
},
"store": [
{
"type": "craft",
"profile": {
"address": {
city: "Padang
}
}
}
],
"meta": {
"title": "Traveling Sumbar",
"price": "150000"
}
}
how i do to solve my problem ?
Below Query will give you exactly expected Output :
var query = [
{
$lookup: {
from: "comments",
localField: "comment",
foreignField: "_id",
as: "comments"
}
},
{ $unwind: "$comments" },
{ $group : {
_id: {
_id: '$_id',
rating: '$comments.rating',
media : '$media',
meta : '$meta',
store : '$store'
},
totalRating: {$sum: 1}
}
},
{
$group : {
_id : {
_id : '$_id._id',
media : '$_id.media',
meta : '$_id.meta',
store : '$_id.store'
},
comments : {
$push : {
rating : '$_id.rating',
totalRating : '$totalRating'
}
}
}
},
{
$lookup: {
from: "stores",
localField: "store",
foreignField: "_id",
as: "store"
}
},
{
$project: {
'_id' : '$_id._id',
media : '$_id.media',
store : '$store',
meta : {
title: '$_id.meta.title',
price : '$_id.meta.price'
},
comments : { "$arrayToObject": {
"$map": {
"input": "$comments",
"as": "el",
"in": {
"k": "$$el.rating",
"v": "$$el.totalRating"
}
}
}
}
}
}
];
Output :
{
"_id" : ObjectId("5cc718715290f4ed550f5305"),
"media" : [
"httpsdssd",
"dfdfd"
],
"store" : [ ],
"meta" : {
"title" : "Traveling Sumbar",
"price" : "150000"
},
"comments" : {
"3" : 1,
"4" : 2
}
}
{
"_id" : ObjectId("5cc88d99d486568c5745e4b7"),
"media" : [
"maha",
"sagar"
],
"store" : [ ],
"meta" : {
"title" : "Sagar Sumbar",
"price" : "15000"
},
"comments" : {
"3" : 2,
"5" : 1,
"1" : 1
}
}
NOTE: Store data will be fetched from stores collection by $lookup. I don't have a model/data so, not in the output.
I have invoice Model as following
{
...
"itemDetails": [
{
"item": "593a1a01bbb00000043d9a4c",
"purchasingPrice": 100,
"sellingPrice": 150,
"qty": 200,
"_id": "59c39c2a5149560004173a05",
"discount": 0
}
],
"payments": [],
"status": "PENDING",
"created": {
"$date": "2017-09-21T11:02:02.675Z"
},
...
}
Sample Invoice Document is as follows.
{
"_id": {
"$oid": "59c39c2a5149560004173a04"
},
"customer": {
"$oid": "5935013832f9fc0004fa9a16"
},
"order": {
"$oid": "59c1df8393cbba0004a0e956"
},
"employee": {
"$oid": "592d0a6238880f0004637e84"
},
"status": "PENDING",
"deliveryStatus": "PROCESSING",
"created": {
"$date": "2017-09-21T11:02:02.675Z"
},
"discount": 0,
"payments": [],
"itemDetails": [
{
"item": {
"$oid": "593a1a01bbb00000043d9a4c"
},
"purchasingPrice": 100,
"sellingPrice": 150,
"qty": 200,
"_id": {
"$oid": "59c39c2a5149560004173a05"
},
"discount": 0
}
],
"__v": 0
}
Item details Item is an object Id which refers to Item collection.
I'm writing a mongoose Aggregate query to get the sale by Item. for that I need to filter the invoice from a given date range and status does not equal to "CANCELED". for that, I have written following code
module.exports.saleByItem = (req, res) => {
let fromDate;
let toDate;
if ((req.query.fromDate && moment(req.query.fromDate, config.dateFormat, true).isValid()) && (req.query.toDate && moment(req.query.toDate, config.dateFormat, true).isValid())) {
fromDate = moment(req.query.fromDate, config.dateFormat).startOf('day');
toDate = moment(req.query.toDate, config.dateFormat).endOf('day');
}
Invoice.aggregate([
{
"$match": {
"created": {
"$gte": fromDate
? fromDate.toDate()
: undefined,
"$lte": toDate
? toDate.toDate()
: undefined
},
"status": {
"$ne": "CANCELED"
}
}
}, {
"$unwind": "$itemDetails"
}, {
"$group": {
"_id": "$itemDetails.item",
"qty": {
"$sum": "$itemDetails.qty"
},
"value": {
"$sum": {
"$multiply": [
"$itemDetails.qty", {
"$subtract": ["$itemDetails.sellingPrice", "$itemDetails.discount"]
}
]
}
},
"avarageSellingPrice": {
"$avg": {
"$subtract": ["$itemDetails.sellingPrice", "$itemDetails.discount"]
}
}
}
}, {
"$sort": {
"value": -1
}
}, {
"$lookup": {
from: "items",
localField: "_id",
foreignField: "_id",
as: "item"
}
}, {
"$unwind": "$item"
}, {
"$project": {
_id: 1,
itemName: "$item.itemName",
qty: 1,
value: 1,
avarageSellingPrice: 1
}
}
]).then(salesFigures => {
res.status(200).json(salesFigures);
}).catch((err) => {
res.status(422).json(err);
});
};
The issue is when I put today date to both dates it returns sale of today. Gives []
How to handle date ranges in $match with local time-zone?