How to sum item in transaction subdocument mongodb from other document relationship - javascript

I'm new in mongodb and try small project and document are design just like below
products
[
{
_id:ObjectId('60d87d6fafd7a1377c03b2e6'),
productName:"noodles instant",
description:"lorem ipsum ... ... ...."
},
{
_id:ObjectId('60d87d6fafd7a1377c03b2e7'),
productName:"Sandal",
description:"lorem ipsum ... ... ...."
}
]
and my transaction document has structure is just like below
transactions
[
{
_id:ObjectId('60d8781031308dbf565eaaa8'),
trasactionSerial:"TRS20210000001",
transactionDate:2021-06-27T13:30:23.369+00:00,
productInTransactions:[
{
_id:ObjectId('60d8781031308dbf565eaab9'),
productId:ObjectId('60d87d6fafd7a1377c03b2e6'),
qty:1500
},
{
_id:ObjectId('60d8781031308dbf565eaa47'),
productId:ObjectId('60d87d6fafd7a1377c03b2e7'),
qty:200
}
]
},
{
_id:ObjectId('60d8781031308dbf565eaab7'),
trasactionSerial:"TRS20210000002",
transactionDate:2021-06-27T13:32:23.369+00:00,
productInTransactions:[
{
_id:ObjectId('60d8781031308dbf565eaab9'),
productId:ObjectId('60d87d6fafd7a1377c03b2e6'),
qty:100
},
{
_id:ObjectId('60d8781031308dbf565eaa47'),
productId:ObjectId('60d87d6fafd7a1377c03b2e7'),
qty:300
}
]
}
]
from the transaction I hope will get some result like below from the same date in transactionDate field
result
[
{
_id:ObjectId('60d87d6fafd7a1377c03b2e6'),
productName:"noodles instant",
qtyInTransaction:1600
},
{
_id:ObjectId('60d87d6fafd7a1377c03b2e7'),
productName:"Sandal",
qtyInTransaction:500
},
]
sorry maybe my English is poor to elaborate it
thanks for any feedback and your help

Use the $lookup stage which has $group in its pipeline option.
db.products.aggregate([
{
"$lookup": {
"from": "transactions",
"let": {
"pId": "$_id"
},
"pipeline": [
{
"$unwind": "$productInTransactions"
},
{
"$match": {
"$expr": {
"$eq": [
"$productInTransactions.productId",
"$$pId"
]
},
},
},
{
"$group": {
"_id": "$$pId",
"qtyInTransaction": {
"$sum": "$productInTransactions.qty"
}
},
},
],
"as": "matchedTransactions"
}
},
{
"$project": {
"_id": 1,
"description": 1,
"qtyInTransaction": {
"$arrayElemAt": [
"$matchedTransactions.qtyInTransaction",
0
]
}
},
},
])
Mongo Playground Sample Execution

Related

How to get object value with dynamic key in $project in mongoDB

I want to retrieve a value from an object with a dynamic key
[
{
"_id": 1,
"item": "sweatshirt",
"price": {
"INR": 45.99
},
"currency": 'INR'
}
]
db.collection.aggregate([
{
"$project": {
"pricenew": "$price.currency"
}
}
])
If I do price.INR it will work fine but here I have currency dynamic, so I want something like price.currency but here currency is coming like "INR" and it gives no data.
I really appreciate any help you can provide.
You need to convert the price object to an array using $objectToArray, filter it and then convert it back, like so:
db.collection.aggregate([
{
$replaceRoot: {
newRoot: {
"$mergeObjects": [
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
"$objectToArray": "$price"
},
cond: {
$eq: [
"$$this.k",
"$currency"
]
}
}
},
in: {
k: "pricenew",
v: "$$this.v"
}
}
}
},
{
_id: "$_id"
}
]
}
}
}
])
Mongo Playground

Find all n days documents in MongoDB

