MongoDB: $filter multiple arrays - mongodb

I have an collection like:
{
_id: 0,
items: [
{ item_id: 43, quantity: 2, price: 10 },
{ item_id: 2, quantity: 1, price: 240 }
],
T: [
{ item_id: 2993, quantity: 3, price: 110 },
{ item_id: 90103, quantity: 4, price: 5 },
{ item_id: 398, quantity: 1, price: 300 }
]
}
{
_id: 1,
items: [
{ item_id: 23, quantity: 3, price: 110 },
{ item_id: 103, quantity: 4, price: 5 },
{ item_id: 38, quantity: 1, price: 300 }
],
T: [
{ item_id: 23, quantity: 3, price: 110 },
{ item_id: 103, quantity: 4, price: 5 },
{ item_id: 38, quantity: 1, price: 300 }
]
}
{
_id: 2,
items: [
{ item_id: 4, quantity: 1, price: 23 }
],
T: [
{ item_id: 203, quantity: 3, price: 110 },
{ item_id: 003, quantity: 4, price: 5 },
{ item_id: 398, quantity: 1, price: 300 }
]
}
I want to return a all the items in the items array with a price >= 100. That is done with the following:
$project: {
items: {
$filter: {
input: "$items",
as: "item",
cond: { $gte: [ "$$item.price", 100 ] }
}
}
}
How can I expand this expression to $filter on the items array and the T array all elements that have a price >= 100?

You can include both the items and T fields in the same $project, each with its own $filter:
db.test.aggregate([
{$project: {
items: {
$filter: {
input: "$items",
as: "item",
cond: { $gte: [ "$$item.price", 100 ] }
}
},
T: {
$filter: {
input: "$T",
as: "t",
cond: { $gte: [ "$$t.price", 100 ] }
}
}
}}
])

Related

How do I get the last object array in MongoDB based on condition

I want to query mongoDB Data:
mongoDBData:
[{ cost: 1, productCode: "A"}],
[{ cost: 2, productCode: "A"}],
[{ cost: 3, productCode: "B"}],
[{ cost: 4, productCode: "A"}],
[{ cost: 5, productCode: "B"}],
[{ cost: 6, productCode: "A"}],
[{ cost: 7, productCode: "C"}],
[{ cost: 8, productCode: "C"}],
[{ cost: 9, productCode: "D"}],
[{ cost: 10, productCode: "D"}]
based on an array. This is the array:
mappedProductCode = ["A", "B", "C"]
This is my desired result:
desiredResult = [
{productCode: "A", cost: 6},
{productCode: "B", cost: 5},
{productCode: "C", cost: 8},
]
Here's how I did by making a loop:
productCost=[]
for (let i = 0; i < mappedProductCode.length; i++) {
const skuLoop = mappedProductCode[i];
const skuCost = await PosCost.findOne({ productCOde: mappedProductCode[i] }).sort({ _id: -1 }).limit(1);
const loopPrice = skuCost? skuCost.cost : 0;
productCost[i] = {
sku: skuLoop,
cost: loopPrice
};
}
My desired result is still achieved but it is very slow, what do I do to improve the code?
Maybe something like this:
db.collection.aggregate([
{
$match: {
productCode: {
$in: [
"A",
"B",
"C"
]
}
}
},
{
$sort: {
productCode: 1,
cost: -1
}
},
{
$group: {
_id: "$productCode",
cost: {
$first: "$cost"
}
}
},
{
$project: {
productCode: "$_id",
cost: 1,
_id: 0
}
},
{
$sort: {
productCode: 1
}
}
])
Explained:
$match the products that you need
$sort by productCode and descending cost
$group by productCode and get only the first cost from the descending order
$project the _id to the original key name "productCode"
$sort by needed final result order
playground

How to convert timestamp to date in an array with Mongodb?

