How to process the fetched data from mongoDB? - node.js

I want my order_total to be calculated from results after joining two different collections
I'm building an Food Ordering App. I've seperate model for item:"ItemModel"
Here's my orderSchema which is being followed while ordering the items. I've referred to the ItemModel in case of Item_Id
My OrderSchema:
const cartOrderSchema = new mongoose.Schema({
items: [
{
item_id: { type: mongoose.Schema.Types.ObjectId, ref: "itemModel" },
quantity: { type: Number, default: 1 },
},
],
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "userModel",
},
date: {
type: Date,
default: new Date(),
},
order_total: {
type: Number,
default: 0,
},
delivery_address: {
type: String,
default: "GGn",
},
payment_type: {
type:String,
default:"Cash"
},
});
I'm using this query:
await req.user.populate({
path: "mycartOrder",
populate:{
path:"items.item_id"
}
});
res.status(200).send(req.user.mycartOrder);
& getting result as:
{
"order_total": 0,
"delivery_address": "GGn",
"payment_type": "Cash",
"_id": "6270d84d89e3be5f291ee923",
"items": [
{
"item_id": {
"item_type": "Veg",
"_id": "62614934693ffd8d8f7979e9",
"name": "Cheese Burger",
"cuisine_category": "Starters",
"price": 67,
"description": "Aloo patty enclosed within Bun Tikki with melted cheese",
"store_id": "626148ca693ffd8d8f7979e2",
"__v": 0
},
"quantity": 10,
"_id": "6270d84d89e3be5f291ee924"
},
{
"item_id": {
"item_type": "Veg",
"_id": "62614977693ffd8d8f7979ec",
"name": "Paneer Lababdar",
"cuisine_category": "Indian",
"price": 249,
"description": "Panner vegetable cooked in very rich Gravy",
"store_id": "626148ca693ffd8d8f7979e2",
"__v": 0
},
"quantity": 3,
"_id": "6270d84d89e3be5f291ee925"
}
],
"date": "2022-05-03T07:22:03.907Z",
},
What I want?
I want that this order_total should be calculated automatically when the query is executed by multiplying the item's price with quantity.
Please HELP!!

