Mongodb Aggregate Filter Array Of Array Of Array - mongodb

We would like to filter SKU's List which has verificationData data and differenceInStock difference greater than or Less than 0
Here is an example Data Set.
[
{
"_id": "636e0beaa13ef73324e613f0",
"status": "ACTIVE",
"inventory": 132,
"parentCategory": [
"Salt"
],
"title": "Aashirvaad MRP: 28Rs Salt 27 kg Bopp Bag (Set of 1 kg x 27)",
"createdAt": "2022-11-11T08:46:34.950Z",
"updatedAt": "2022-11-24T17:43:27.361Z",
"__v": 3,
"verificationData": [
{
"_id": "637c57ebbe783a9a138fc2d3",
"verificationDate": "2022-11-22T05:02:35.155Z",
"items": {
"listingId": "636e0beaa13ef73324e613f0",
"phyiscalVerification": [
{
"verifiedBy": "634534e72ef6462fcb681a39",
"closingStock": 178,
"phyiscalStock": 178,
"differenceInStock": 0,
"verifiedAt": "2022-11-22T10:19:38.388Z",
"_id": "637ca23abe783a9a1394f402"
}
],
"_id": "637ca23abe783a9a1394f401"
},
"yearMonthDayUTC": "2022-11-22"
},
{
"_id": "637d9b65be783a9a13998726",
"verificationDate": "2022-11-23T04:02:45.804Z",
"items": {
"listingId": "636e0beaa13ef73324e613f0",
"phyiscalVerification": [
{
"verifiedBy": "634534e72ef6462fcb681a39",
"closingStock": 161,
"phyiscalStock": 167,
"differenceInStock": 6,
"verifiedAt": "2022-11-23T09:52:36.815Z",
"_id": "637ded64be783a9a13a29d55"
}
],
"_id": "637ded64be783a9a13a29d54"
},
"yearMonthDayUTC": "2022-11-23"
},
{
"_id": "637f0254be783a9a13a94354",
"verificationDate": "2022-11-24T05:34:12.995Z",
"items": {
"listingId": "636e0beaa13ef73324e613f0",
"phyiscalVerification": [
{
"verifiedBy": "634534e72ef6462fcb681a39",
"closingStock": 144,
"phyiscalStock": 146,
"differenceInStock": 2,
"verifiedAt": "2022-11-24T12:02:28.123Z",
"_id": "637f5d54be783a9a13b1039a"
}
],
"_id": "637f5d54be783a9a13b10399"
},
"yearMonthDayUTC": "2022-11-24"
},
{
"_id": "2022-11-25",
"yearMonthDayUTC": "2022-11-25",
"items": null
}
]
},
{
"_id": "62b5c39062ddb963fc64c42d",
"status": "ACTIVE",
"inventory": 10,
"parentCategory": [
"Salt"
],
"finalMeasurementUnit": "kg",
"finalMeasure": "1 kg",
"title": "Marvella Citric Acid Lemon Salt 1 kg Pouch (Set of 500 gm x 2)",
"createdAt": "2022-06-24T14:00:49.052Z",
"updatedAt": "2022-11-21T11:04:21.643Z",
"__v": 2,
"verificationData": [
{
"_id": "2022-11-22",
"yearMonthDayUTC": "2022-11-22",
"items": null
},
{
"_id": "2022-11-23",
"yearMonthDayUTC": "2022-11-23",
"items": null
},
{
"_id": "2022-11-24",
"yearMonthDayUTC": "2022-11-24",
"items": null
},
{
"_id": "2022-11-25",
"yearMonthDayUTC": "2022-11-25",
"items": null
}
]
}
]
This could have array of 100+ SKU's
Our Aggregate Functions is as Follows
let reqData = await userListing.aggregate([
{
$match: {
warehouseId: { $eq: ObjectId(warehouseId) },
parentCategory: { $in: catList },
isWarehouseListing: { $eq: true },
isBlocked: { $ne: true },
isArchived: { $ne: true },
},
},
{ $sort: { whAddedAt: -1 } },
{
$lookup: {
from: "listingstockverifications",
let: { listId: "$_id" },
pipeline: [
{
$match: {
verificationDate: {
$gte: newFromDate,
$lt: newToDate,
},
},
},
{
$project: {
verificationDate: 1,
items: {
$filter: {
input: "$items",
cond: {
$and: [
/* {
"$$this.phyiscalVerification": {
$filter: {
input: "$$this.phyiscalVerification",
as: "psitem",
cond: { $gt: [ "$$psitem.differenceInStock", 0 ] },
},
},
}, */
{
$eq: ["$$this.listingId", "$$listId"],
},
],
},
},
},
yearMonthDayUTC: {
$dateToString: {
format: "%Y-%m-%d",
date: "$verificationDate",
},
},
},
},
{ $unwind: "$items" },
],
as: "stockVerification",
},
},
{
$addFields: {
verificationData: {
$map: {
input: dummyArray,
as: "date",
in: {
$let: {
vars: {
dateIndex: {
$indexOfArray: [
"$stockVerification.yearMonthDayUTC",
"$$date",
],
},
},
in: {
$cond: {
if: { $ne: ["$$dateIndex", -1] },
then: {
$arrayElemAt: ["$stockVerification", "$$dateIndex"],
},
else: {
_id: "$$date",
yearMonthDayUTC: "$$date",
items: null,
},
},
},
},
},
},
},
},
},
{
$project: {
stockVerification: 0,
},
},
]);
At Last now we would like to filter the SKU List the which has following Data
verificationData[].items.phyiscalVerification[].differenceInStock is Greater than or Less than 0
Expected Output in the following Exmaple would be 1st SKUs
as 2nd SKU does not have any Item Data
and even if in 3rd SKU if we got Item Data but should match the following condition
verificationData[].items.phyiscalVerification[].differenceInStock is Greater than or Less than 0
Thank you for taking your time to read and support.