I need to convert the timestamp to date and update it in the same field, in Mongosh.
Here is the data:
{ _id: 1,
name: 'Annelise',
movies: [ { movieid: 12, rating: 3, timestamp: 97830291 } ] },
{ _id: 2,
name: 'Maria',
movies: [ { movieid: 3, rating: 3, timestamp: 978301398 } ]
},
{
_id: 3,
name: 'Paul',
movies: [ { movieid: 23, rating: 4, timestamp: 978302174 } ]
},
{
_id: 4,
name: 'Fred',
movies: [
{ movieid: 23, rating: 4, timestamp: 978302174 },
{ moviedid: 45, rating: 2, timestamp: 978302149 }
]
},
{ _id: 5, name: 'Annelise', timestamp: 97830291 },
{ _id: 6, name: 'Maria', timestamp: 978301398 },
{ _id: 7, name: 'Paul', timestamp: 978302174 },
{ _id: 8, name: 'Fred', timestamp: 978302149 }
I tried using an aggregation pipeline, but I have 2 problems:
This command works to update the timestamps for _id 5 to 8, but how can I save it to the same field?
db.usertest.aggregate(
[ {"$project":
{"timestamp": { $dateToString: { format: "%Y-%m-%d", date: { "$toDate": {$toLong:"$timestamp"}}}}
} } ])
How can I update the timestamp inside the movies array for _id 1 to 4?
I tried the following but this does not work:
db.usertest.aggregate( [ {"$project": {"timestamp": { $dateToString: { format: "%Y-%m-%d", date: { "$toDate": {$toLong:"$timestamp"}}}}}} ])
Thanks for your help!
You can use an aggregation pipeline in update. In MongoDB, the date in unix timestamp is in milliseconds(ms). So, multiply your raw timestamp with 1000 before converting them to date.
For _id 1-4, use $map to perform element-wise transformation.
db.collection.update({
_id: {
$in: [
1,
2,
3,
4
]
}
},
[
{
"$addFields": {
"movies": {
"$map": {
"input": "$movies",
"as": "m",
"in": {
movieid: "$$m.movieid",
rating: "$$m.rating",
timestamp: {
"$toDate": {
"$multiply": [
{
$toLong: "$$m.timestamp"
},
1000
]
}
}
}
}
}
}
}
],
{
multi: true
})
Mongo playground
For _id: 5-8, simply update the field.
db.collection.update({
_id: {
$in: [
5,
6,
7,
8
]
}
},
[
{
"$addFields": {
"timestamp": {
"$toDate": {
"$multiply": [
{
$toLong: "$timestamp"
},
1000
]
}
}
}
}
],
{
multi: true
})
Mongo playground

Express + Mongoose + Aggregation + Calculate Available Stock

I'm using express js and mongoose and i'm new to these platforms. It would be great if someone can help me on this. Please refer the the below data and I'm looking for output like:
itemsizeId: 609578ca23033e55886e7590, AvailableQuantity: 112
itemsizeId: 609578ca23033e55886e758f, AvailableQuantity: 495
Note: Group by movementtype (inward / outward).
Available Stock: inward - outward
[
{
_id: '609fb1a1a7ed990f30d6cae2',
refId: 'Purchase-1',
itemsizeId: '609578ca23033e55886e7590',
itemId: '609578ca23033e55886e758e',
sizeId: '60950c0ba4982390f8dfed79',
movementdate: '2021-05-15T11:33:52.894Z',
movementtype: 'inward',
movementcategory: 'purchase',
quantity: 100,
id: '609fb1a1a7ed990f30d6cae2',
},
{
_id: '609fb1cba7ed990f30d6cae3',
refId: 'Purchase-2',
itemsizeId: '609578ca23033e55886e7590',
itemId: '609578ca23033e55886e758e',
sizeId: '60950c0ba4982390f8dfed79',
movementdate: '2021-05-15T11:34:35.118Z',
movementtype: 'inward',
movementcategory: 'purchase',
quantity: 20,
id: '609fb1cba7ed990f30d6cae3',
},
{
_id: '609fb1fda7ed990f30d6cae4',
refId: 'Sale-1',
itemsizeId: '609578ca23033e55886e7590',
itemId: '609578ca23033e55886e758e',
sizeId: '60950c0ba4982390f8dfed79',
movementdate: '2021-05-15T11:35:25.068Z',
movementtype: 'outward',
movementcategory: 'sales',
quantity: 5,
id: '609fb1fda7ed990f30d6cae4',
},
{
_id: '609fb255a7ed990f30d6cae5',
refId: 'Purchase-3',
itemsizeId: '609578ca23033e55886e758f',
itemId: '609578ca23033e55886e758e',
sizeId: '60950be9a4982390f8dfed78',
movementdate: '2021-05-15T11:36:53.835Z',
movementtype: 'inward',
movementcategory: 'purchase',
quantity: 500,
id: '609fb255a7ed990f30d6cae5',
},
{
_id: '609fb27ea7ed990f30d6cae6',
refId: 'Sale-2',
itemsizeId: '609578ca23033e55886e758f',
itemId: '609578ca23033e55886e758e',
sizeId: '60950be9a4982390f8dfed78',
movementdate: '2021-05-15T11:37:34.066Z',
movementtype: 'outward',
movementcategory: 'sales',
quantity: 8,
id: '609fb27ea7ed990f30d6cae6',
},
]
I have tried till below code and I'm struck to complete it:
const itemStock = await itemStockMovementModel.aggregate([
{
$match: { movementtype: 'inward' },
},
{
$group: {
_id: {
itemsizeId: '$itemsizeId',
},
quantity: { $sum: '$quantity' },
},
},
])
$group by itemsizeId and get quantity sum by condition for inward and outward
$subtract to inward - outward
const itemStock = await itemStockMovementModel.aggregate([
{
$group: {
_id: "$itemsizeId",
inward: {
$sum: { $cond: [{ $eq: ["$movementtype", "inward"] }, "$quantity", 0] }
},
outward: {
$sum: { $cond: [{ $eq: ["$movementtype", "outward"] }, "$quantity", 0] }
}
}
},
{
$project: {
quantity: {
$subtract: ["$inward", "$outward"]
}
}
}
])
Playground
You can use second formula as well, if you don't have third type in movementtype,
$group by itemsizeId
check condition if movementtype is inward then sum quantity otherwise sum negative quantity
const itemStock = await itemStockMovementModel.aggregate([
{
$group: {
_id: "$itemsizeId",
quantity: {
$sum: {
$cond: [
{ $eq: ["$movementtype", "inward"] },
"$quantity",
{ $subtract: [0, "$quantity"] }
]
}
}
}
}
])
Playground