If your collection is called cart (I'm not familiar with node.js or mongoose), you can use the $unwind operator and unwind your items. Then use the $addFields operator to add a new field overwriting your order_total, with the value to be the multiplication of the price and quantity. Then you'll have to group them back together, using the $sum operator on all the order_total fields, and then re-add all the previous fields..
db.cart.aggregate([
{
$unwind: "$items"
},
{
$addFields: {
"order_total": {
$multiply: [ "$items.quantity", "$items.item_id.price" ]
}
}
},
{
$group: {
_id: "$_id",
order_total: {$sum: "$order_total"},
delivery_address: { $first: "$delivery_address" },
payment_type: { $first: "$payment_type" },
items: { $push: "$items" },
date: {$first: "$date" }
}
}
])
You can skip the $addFields step and do the sum and multiply all in one step but I wanted to show both for clarity:
db.cart.aggregate([
{
$unwind: "$items"
},
{
$group: {
_id: "$_id",
order_total: {$sum: {$multiply: [ "$items.quantity", "$items.item_id.price" ]}},
delivery_address: { $first: "$delivery_address" },
payment_type: { $first: "$payment_type" },
items: { $push: "$items" },
date: {$first: "$date" }
}
}
])
You might need a match stage if you only want one specific cart.

Related

Pagination in aggregation and also used $project in mongoose

i want to show user list of object with projection and also want to show the total page i want output like this
{
"Success": true,
"message": " Fetched post comment successfully.",
"data": {
"docs": [
{
"_id": "60101fcc4077e698facd63aa",
"commentDetail": {
"hasReply": false,
"likeCount": 0,
"angryCount": 0,
"favCount": 0,
"totalReaction": 0,
"description": "four comment",
"media": null,
"date": "2021-01-26T13:57:32.220Z",
"action": [],
"commentReplies": []
},
"userID": "5f5238b5458b7c63a477bf87",
"postID": "5fb7a19bcae255415e99781b",
"commentID": "60101fcb4077e698facd63a9",
},
{
"_id": "60101fcc4077e698facd63aa",
"commentDetail": {
"hasReply": false,
"likeCount": 0,
"angryCount": 0,
"favCount": 0,
"totalReaction": 0,
"description": "four comment",
"media": null,
"date": "2021-01-26T13:57:32.220Z",
"action": [],
"commentReplies": []
},
"userID": "5f5238b5458b7c63a477bf87",
"postID": "5fb7a19bcae255415e99781b",
"commentID": "60101fcb4077e698facd63a9",
}
],
"count": 1
}
}
i write this query
getPostAllComment = await this.comment.aggregate([
{ $match: { postID: ObjectId(getPostCommentDTO.postID) } },
{ $sort: { createdAt: 1 } }, //sort on created At
{ $skip: (parseInt(getPostCommentDTO.pageNum) - 1) * parseInt(getPostCommentDTO.pageSize) },
{ $limit: parseInt(getPostCommentDTO.pageSize) },
{
$group: {
_id: "$postID",
docs: { $push: "$$ROOT" },
count: { $sum: 1 },
},
},
{ $project: { _id: 0 }
}
it give me output like above which i am expecting but my $project is not working here.so how can i show output exactly like above with total page count field at root level not in each document with projection.i already tried to used $facet but it not give me output like i above explain
I get my desire result by this query
getPostAllComment = await this.comment.aggregate([
{ $match: { postID: ObjectId(getPostCommentDTO.postID) } },
{
$group: {
_id: null,
collection: { $push: "$$ROOT" },
count: { $sum: 1 },
},
},
{ $unwind: "$collection" },
{ $sort: { createdAt: 1 } }, //sort on created At
{ $skip: (parseInt(getPostCommentDTO.pageNum) - 1) * parseInt(getPostCommentDTO.pageSize) },
{ $limit: parseInt(getPostCommentDTO.pageSize) },
{
$project: {
hasReply: "$collection.commentDetail.hasReply",
likeCount: "$collection.commentDetail.likeCount",
angryCount: "$collection.commentDetail.angryCount",
favCount: "$collection.commentDetail.favCount",
totalReaction: "$collection.commentDetail.totalReaction",
date: "$collection.commentDetail.date",
media: "$collection.commentDetail.media",
description: "$collection.commentDetail.description",
action: "$collection.commentDetail.action",
recentCommentReplies: "$collection.commentDetail.commentReplies",
userProfilePicture: "$collection.userProfilePicture",
userProfilePictureThumbnail: "$collection.userProfilePictureThumbnail",
userName: "$collection.userName",
userID: "$collection.userID",
postID: "$collection.postID",
commentID: "$collection.commentID",
createdAt: "$collection.createdAt",
updatedAt: "$collection.updatedAt",
count: "$count",
},
},
{
$group: {
_id: "$postID",
docs: { $push: "$$ROOT" },
total: { $first: "$count" },
},
},
{ $project: { "docs.count": 0, _id: 0 } },
])
return getPostAllComment[0]

How to get conversation list between two user in MongooseJs

i'm new to MongoDB and trying to build a simple chat app using Node.Js and MongoDB using mongoose Js. and i'm stuck here for last 2 days so need help!!
Tested most of the related answer of stack overflow but not getting desirable result.
What i want to achieve is something similar we see in chat application like Facebook Messenger and whatsApp web where in one side we see all our conversation list which show last message and person profile.
an example here http://roba.laborasyon.com/demos/dark/ (left sidebar)
this is how my model look like
const chat = new Schema({
to:{
type:Schema.Types.ObjectId,
ref:"User",
required:true,
},
from:{
type:Schema.Types.ObjectId,
ref:"User",
required:true,
},
seenbySender:Boolean,
seenByReceiver:Boolean,
isFile:{
type:Boolean,
default:false
},
file:{
type:Object,
required:false,
},
text:String,
},{timestamps:true});
In My Controller or route file (this is not my code but i tried similar logic they all return empty array or error)
exports.test = (request,response,next) => {
let {user} = request.body;
const u=await User.findById(user);
//Chat.find({from:user}).populate('from').populate('to').then(r => response.json(r));
//user ="5f46319ac483a43d98ae3626";
Chat.aggregate([
{$match:{$or:[{"to":user},{"from":user}]}},
{
$group:{"_id":
{
"text":{
$cond:[
{
$gt:[
{$substr:["$to",0,1]},
{$substr:["$from",0,1]}]
},
{$concat:["$to"," and ","$from"]},
{$concat:["$from"," and ","$to"]}
]
}
},
"text":{$first:"$text"}
}
}]).then(result => {
response.json(result)
}).catch(error => {
console.log("[[error]]",error)
response.json({error})
});
}
Here is data i'm working with (exported JSON file)
[
{
"_id": {
"$oid": "5f4a3ae0a7ff491f3024668e"
},
"isFile": false,
"to": {
"$oid": "5f46325ec483a43d98ae3627"
},
"from": {
"$oid": "5f46319ac483a43d98ae3626"
},
"text": "Hi John,Yash here!",
"createdAt": {
"$date": "2020-08-29T11:24:16.416Z"
},
"updatedAt": {
"$date": "2020-08-29T11:24:16.416Z"
},
"__v": 0
},
{
"_id": {
"$oid": "5f4a3affa7ff491f3024668f"
},
"isFile": false,
"to": {
"$oid": "5f46319ac483a43d98ae3626"
},
"from": {
"$oid": "5f46325ec483a43d98ae3627"
},
"text": "hello Yash - John",
"createdAt": {
"$date": "2020-08-29T11:24:47.519Z"
},
"updatedAt": {
"$date": "2020-08-29T11:24:47.519Z"
},
"__v": 0
},
{
"_id": {
"$oid": "5f4a3b25a7ff491f30246690"
},
"isFile": false,
"to": {
"$oid": "5f4632c8c483a43d98ae3628"
},
"from": {
"$oid": "5f46319ac483a43d98ae3626"
},
"text": "Hello Don, Yash this side.",
"createdAt": {
"$date": "2020-08-29T11:25:25.067Z"
},
"updatedAt": {
"$date": "2020-08-29T11:25:25.067Z"
},
"__v": 0
}
]
So what i need is last message of user he chatted with, with the user reference. in this case for Id: 5f46319ac483a43d98ae3626 the last 2 objects should be render
Thanks a lot!!
You can try using $split to get limited records,
db.collection.aggregate([
{
$match: {
$or: [
{ "to": ObjectId("5f46319ac483a43d98ae3626") },
{ "from": ObjectId("5f46319ac483a43d98ae3626") }
]
}
},
// for descending order
{ $sort: { updatedAt: -1 } },
{
$group: {
_id: {
$cond: [
{ $eq: ["$to", ObjectId("5f46319ac483a43d98ae3626")] },
{ $concat: [{ $toString: "$to" }, " and ", { $toString: "$from" }] },
{ $concat: [{ $toString: "$from" }, " and ", { $toString: "$to" }] }
]
},
updatedAt: { $first: "$updatedAt" },
// push messages
messages: { $push: "$$ROOT" }
}
},
// order by descending order
{ $sort: { updatedAt: -1 } },
// limit to 2 messages only
{ $addFields: { messages: { $slice: ["$messages", 2] } } }
])
Playground
for joining user data you can use $lookup
I guess you can use this schema for a chat
MessageSchema = new mongoose.Schema({
from: {
type: string,
required: true,
},
to: {
type: string,
required: true,
},
time: {
type: Date,
default: Date.now(),
},
message: {
type: string,
required: true,
},
});
ChatSchema = new mongoose.Schema({
firstUserId:{
type: mongoose.Schema.Types.ObjectId,
required: true,
},
secondUserId:{
type: mongoose.Schema.Types.ObjectId,
required: true,
},
Chat: MessageSchema,
})
and data will be as this way
[
{
firstUserId: "hjakdsf323275lks",
secondUserId: "asdfe2342232aas",
Chat: [
{
from: "hjakdsf323275lks",
to: "asdfe2342232aas",
time: "18/7/2020 20:06:09",
message: "Hi ",
},
{
from: "asdfe2342232aas",
to: "hjakdsf323275lks",
time: "18/7/2020 21:07:09",
message: "hello ",
},....
],
},
];

I need mongodb group pipeline for multiple condition

Document
[
{
type: 1,//credit
amount: 60
},
{
type: 2,//debit
amount: 35
},
{
type: 3,//credit
amount: 25
},
{
type: 4,//debit
amount: 80
},
{
type: 5,//credit
amount: 70
},
]
Result
[
{
_id: {
Name: "Credition",
Type: [1, 3, 5]
},
Total_Amount: 155
},
{
_id: {
Name: "Debition",
Type: [2, 4]
},
Total_Amount: 115
},
]
In my schema, there are millions of logs records in which few are credited logs, few are debited logs.
I want to use MongoDB aggregate pipe and have to group like above for million records at a time
Yes you can do that first you need to add a new field transaction on the basis of the type of logs, then you can group the logs on the basis of that field.
Working example - https://mongoplayground.net/p/e4kqeKLIuIr
db.collection.aggregate([
{
$addFields: {
transaction: {
$cond: {
if: {
$in: [
"$type",
[
1,
3,
5
]
]
},
then: "Credition",
else: "Debition"
}
}
}
},
{
$group: {
_id: "$transaction",
Type: {
$addToSet: "$type"
},
Total_Amount: {
$sum: "$amount"
}
}
}
])
After this, you can also use $project operator to change the name or structure of the record, if needed
You can use the operator $cond during the grouping stage:
db.collection.aggregate([
{
$group: {
_id: {
$cond: [
{
$in: [ "$type", [1,3,5] ]
},
"Credition",
"Debition"
]
},
type: {
$addToSet: "$type"
},
amount: {
$sum: "$amount"
}
}
},
{
$project: {
_id: {
Name: "$_id",
Type: "$type"
},
Total_Amount: "$amount"
}
}
])
MongoPlayground

MongoDB aggregate lookup match not working the same without lookup

Can you please help? I'm trying to aggregate data over the past 12 months by both ALL publication data specific to a certain publisher and per publication to return a yearly graph data analysis based on the subscription type.
Here's a snapshot of the Subscriber model:
const SubscriberSchema = new Schema({
publication: { type: Schema.Types.ObjectId, ref: "publicationcollection" },
subType: { type: String }, // Print, Digital, Bundle
subStartDate: { type: Date },
subEndDate: { type: Date },
});
Here's some data for the reader (subscriber) collection:
{
_id: ObjectId("5dc14d3fc86c165ed48b6872"),
publication: ObjectId("5d89db9d82273f1d18970deb"),
subStartDate: "2019-11-20T00:00:00.000Z",
subtype: "print"
},
{
_id: ObjectId("5dc14d3fc86c165ed48b6871"),
publication: ObjectId("5d89db9d82273f1d18970deb"),
subStartDate: "2019-11-19T00:00:00.000Z",
subtype: "print"
},
{
_id: ObjectId("5dc14d3fc86c165ed48b6870"),
publication: ObjectId("5d89db9d82273f1d18970deb"),
subStartDate: "2019-11-18T00:00:00.000Z",
subtype: "digital"
},
{
_id: ObjectId("5dc14d3fc86c165ed48b6869"),
publication: ObjectId("5d8b36c3148c1e5aec64662c"),
subStartDate: "2019-11-19T00:00:00.000Z",
subtype: "print"
}
The publication model has plenty of fields but the _id and user fields are the only point of reference in the following queries.
Here's some data for the publication collection:
// Should use
{ "_id": {
"$oid": "5d8b36c3148c1e5aec64662c"
},
"user": {
"$oid": "5d24bbd89f09024590db9dcd"
},
"isDeleted": false
},
// Should use
{ "_id": {
"$oid": "5d89db9d82273f1d18970deb"
},
"user": {
"$oid": "5d24bbd89f09024590db9dcd"
},
"isDeleted": false
},
// Shouldn't use as deleted === true
{ "_id": {
"$oid": "5d89db9d82273f1d18970dec"
},
"user": {
"$oid": "5d24bbd89f09024590db9dcd"
},
"isDeleted": true
},
// Shouldn't use as different user ID
{ "_id": {
"$oid": "5d89db9d82273f1d18970dfc"
},
"user": {
"$oid": "5d24bbd89f09024590db9efd"
},
"isDeleted": true
}
When I do a lookup on a publication ID with the following, I'm getting perfect results:
Subscriber.aggregate([
{
$match: {
$and: [
{ 'publication': mongoose.Types.ObjectId(req.params.id) },
],
"$expr": { "$eq": [{ "$year": "$subStartDate" }, new Date().getFullYear()] }
}
},
{
/* group by year and month of the subscription event */
$group: {
_id: { year: { $year: "$subStartDate" }, month: { $month: "$subStartDate" }, subType: "$subType" },
count: { $sum: 1 }
},
},
{
/* sort descending (latest subscriptions first) */
$sort: {
'_id.year': -1,
'_id.month': -1
}
},
{
$limit: 100,
},
])
However, when I want to receive data from the readercollections (Subscriber Model) for ALL year data, I'm not getting the desired results (if any) from all of the things I'm trying (I'm posting the best attempt result below):
Publication.aggregate([
{
$match:
{
user: mongoose.Types.ObjectId(id),
isDeleted: false
}
},
{
$project: {
_id: 1,
}
},
{
$lookup: {
from: "readercollections",
let: { "id": "$_id" },
pipeline: [
{
$match:
{
$expr: {
$and: [
{ $eq: ["$publication", "$$id"] },
{ "$eq": [{ "$year": "$subStartDate" }, new Date().getFullYear()] }
],
}
}
},
{ $project: { subStartDate: 1, subType: 1 } }
],
as: "founditem"
}
},
// {
// /* group by year and month of the subscription event */
// $group: {
// _id: { year: { $year: "$founditem.subStartDate" }, month: { $month: "$foundtitem.subStartDate" }, subType: "$founditem.subType" },
// count: { $sum: 1 }
// },
// },
// {
// /* sort descending (latest subscriptions first) */
// $sort: {
// '_id.year': -1,
// '_id.month': -1
// }
// },
], function (err, result) {
if (err) {
console.log(err);
} else {
res.json(result);
}
})
Which returns the desired data without the $group (commented out) but I need the $group to work or I'm going to have to map a dynamic array based on month and subtype which is completely inefficient.
When I'm diagnosing, it looks like this $group is the issue but I can't see how to fix as it works in the singular $year/$month group. So I tried the following:
{
/* group by year and month of the subscription event */
$group: {
_id: { year: { $year: "$subStartDate" }, month: { $month: "$subStartDate" }, subType: "$founditem.subType" },
count: { $sum: 1 }
},
},
And it returned the $founditem.subType fine, but any count or attempt to get $year or $month of the $founditem.subStartDate gave a BSON error.
The output from the single publication ID lookup in the reader collection call that works (and is plugging into the line graph perfectly) is:
[
{
"_id": {
"year": 2019,
"month": 11,
"subType": "digital"
},
"count": 1
},
{
"_id": {
"year": 2019,
"month": 11,
"subType": "print"
},
"count": 3
}
]
This is the output I'd like for ALL publications rather than just a single lookup of a publication ID within the reader collection.
Thank you for any assistance and please let me know if you need more details!!

Find an document and it's referenced documents

I'm getting throw trying to populate related fields using aggregation pipeline in mongodb, plain relations works as well (I mean oid reference to oid in other collection) but what happens when you have an object array that one of its properties reference a sub document. If I wasn't clear, here a little representation.
Suppose I have the following schema:
Profile {
_id: {
type: mongoose.Schema.Types.ObjectId
},
Gender: {
type: mongoose.Schema.Types.ObjectId,
ref: "Gender"
},
PhoneNo: [
Value: {
type: String
},
PhoneType: {
type: mongoose.Schema.Types.ObjectId,
ref: "PhoneType"
}
]
}
PhoneType {
_id: {
type: mongoose.Schema.Types.ObjectId
},
Name: {
type: String
}
}
Gender {
_id: {
type: mongoose.Schema.Types.ObjectId
},
Name: {
type: String
}
}
So, I would like to get results like:
{
_id: $oid,
Gender: {Value:"Masculine"},
PhoneNo: {
Value: "000000",
PhoneType: {
_id: $oid
Name: "Cell"
}
}
},
{
_id: $oid,
Gender: {Value:"Feminine"},
PhoneNo: {
Value: "999999",
PhoneType: {
_id: $oid
Name: "Work"
}
}
}
Lookup in Gender works good, but when I try to lookup PhoneNo then I lost the value property.
What I'm getting is:
Pay attention to the field/property Value, is lost.
{
_id: $oid,
Gender: {Value:"Masculine"},
PhoneNo: [{
PhoneType: {
_id: $oid
Name: "Cell"
}
}]
},
{
_id: $oid,
Gender: {Value:"Feminine"},
PhoneNo: [{
PhoneType: {
_id: $oid
Name: "Work"
}
}]
}
Here is the code that I used:
{
from: 'PhoneType',
'let': {"ids":"$PhoneNo.PhoneType"},
"pipeline": [
{ "$match": { "$expr": { "$in": ["$_id", "$$ids"] } } },
],
as: "PhoneNo"
}
How can I do that? :S
So this is a walkthrough for your problem, we also include Values in lookup let declaration it'll be an array because it's stored in an array and in project stage we'll fetch indexOf Values array where $$ROOT 's id and id match
https://mongoplayground.net/p/UUXus3N3ncw
Input collections:
"user": [
{
_id: {
type: "1"
},
Gender: 12,
PhoneNo: [
{
Value: "name",
PhoneType: 21
},
{
Value: "name2",
PhoneType: 212
}
]
}
],
"gender": [
{
"_id": 12,
"Name": "Male"
}
],
"PhoneType": [
{
"_id": 21,
name: "Sus",
val: "750"
},
{
"_id": 212,
name: "Suss",
val: "7500"
}
]
Aggregate Pipeline:
user.aggregate([
{
$lookup: {
from: "PhoneType",
"let": {
"ids": "$PhoneNo.PhoneType",
"val": "$PhoneNo.Value",
},
"pipeline": [
{
"$match": {
"$expr": {
"$in": [
"$_id",
"$$ids"
]
}
}
},
{
$project: {
_id: 0,
Value: {
$arrayElemAt: [
"$$val",
{
$indexOfArray: [
"$$ids",
"$$ROOT._id"
]
}
]
},
PhoneType: "$$ROOT"
}
}
],
as: "PhoneNo"
}
}
])
Output Result:
[
{
"Gender": 12,
"PhoneNo": [
{
"PhoneType": {
"_id": 21,
"name": "Sus",
"val": "750"
},
"Value": "name"
},
{
"PhoneType": {
"_id": 212,
"name": "Suss",
"val": "7500"
},
"Value": "name2"
}
],
"_id": {
"type": "1"
}
}
]

Resources