You can add these two following stages to your aggregation, The idea is simple - just filter out all subdocuments that do not match the condition.
Because of the nested structure it's just not the sexiest of pipelines but it will suffice.
db.collection.aggregate([
{
$match: {
$or: [
{
"verificationData.items.phyiscalVerification.differenceInStock": {
$gt: 0
}
},
{
"verificationData.items.phyiscalVerification.differenceInStock": {
$lt: 0
}
}
]
}
},
{
$addFields: {
verificationData: {
$filter: {
input: {
$map: {
input: {
$filter: {
input: "$verificationData",
as: "verification",
cond: {
$ne: [
"$$verification.items",
null
]
}
}
},
as: "top",
in: {
$mergeObjects: [
"$$top",
{
"items": {
"$mergeObjects": [
"$$top.items",
{
phyiscalVerification: {
$filter: {
input: "$$top.items.phyiscalVerification",
as: "pshycical",
cond: {
$ne: [
"$$pshycical.differenceInStock",
0
]
}
}
}
}
]
}
}
]
}
}
},
cond: {
$gt: [
{
$size: "$$this.items.phyiscalVerification"
},
0
]
}
}
}
}
}
])
Mongo Playground

Related

MongoDB Count With Condition within Project with $eq

I'm trying to count my "$attendance.status" with aggregation mongodb.
I've get my data with relations. then i want to count by relation columns like 'present', 'off', etc.
code
Employee.aggregate([
{
$lookup: {
from: "Attendance",
let: { employeeId: "$_id" },
pipeline: [
{
$match: {
$and: [
{ $expr: { $eq: ["$employeeId", "$$employeeId"] } },
{ isApproved: true },
{
createdAt: {
$gte: startOfMonth.toDate(),
$lte: endOfMonth.toDate(),
},
},
],
},
},
],
as: "attendance",
},
},
{
$project: {
_id: 1,
username: 1,
name: 1,
attendance: 1,
present: { $sum: { $eq: ["$attendance.status", "present"] } },
},
},
]);
But why cannot count my column?
i use $eq, with $sum then count the result. but the result is 0
{
"username": "Ethyl",
"name": "Kuhn",
"id": "614d43cde735f3e601dea165",
"attendance": [
{
"_id": "614d43cde735f3e601dea16f",
"status": "present",
"start": "2021-09-24T03:19:41.645Z",
"employeeId": "614d43cde735f3e601dea165",
"isApproved": true
},
],
"present": 0,
"sick": 0,
"off": 0,
},