I've been looking to get data for the last n days. For example, I want to just all the data of the last 3 days. After implementing a solution from another question in StackOverflow I'm not getting all the documents. I am only getting one document. If I want to see the documents for the last 3 days it is only showing data for one particular day.
Here's my Schema:
dayWiseClicks: [
{
date: {
type: Date,
},
dailyClicks: {
type: Number,
default: 0,
},
},
],
Here's the data I have before performing the query:
{
"_id": {
"$oid": "61eff4bacf8335c7013f8065"
},
"dayWiseClicks": [
{
"dailyClicks": 3,
"_id": {
"$oid": "61eff5db5dca56cae4530db2"
},
"date": {
"$date": "2022-01-24T18:00:00.000Z"
}
},
{
"dailyClicks": 4,
"_id": {
"$oid": "61eff60b5dca56cae4530db6"
},
"date": {
"$date": "2022-01-25T18:00:00.000Z"
}
},
{
"dailyClicks": 2,
"_id": {
"$oid": "61eff64a5dca56cae4530dba"
},
"date": {
"$date": "2022-01-26T18:00:00.000Z"
}
},
{
"dailyClicks": 7,
"_id": {
"$oid": "61f60ce51f14b01f8f5be936"
},
"date": {
"$date": "2022-01-29T18:00:00.000Z"
}
},
{
"dailyClicks": 11,
"_id": {
"$oid": "61f7b1d3931b0f8bc33703d4"
},
"date": {
"$date": "2022-01-30T18:00:00.000Z"
}
},
{
"dailyClicks": 8,
"_id": {
"$oid": "61f8bdf63cc3a51b72474cb9"
},
"date": {
"$date": "2022-01-31T18:00:00.000Z"
}
},
{
"dailyClicks": 7,
"_id": {
"$oid": "61fba7159692624ce8ea04d6"
},
"date": {
"$date": "2022-02-02T18:00:00.000Z"
}
}
],
}
In theory, if I want to see the last 3 days of data. It should be showing data of 31st Jan, 2nd February but It is only showing Data of 31st January.
Here's the data I am getting:
{
"message": "Url By ID",
"result": {
"_id": "61eff4bacf8335c7013f8065",
"dayWiseClicks": [
{
"dailyClicks": 8,
"_id": "61f8bdf63cc3a51b72474cb9",
"date": "2022-01-31T18:00:00.000Z"
}
]
}
}
Here's my Code:
exports.lastNDays = async (req, res) => {
try {
const url = await URL.findById(
{ _id: req.params.id },
{
dayWiseClicks: {
$elemMatch: {
date: {
$gte: moment().add(-3, "days"),
},
},
},
}
)
return res.status(200).json({
message: "Url By ID",
result: url,
});
} catch (error) {
return res.status(404).json({ error: error.message });
}
};
Can any one tell me exactly where I am making the mistake?
The aggregation->$filter option seems more suitable for the task , example:
db.collection.aggregate([{
$match: {
"_id": {
"$oid": "61eff4bacf8335c7013f8065"
}
}
},
{
$project: {
dayWiseClicks: {
$filter: {
input: "$dayWiseClicks",
as: "item",
cond: {
$gte: [
"$$item.date",
{
"$date": "2022-01-30T18:00:00.000Z"
}
]
}
}
}
}
}
])
Explained:
$match single document by _id
$filter only the dayWiseClicks greater or equal to certain date.
playground
If your dayWiseClicks are in natural date-ascending order (and it seems that way), then to generically capture the last 3 days of any sequence of dates can be done with $slice:
db.foo.aggregate([
{$addFields: {dayWiseClicks: {$slice: ["$dayWiseClicks", -3]}}}
]);
You can prepend $match stages as detailed in previous answers.
NOTE: Coming up in v5.4 (available now in 5.2 rapid release for MongoDB Atlas) is the long-awaited sortArray operator. If the dayWiseClicks was not in date order, then making it that way and finding the last 3 dates is freshingly simple:
db.foo.aggregate([
{$addFields: {dayWiseClicks: {$slice: [{$sortArray:{input: "$dayWiseClicks", sortBy: {"date":1}}, -3]}}}
]);
Similarly, to get the last 3 dates but in descending order:
db.foo.aggregate([
{$addFields: {dayWiseClicks: {$slice: [{$sortArray:{input: "$dayWiseClicks", sortBy: {"date":-1}}, 3]}}}
]);

Add alias to the collection targeted in lookup

I am a beginner in MongoDB. I will try my best to explain this as easy as possible, so I will take an internet example of collection, instead of the more complicated schema I am working with!
I have two collections as follows:
Users:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE"
}
]
Posts:
[
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8"
}
]
I want to join these two collections using $lookup. So I use the following aggregation on the users collection:
{
'$addFields': {
'userStrId': {
'$toString': '$_id'
}
}
}, {
'$lookup': {
'from': 'posts',
'localField': 'userStrId',
'foreignField': 'user_id',
'as': 'user_posts'
}
},
I used $addFields to add the _id field of the user as a string field, so I can use it in $lookup,
The following result is generated:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE",
"user_posts": [
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8"
}
]
}
]
The question that I have right now is, how can I add a field to each of the documents in the user_posts such that I get the following result:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE",
"user_posts": [
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8",
"post_id":"610bce417b0c4008346547bc"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8",
"post_id": "610bce417b0c4008346547be"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8",
"post_id":"610bce417b0c4008346547bd"
}
]
}
]
post_id added to each of the documents, and its value equal to the _id of that document converted to string.
You can add a stage at the end of your pipeline stages,
$map to iterate loop of user_posts
$mergeObjects to merge current object of user_posts and new fields user_id and post_id
{
$addFields: {
user_posts: {
$map: {
input: "$user_posts",
in: {
$mergeObjects: [
"$$this",
{
user_id: "$userStrId",
post_id: { $toString: "$$this._id" }
}
]
}
}
}
}
}
Playground