Merge 2 array objects into 1

I am trying to merge objects within an array based on another array. What I have is,
Orders
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2 },
{ item_id: 2, quantity: 1}
]
}
Items
{
_id: 43,
item_name: "tshirt"
}
{
_id: 2,
item_name: "jeans"
}
After lookup I am getting the below document with 2 arrays - entries and items. I would like to have entries contain the corresponding item.
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2 },
{ item_id: 2, quantity: 1}
]
items: [
{ item_id: 43, item_name: "tshirt" },
{ item_id: 2, item_name: "jeans" },
]
}
Desired Output:
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2, item_name: "tshirt" },
{ item_id: 2, quantity: 1, item_name: "jeans"}
]
}
I was able to achieve this by unwinding both arrays, addFields and then grouping as mentioned by #whoami.
See current pipeline: https://mongoplayground.net/p/rL-Lzmfuw9h
Is there any way to achieve this without unwinding?

Mongo group inside $addToSet

I have the following set of objects:
[
{
id: 1,
clientId: 1,
cost: 200
},
{
id: 1,
clientId: 2,
cost: 500
},
{
id: 1,
clientId: 2,
cost: 800
},
{
id: 2,
clientId: 1,
cost: 600
},
{
id: 2,
clientId: 2,
cost: 100
}
]
And I made a group of that with:
db.collection.aggregate(
{
'$group': {
'_id': '$id',
'clients': {
'$addToSet': {
'id': '$clientId',
'cost': '$cost'
}
}
}
}
)
So I obteined the following:
[
{
'_id': 1,
'clients': [
{
id: 1,
cost: 200
},
{
id: 2,
cost: 500
},
{
id: 2,
cost: 800
}
],
'_id': 2,
'clients': [
{
id: 1,
cost: 600
},
{
id: 2,
cost: 100
}
]
}
]
As you can see in the array of clients of the first value, I have 2 repeated and what I want is to have 1 with the cost added. So instead of have:
'clients': [
{
id: 1,
cost: 200
},
{
id: 2,
cost: 500
},
{
id: 2,
cost: 800
}
]
I need:
'clients': [
{
id: 1,
cost: 200
},
{
id: 2,
cost: 1300
}
]
So my question is: how can I do that? Because $addToSet nor $push allow $sum.
You can use aggregation operators to get expected output like following:
db.collection.aggregate({
"$group": {
"_id": {
"mainId": "$id",
"client": "$clientId"
},
"cost": {
"$sum": "$cost"
}
}
}, {
"$project": {
"mainId": "$_id.mainId",
"clients": {
"clientId": "$_id.client",
"cost": "$cost"
},
"_id": 0
}
}, {
"$group": {
"_id": "$mainId",
"clients": {
"$push": "$clients"
}
}
})