MongoDB match filters with grouping and get total count

My sample data:
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
{
"_id": "random_id_2",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
{
"_id": "random_id_3",
"priority": "P2",
"owners": ["user-1", "user-2"],
},
I want to run an aggregation pipeline on the data involving match filters and grouping, also I want to limit the number of groups returned as well as the number of items in each group.
Essentially, if limit=2, limit_per_group=1, group_by=owner, priority=P1, I want the following results:
[
{
"data": [
{
"group_key": "user-1",
"total_items_in_group": 2,
"limited_items": [
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
],
},
{
"group_key": "user-2",
"total_items_in_group": 2,
"limited_items": [
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
],
},
]
},
{
"metadata": {
"total_items_matched": 2,
"total_groups": 2
}
},
]
Need some help on how to write an aggregation pipeline to get the required result.
My current query is as follows:
{
"$match": {
"priority": "P1"
}
},
{
"$facet": {
"data": [
{
$addFields: {
"group_by_owners": "$owners"
}
},
{
$unwind: "$group_by_owners"
},
{
$group: {
"_id": "$group_by_owners",
"total_items_in_group": {
$sum: 1
},
"items": {
$push: "$$ROOT"
}
}
},
{
$sort: {
"total": -1
}
},
{
$unset: "items.group_by_owners"
},
{
$project: {
"_id": 1,
"total_items_in_group": 1,
"limited_items": {
$slice: [
"$items",
1
]
}
}
},
{
"$limit": 2
}
],
"metadata": [
{
$count: "total_items_matched"
}
]
}
}
Mongo playground link
I am unable to calculate the total number of groups.
add new stage of $addfields at the end of pipeline
db.collection.aggregate([
{
"$match": {
"priority": "P1"
}
},
{
"$facet": {
"data": [
{
$addFields: {
"group_by_owners": "$owners"
}
},
{
$unwind: "$group_by_owners"
},
{
$group: {
"_id": "$group_by_owners",
"total_items_in_group": {
$sum: 1
},
"items": {
$push: "$$ROOT"
}
}
},
{
$sort: {
"total": -1
}
},
{
$unset: "items.group_by_owners"
},
{
$project: {
"_id": 0,
"group_key": "$_id",
"total_items_in_group": 1,
"limited_items": {
$slice: [
"$items",
1
]
}
}
},
{
"$limit": 2
}
],
"metadata": [
{
$count: "total_items_matched",
}
]
}
},
{
"$addFields": {
"metadata.total_groups": {
"$size": "$data"
}
}
}
])
https://mongoplayground.net/p/y5a0jvr6fxI

How to use `$unwind` to iterate exact value

I am new to mongoose and mongodb.
In my addtocart schema I have added $lookups and projection to populate products in add to cart.
In current response product_purchase_quantity it was in array in add to cart collection it is key and value. So, in $lookups I tried to add {$unwind: '$product_purchase_quantity'} but after adding add_to_cart_products object prints twice. I don't know where the mistake was?
Below i have mentioned expected result.
AddToCart Schema:
lookups: [
{
from: 'shop_db_products',
let: {
productId: '$add_to_cart_products.product',
purchaseQuantity: '$add_to_cart_products.product_quantity',
productItemId: '$add_to_cart_products.product_item',
},
pipeline: [
{
$match: { $expr: { $in: ['$_id', '$$productId'] } },
},
{
$lookup: {
from: 'shop_db_products',
localField: 'product_id',
foreignField: '_id',
as: 'products',
},
},
{
$project: {
_id: true,
product: {
_id: '$_id',
product_name: '$product_name',
},
product_purchase_quantity: '$$purchaseQuantity',
product_item: {
$reduce: {
input: {
$filter: {
input: '$product_items',
cond: {
$in: ['$$this._id', '$$productItemId'],
},
},
},
initialValue: {},
in: {
_id: '$$this._id',
product_size: { $concat: [{ $toString: '$$this.product_size.value' }, '$$this.product_size.unit'] },
product_price: '$$this.product_price',
product_type: '$$this.product_type'
},
},
},
},
},
],
localField: '',
as: 'add_to_cart_products',
model: 'ProductModel',
},
],
AddToCart Collection
[
{
"add_to_cart_user": "5f0076b7bd530928fc0c0285",
"add_to_cart_products": [
{
"product": "5f05a0270b4f3a5c41c70826",
"product_item": "5f05a0270b4f3a5c41c70877",
"product_quantity": 5
},
{
"product": "5f05a0270b4f3a5c41c70827",
"product_item": "5f05a0270b4f3a5c41c70666",
"product_quantity": 3
}
],
"add_to_cart_product_total": 5,
"add_to_cart_discount": 50,
"add_to_tax": "5eae321d21924800122f978e",
"add_to_cart_grand_total": 500
}
]
Current Response:
{
"_id": "5fa2a09b3c6316482098f6ff",
"add_to_cart_status_is_active": true,
"add_to_cart_discount": 50,
"add_to_cart_tax": 8,
"add_to_cart_products": [
{
"product": {
"_id": "5f05a0270b4f3a5c41c70826",
"product_name": "Avery Apricot Sour"
},
"product_purchase_quantity": [
5,
3
],
"product_item": {
"_id": "5f05a0270b4f3a5c41c70877",
"product_size": "22oz",
"product_price": 13.99
}
},
{
"product": {
"_id": "5f05a0270b4f3a5c41c70827",
"product_name": "Avery Dugana"
},
"product_purchase_quantity": [
5,
3
],
"product_item": {
"_id": "5f05a0270b4f3a5c41c70666",
"product_size": "22oz",
"product_price": 8.99
}
}
]
}
Expected response:
{
"_id": "5fa2a09b3c6316482098f6ff",
"add_to_cart_status_is_active": true,
"add_to_cart_discount": 50,
"add_to_cart_tax": 8,
"add_to_cart_products": [
{
"product": {
"_id": "5f05a0270b4f3a5c41c70826",
"product_name": "Avery Apricot Sour"
},
"product_purchase_quantity": 5,
"product_item": {
"_id": "5f05a0270b4f3a5c41c70877",
"product_size": "22oz",
"product_price": 13.99
}
},
{
"product": {
"_id": "5f05a0270b4f3a5c41c70827",
"product_name": "Avery Dugana"
},
"product_purchase_quantity": 3,
"product_item": {
"_id": "5f05a0270b4f3a5c41c70666",
"product_size": "22oz",
"product_price": 8.99
}
}
]
}
Product (shop_db_products) collection:
[
{
"_id": "5f05a0270b4f3a5c41c70826",
"product_no": "PRO04087",
"product_store_no": "1001",
"product_dept_no": "Irish Whiskey",
"product_name": "2Gingers",
"product_overview": "No Overview",
"product_items": [
{
"_id": "5f05a0270b4f3a5c41c70877",
"product_item_number": "857566003019",
"product_price": 20.99,
"product_cost": 20.99,
"product_size": "750ml",
"product_type": "Bottle",
"product_value": 0,
"product_quantity": 0,
"product_images": [
"pro04087-857566003019-1.png"
],
"product_item_is_active": true
}
]
},
...
]
You have to $unwind add_to_cart_products before performing $lookup
db.addToCart.aggregate([
{
$unwind: "$add_to_cart_products"
},
{
$lookup: {
from: "shop_db_products",
let: {
productId: "$add_to_cart_products.product",
purchaseQuantity: "$add_to_cart_products.product_quantity",
productItemId: "$add_to_cart_products.product_item"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$_id",
"$$productId"
]
}
}
},
{
$project: {
_id: true,
product: {
_id: "$_id",
product_name: "$product_name",
},
product_purchase_quantity: "$$purchaseQuantity",
product_item: {
$reduce: {
input: {
$filter: {
input: "$product_items",
cond: {
$eq: [
"$$this._id",
"$$productItemId"
],
},
},
},
initialValue: {},
in: {
_id: "$$this._id",
product_size: "$$this.product_size",
product_price: "$$this.product_price",
product_type: "$$this.product_type"
}
}
}
}
}
],
as: "add_to_cart_products"
}
},
{
$unwind: "$add_to_cart_products"
},
{
$group: {
_id: "$_id",
"add_to_cart_products": {
$push: "$add_to_cart_products"
}
}
}
])
MongoDB Playground
Another way
Without reducer, with $unwind
https://mongoplayground.net/p/3uWA5pVBv83
db.addToCart.aggregate([
{
"$unwind": "$add_to_cart_products"
},
{
"$lookup": {
"from": "shop_db_products",
"let": {
"productId": "$add_to_cart_products.product",
"purchaseQuantity": "$add_to_cart_products.product_quantity",
"productItemId": "$add_to_cart_products.product_item"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$productId"
]
}
}
},
{
"$unwind": "$product_items"
},
{
"$match": {
"$expr": {
"$eq": [
"$product_items._id",
"$$productItemId"
]
}
}
},
{
"$project": {
"_id": true,
"product": {
"_id": "$_id",
"product_name": "$product_name"
},
"product_purchase_quantity": "$$purchaseQuantity",
"product_item": "$product_items"
}
}
],
"as": "productResolved"
}
},
{
"$unwind": "$productResolved"
},
{
"$group": {
"_id": "$_id",
"add_to_cart_products": {
"$push": "$productResolved"
}
}
}
])