MongoDB lookup and map 2 arrays of result

There are 2 array fields after I looked up in MongoDB aggregation pipeline.
the first one
[
{
"colorId": "60828a1b216b0972da695f2a",
"name": "Exellent",
"description": "Great work"
}
]
and the second one
[
{
"_id": "60828a1b216b0972da695f2a",
"colorName": "Green",
"hexColorCodes": "#2D9D78",
"sequence": 1,
"isActivated": true,
"created_at": "2021-04-23T08:49:31.729Z",
"updated_at": "2021-04-23T08:49:31.729Z",
"__v": 0,
"isDefault": true
}
]
the result I want is
[
{
"colorId": "60828a1b216b0972da695f2a",
"name": "Exellent",
"description": "Great work",
"colorName": "Green",
"hexColorCodes": "#2D9D78"
}
]
then I want to map colorName and hexColorCodes to the first array. Here is my aggregate pipeline
db.collection.aggregate([
{
$lookup: {
from: "color_tags",
localField: "colors.colorId",
foreignField: "_id",
as: "tempColors",
},
},
{
$addFields: {
stages3: {
$map: {
input: "$colors",
in: {
$mergeObjects: [
"$$this",
{
$arrayElemAt: [
"$tempColors",
{
$indexOfArray: [
"$tempColors._id",
"$$this.colors.colorId",
],
},
],
},
],
},
},
},
},
}
])
but the result is not what I expected. It mapped with incorrect id. Please suggest.
$map to iterate loop of first array
$filter to iterate loop of second array and match colorId with _id and return matching result
$arrayElemAt to get first matching element
$mergeObjects to merge current object with return result from second array
{
$project: {
first: {
$map: {
input: "$first",
as: "f",
in: {
$mergeObjects: [
"$$f",
{
$arrayElemAt: [
{
$filter: {
input: "$second",
cond: { $eq: ["$$this._id", "$$f.colorId"] }
}
},
0
]
}
]
}
}
}
}
}
If you want to result specific fields then add a $project stage at the end,
{
$project: {
"first.colorId": 1,
"first.name": 1,
"first.description": 1,
"first.colorName": 1,
"first.hexColorCodes": 1
}
}
Playground

how to use lookup on array object mongodb

I'm new on mongodb. so I try design the schema for my collection is like below
all the ObjectId is not real
stockIn documents
{
serial:"stk0001",
date:'2021-06-11',
productInTransation:[
{
_id:"60ae220b066b8d9861118cb1",
productId:"60ae220b066b8d9861118cb2"
qty:2
},
{
_id:"60ae220b066b8d9861118cb1",
productId:"60ae220b066b8d9861118cb1",
qty:2
}
]
}
and I have a products collection
[
{
_id:"60ae220b066b8d9861118cb5",
name:"sepatu"
},
{
_id:"60ae220b066b8d9861118cb4",
name:"sendal"
}
]
so what I expect from those documents is just like below
{
serial:"stk0001",
date:'2021-06-11',
productInTransation:[
{
_id:"60ae220b066b8d9861118cb1",
productId:"60ae220b066b8d9861118cb2"
qty:2,
product:
{
_id:"60ae220b066b8d9861118cb5",
name:"sepatu"
},
},
{
_id:"60ae220b066b8d9861118cb1",
productId:"60ae220b066b8d9861118cb1",
qty:2,
product:
{
_id:"60ae220b066b8d9861118cb4",
name:"sendal"
}
}
]
}
this collection is just simplified from the real case.
and the problem I don't know how to do a query on mongodb, so the output will same as the expected. thank's for any help
You can use $lookup
$unwind to deconstruct the array
$lookup to join collections
$ifNull to make sure this doesn't give any NPE when we take from first element from the joined array using $arrayElemAt
$group to reconstruct the array
Here is the code
db.stockIn.aggregate([
{ $unwind: "$productInTransation" },
{
"$lookup": {
"from": "products",
"localField": "productInTransation.productId",
"foreignField": "_id",
"as": "productInTransation.product"
}
},
{
"$addFields": {
"productInTransation.product": {
"$ifNull": [ { "$arrayElemAt": [ "$productInTransation.product", 0 ] }, [] ]
}
}
},
{
"$group": {
"_id": "$_id",
"date": { "$first": "$date" },
"serial": { "$first": "$serial" },
"productInTransation": { $push: "$productInTransation" }
}
}
])
Working Mongo playground

Categories

Resources