Mongo Query to fetch distinct nested documents

I need to fetch distinct nested documents.
Please find the sample document:
{
"propertyId": 1001820437,
"date": ISODate("2020-07-17T00:00:00.000Z"),
"HList":[
{
"productId": 123,
"name": "Dubai",
"tsh": true
}
],
"PList":[
{
"productId": 123,
"name": "Dubai",
"tsh": false
},
{
"productId": 234,
"name": "India",
"tsh": true
}
],
"CList":[
{
"productId": 234,
"name": "India",
"tsh": false
}
]
}
Expected result is:
{
"produts":[
{
"productId": 123,
"name": "Dubai"
},
{
"productId": 234,
"name": "India"
}
]
}
I tried with this query:
db.property.aggregate([
{
$match: {
"propertyId": 1001820437,
"date": ISODate("2020-07-17T00:00:00.000Z")
}
},
{
"$project": {
"_id": 0,
"unique": {
"$filter": {
"input": {
"$setDifference": [
{
"$concatArrays": [
"$HList.productId",
"$PList.productId",
"$CList.productId"
]
},
[]
]
},
"cond": {
"$ne": [ "$$this", "" ]
}
}
}
}
}
]);
Is $setDifference aggregation is correct choice here?
My query returns only unique product ids but i need a productId with name.
Could someone help me to solve this?
Thanks in advance
You can use $projectfirst to get rid of tsh field and then run $setUnion which ignores duplicated entries:
db.collection.aggregate([
{
$project: {
"HList.tsh": 0,
"PList.tsh": 0,
"CList.tsh": 0,
}
},
{
$project: {
products: {
$setUnion: [ "$HList", "$PList", "$CList" ]
}
}
}
])
Mongo Playground
The following two aggregations return the expected and same result (you can use any of the two):
db.collection.aggregate( [
{
$project: {
_id: 0,
products: {
$reduce: {
input: { $setUnion: [ "$HList", "$PList", "$CList" ] },
initialValue: [],
in: {
$setUnion: [ "$$value", [ { productId: "$$this.productId", name: "$$this.name" } ] ]
}
}
}
}
}
] )
This one is little verbose:
db.collection.aggregate( [
{
$project: { list: { $setUnion: [ "$HList", "$PList", "$CList" ] } }
},
{
$unwind: "$list"
},
{
$group: {
_id: null,
products: { $addToSet: { "productId": "$list.productId", "name": "$list.name" } }
}
},
{
$project: { _id: 0 }
}
] )
db.collection.aggregate([
{
$match: {
"propertyId": 1001820437,
"date": ISODate("2020-07-17T00:00:00.000Z")
}
},
{
$project: {
products: {
$filter: {
input: { "$setUnion" : ["$CList", "$HList", "$PList"] },
as: 'product',
cond: {}
}
}
}
},
{
$project: {
"_id":0,
"products.tsh": 1,
"products.name": 1,
}
},
])

Get objects containing max values for multiple fields using aggregation in mongodb

I want to fetch the documents having highest value for a list of specifics fields. I don't know if it's possible in only one request.
Consider below data:
_id:1, kills:12, deaths:6, assists:1
_id:2, kills:2, deaths:2, assists:22
_id:3, kills:1, deaths:2, assists:3
_id:4, kills:0, deaths:23, assists:4
_id:5, kills:6, deaths:3, assists:5
_id:6, kills:7, deaths:1, assists:6
Answer should be something like
maxKills: { _id:1, kills:12, deaths:6, assists:1 },
maxDeaths: { _id:4, kills:0, deaths:23, assists:4 },
maxAssists: { _id:2, kills:2, deaths:2, assists:22 },
I have tried several queries, but I can't get the whole objects containing the max values.
db.coll.aggregate([{
$group: {
_id: null,
kills: { $max: "$stats.kills" },
deaths: { $max: "$stats.deaths" },
assists: { $max: "$stats.assists" },
}
}])
For example here I have all the max values I want but I don't get the whole matches Objects.
---- UPDATE ----
With this answer https://stackoverflow.com/a/33361913/9188650, I've made it works but I receive data in a not really user friendly way.
{
"$group": {
"_id": null,
"maxKills": { "$max": "$stats.kills" },
"maxDeaths": { "$max": "$stats.deaths" },
"maxAssists": { "$max": "$stats.assists" },
"matches": {
"$push": {
"champion": "$champion",
"gameId": "$gameId",
"kills": "$stats.kills",
"deaths": "$stats.deaths",
"assists": "$stats.assists",
}
}
}
},
{
"$project": {
"_id": 0,
"maxKills": 1,
"maxDeaths": 1,
"maxAssists": 1,
"matches": {
"$setDifference": [
{
"$map": {
"input": "$matches",
"as": "match",
"in": {
$switch: {
branches: [
{ case: { $eq: ["$maxKills", "$$match.kills"] }, then: "$$match" },
{ case: { $eq: ["$maxDeaths", "$$match.deaths"] }, then: "$$match" },
{ case: { $eq: ["$maxAssists", "$$match.assists"] }, then: "$$match" },
],
default: false
}
}
}
},
[false]
]
}
}
}
It will returns:
{
"maxKills": 25,
"maxDeaths": 20,
"maxAssists": 39,
"matches": [
{
"champion": {
"id": 145,
"name": "Kai'Sa",
},
"gameId": 4263819967,
"kills": 25,
"deaths": 3,
"assists": 16
},
{
"champion": {
"id": 8,
"name": "Vladimir",
},
"gameId": 4262731529,
"kills": 8,
"deaths": 20,
"assists": 3
},
{
"champion": {
"id": 22,
"name": "Ashe",
},
"gameId": 4340383097,
"kills": 9,
"deaths": 7,
"assists": 39
},
{
"champion": {
"id": 23,
"name": "Tryndamere",
},
"gameId": 4352236936,
"kills": 25,
"deaths": 6,
"assists": 22
}
]
}
My last issue are cases when multiple objects have the same max value (as the example above, 2 matches have 25 kills). I only want the oldest one in these cases.
You can do it easier by using $filter and $arrayElemAt after $group stage:
db.collection.aggregate([
{
$group: {
_id: null,
maxKills: { $max: "$kills" },
maxDeaths: { $max: "$deaths" },
maxAssists: { $max: "$assists" },
docs: { $push: "$$ROOT" }
}
},
{
$project: {
_id: 0,
maxKills: { $arrayElemAt: [ { $filter: { input: "$docs", cond: { $eq: [ "$$this.kills", "$maxKills" ] } } }, 0 ] },
maxDeaths: { $arrayElemAt: [ { $filter: { input: "$docs", cond: { $eq: [ "$$this.deaths", "$maxDeaths" ] } } }, 0 ] },
maxAssists: { $arrayElemAt: [ { $filter: { input: "$docs", cond: { $eq: [ "$$this.assists", "$maxAssists" ] } } }, 0 ] }
}
}
])
Mongo Playground