$addFields is not adding value in document - mongodb

Query is as follows and result is given below:
What I want is I am adding field called name, in which I want categoryObj[0].categoryName but it is empty.
Tried categoryObj.$.categoryName but giving error.
Once name is obtained as I want i will exclude categoryObj with project opertator.
Thanks for help in advance
let itemsByCategory = await VendorItem.aggregate([
{$match: {vendor: vendorId}},
{$lookup: {
from: "vendorcategories",
localField: "category",
foreignField: "_id",
as: 'categoryDetails'
}},
{$group:{
"_id":"$category",
"count":{"$sum":1},
"items":{"$push":"$$ROOT"},
"categoryObj":{"$addToSet":"$categoryDetails"}
}},
{$project: {"items.categoryDetails":0}},
{$addFields: {"categoryName" : "$categoryObj.categoryName"}},
//{$project: {"categoryObj":0}},
]);
and the result is as follows
{
"itemsByCategory": [
{
"_id": "62296d612a1462a7d5e4b86b",
"count": 1,
"menuItems": [
{
"_id": "622971fa4fda7b4c792a7812",
"category": "62296d612a1462a7d5e4b86b",
"vendor": "62296c6f2a1462a7d5e4b863",
"item": "Dahi Chaat",
"price": 30,
"inStock": true,
"variants": [
{
"variantName": "With Sev",
"variantPrice": 40,
"_id": "622975b9f7bdf6c2a3b7703c"
}
],
"toppings": [
{
"name": "cheese",
"price": 10,
"inStock": true,
"_id": "62297766ff9f01d236c60736"
}
],
"categoryDetails": [
{
"_id": "62296d612a1462a7d5e4b86b",
"categoryName": "Snacks",
"categoryDescription": "Desciption changed!",
"vendor": "621c6c944d6d79e83219e59a",
"__v": 0
}
]
}
],
"categoryObj": [
[
{
"_id": "62296d612a1462a7d5e4b86b",
"categoryName": "Snacks",
"categoryDescription": "Desciption changed!",
"vendor": "621c6c944d6d79e83219e59a",
"__v": 0
}
]
],
"name": []
}
]
}

You can add an $unwind phase in order to "loop" all objects inside "categoryObj", but you will need to group it back afterwards:
{"$addFields": {orig_id: "$_id"}},
{"$unwind": "$categoryObj"},
{"$addFields": {"name": {"$arrayElemAt": ["$categoryObj", 0]}}},
{"$group": {_id: "$orig_id", name: {$push: "$name.categoryName"},
menuItems: {$first: "$menuItems"}, count: {$first: "count"},
}
}
See playground here:
https://mongoplayground.net/p/wsH2Y0UZ_FH

Related

Mongodb Aggregations - Group by date including condition

I have a series of documents gathered by aggregation grouping. This is the result for one document:
{
"_id": {
"ip": "79.xxx.xxx.117",
"myDate": "2022-10-19"
},
"date": "2022-10-19",
"allVisitedPages": [
{
"page": "/",
"time": {
"time": "2022-10-19T11:35:44.655Z",
"tz": "-120",
"_id": "634fe1100a011986b7137da0"
}
},
{
"page": "/2",
"time": {
"time": "2022-10-19T12:14:29.536Z",
"tz": "-120",
"_id": "634fea257acb264f23d421f1"
}
},
{
"page": "/",
"time": {
"time": "2022-10-19T15:37:30.002Z",
"tz": "-120",
"_id": "634fea266001ea364eeb38ea"
}
},
],
"visitedPages": 3,
"createdAt": "2022-10-19T11:35:44.920Z"
},
I want to get this (in this case 2 documents as the time difference between array position 2 and 3 is greater than 2 hours):
{
"_id": {
"ip": "79.xxx.xxx.117",
"myDate": "2022-10-19"
},
"date": "2022-10-19",
"allVisitedPages": [
{
"page": "/",
"durationInMinutes": "39",
"time": {
"time": "2022-10-19T11:35:44.655Z",
"tz": "-120",
"_id": "634fe1100a011986b7137da0"
}
},
{
"page": "/2",
"durationInMinutes": "2",
"time": {
"time": "2022-10-19T12:14:29.536Z",
"tz": "-120",
"_id": "634fea257acb264f23d421f1"
}
}
],
"visitedPages": 2,
},
{
"_id": {
"ip": "79.xxx.xxx.117",
"myDate": "2022-10-19"
},
"date": "2022-10-19",
"allVisitedPages": [
{
"page": "/",
"durationInMinutes": "2",
"time": {
"time": "2022-10-19T15:37:30.002Z",
"tz": "-120",
"_id": "634fea266001ea364eeb38ea"
}
},
],
"visitedPages": 1,
},
I want to get a new grouping document if the time between an array position and the following array position is greater than 2 hours. On the last array position it show always show "2".
I tried $divide and $datediff. But this is not possible on the group stage as it's an unary operator. An approach I tried is to calculate the sum of start and end time by dividing. But how to execute this on an array level on the group stage? Maybe someone could point me in the right direction if possible at all?
You can group and then reduce, but another option is to use $setWindowFields to calculate your grouping index before grouping:
db.collection.aggregate([
{$setWindowFields: {
partitionBy: {$concat: ["$ip", "$date"]},
sortBy: {"time.time": 1},
output: {prevtime: {
$push: "$time.time",
window: {documents: [-1, "current"]}
}}
}},
{$addFields: {
minutesDiff: {
$toInt: {
$dateDiff: {
startDate: {$first: "$prevtime"},
endDate: {$last: "$prevtime"},
unit: "minute"
}
}
}
}},
{$addFields: {deltaIndex: {$cond: [{$gt: ["$minutesDiff", 120]}, 1, 0]}}},
{$setWindowFields: {
partitionBy: {$concat: ["$ip", "$date"]},
sortBy: {"time.time": 1},
output: {
groupIndex: {
$sum: "$deltaIndex",
window: {documents: ["unbounded", "current"]}
},
duration: {
$push: "$minutesDiff",
window: {documents: ["current", 1]}
}
}
}
},
{$set: {
duration: {
$cond: [
{$and: [
{$eq: [{$size: "$duration"}, 2]},
{$lte: [{$last: "$duration"}, 120]}
]},
{$last: "$duration"},
2
]
}
}},
{$group: {
_id: {ip: "$ip", myDate: "$date", groupIndex: "$groupIndex"},
date: {$first: "$date"},
allVisitedPages: {$push: {page: "$page", time: "$time", duration: "$duration"}},
visitedPages: {$sum: 1}
}},
{$unset: "_id.groupIndex"}
])
See how it works on the playground example

I want to aggregate data array inside another array in mongodb

I want to aggregate MongoDB documents which is having arrays inside of an array. my document was like the below.
{
"_id": "6257e31d11a9d5231c05c084",
"name": "Test Name 1",
"phone": "1234567891",
"visits": [
{
"_id": "6257e31d11a9d5231c05c069",
"date": "2-7-2021",
"samples": [
"6257f8855197613b641d494e",
....
],
"products_detailed": [
"5d725cd2c4ded7bcb480eab2",
.....
]
},
...........
]
}
and I want to get the output line below
{
"_id": "6257e31d11a9d5231c05c084",
"name": "Test Name 1",
"phone": "1234567891",
"visits": [
{
"_id": "6257e31d11a9d5231c05c069",
"date": "2-7-2021",
"samples": [
{
"_id": "6257f8855197613b641d494e",
"product_name": "Samor",
"price": 250
},
........
],
"products_detailed": [
{
"_id": "5d725cd2c4ded7bcb480eab2",
"product_name": "Pahad",
"price": 100
},
............
]
},
.........................
]
}
how can I get like this? I tried to use $lookup & group to get the output, but I am not getting the output as required me.
Since you have a list of visits on each document, one way to go is to $unwind and then $group at the end, like this:
db.Main.aggregate([
{
$unwind: "$visits"
},
{
"$lookup": {
"from": "Samples",
"localField": "visits.samples",
"foreignField": "_id",
"as": "samples"
}
},
{
"$lookup": {
"from": "Product Detailed",
"localField": "visits.products_detailed",
"foreignField": "_id",
"as": "products_detailed"
}
},
{
$project: {
name: 1,
phone: 1,
"visits._id": 1,
"visits.date": 1,
"visits.products_detailed": "$products_detailed",
"visits.samples": "$samples"
}
},
{
$group: {
_id: 0,
name: {$first: "$name"},
visits: {$push: "$visits"}
}
}
])
As you can see on the playground, on your data sample it will return:
[
{
"_id": 0,
"name": "Test Name 1",
"visits": [
{
"_id": "6257e31d11a9d5231c05c069",
"date": "2-7-2021",
"products_detailed": [
{
"_id": "5d725cd2c4ded7bcb480eab2",
"price": 100,
"product_name": "Pahad"
}
],
"samples": [
{
"_id": "6257f8855197613b641d494e",
"price": 250,
"product_name": "Samor"
}
]
}
]
}
]

MySQL update with Join to Mongo Aggregate with Lookup

I'm migrating from MySQL to MongoDB. In this process I want to rewrite the following MySQL query:
This query is used to add additional information to the minutes table (adding the program, title, broadcaster and program_id (prid) to the other table)
UPDATE etl_staging.sko_minutes minut
JOIN etl_staging.sko_daily AS daily ON
minut.channel = daily.channel AND
(minut.C BETWEEN daily.start_datetime and daily.end_datetime)
set minut.sko_frequency = daily.sko_frequency,
minut.programma = daily.programma,
minut.titel = daily.titel,
minut.omroep = daily.omroep,
minut.prid = daily.prid
WHERE daily.doelgroep = '6+'
AND daily.universe = 'Currency'
Now i'm trying to rewrite it to the following... Only i found out that i could not make a join in MongoDB. Was looking at something like this. But that returns an empty minutes array as field to the minutes document.
db.sko_minutes.aggregate(
[
{ $lookup: {
from: "sko_daily",
let: { channel: "$channel", start_datetime: "$start_datetime", end_datetime: "$end_datetime",
doelgroep: "$doelgroep", universe: "$universe"},
pipeline: [
{ $match: {
$expr: {
$and: [
{ $eq: ["$$universe", 'Currency']},
{ $eq: ["$$doelgroep", '6+']},
{ $eq: ["$channel", "$$channel"]},
{ $gte: ["$datetime", "$$start_datetime"]},
{ $lte: ["$datetime", "$$end_datetime"]}
]
}
}}
],
as: 'minutes'
}}
]
)
Does anybody have an idea what would be the best approach for this problem?
The documents of the sko_minutes looks like this:
{
"_id": {"$oid": "5fbb8b85336e42949248fb1b"},
"abs": 0,
"channel": "vicetv",
"date": {"$date": "2020-11-22T00:00:00.000Z"},
"datetime": {"$date": "2020-11-22T09:20:00.000Z"},
"hour": 9,
"kta": 0,
"minutes": "20",
"start_time": "09:20:00"
},
And the daily document looks like this:
{
"_id": {"$oid": "5fbb8afb4cab8a4ce5acac5f"},
"abs": 0,
"brk": 0,
"channel": "net5",
"date": "2020-11-18",
"doelgroep": "20-34",
"duration": "47",
"end_date": "2020-11-18",
"end_datetime": {"$date": "2020-11-18T01:12:00.000Z"},
"end_hour": "1",
"end_minutes": "12",
"end_time": "25:12:59",
"hour": "0",
"kdh": 0,
"kta": 0,
"minutes": "12",
"omroep": "net5",
"prid": "694949124",
"programma": "vtwonenverbouwenofverhuizen",
"sko_frequency": "overige herhaling",
"start_datetime": {"$date": "2020-11-18T00:12:00.000Z"},
"start_time": "24:12:00",
"titel": "Vtwonen verbouwen of verhuizen",
"universe": "UGK1-6",
"waardering": 0,
"webtv_gaas": 0,
"webtv_nstreams": 0
},
Brings part of sko_daily to sko_minutes
let fields are from sko_minutes: date, datetime, channel
this fields are matched to daily
You also require a subset of sko_daily, using filters.
Mind you that I tweaked the date fields, and some data, just to make mongoplayground work.
Playground
db.sko_minutes.aggregate([
{
$lookup: {
/**$$ are from sko_minutes*/
from: "sko_daily",
let: {
"start_datetime": "$date",
"end_datetime": "$datetime",
channel: "$channel"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$universe",
"Currency"
]
},
{
$eq: [
"$doelgroep",
"6+"
]
},
{
$eq: [
"$$channel",
"$channel"
]
},
{
$gte: [
"$$start_datetime",
"$start_datetime"
]
},
{
$lte: [
"$$end_datetime",
"$end_datetime"
]
}
]
}
}
}
],
as: "minutes"
}
}
])

how to transform data using aggregate function mongodb

How to transform data base on parent_id within self join ? Is this possible make the result as expected. Please help on this thanks
db={
post: [
{
"_id": ObjectId("59f9c5629f75813e21a6fe34"),
"parent_id": "0",
"name": "main_category",
"short_desc": "",
"long_desc": "",
"slug": "main_category",
"status": true,
"createdAt": ISODate("2017-11-01T13:00:18.714Z"),
"updatedAt": ISODate("2019-02-19T07:31:20.967Z")
},
{
"_id": ObjectId("59f9c5629f75813e21a6fe73"),
"parent_id": "59f9c5629f75813e21a6fe34",
"name": "sub_category",
"short_desc": "",
"long_desc": "",
"slug": "sub_category",
"status": true,
"createdAt": ISODate("2017-11-01T13:00:18.714Z"),
"updatedAt": ISODate("2019-02-19T07:31:20.967Z")
},
{
"_id": ObjectId("59f9c5629f75813e21a6fe33"),
"parent_id": "59f9c5629f75813e21a6fe73",
"name": "sub_category1",
"short_desc": "",
"long_desc": "",
"slug": "sub_category1",
"status": true,
"createdAt": ISODate("2017-11-01T13:00:18.714Z"),
"updatedAt": ISODate("2019-02-19T07:31:20.967Z")
}
]
}
output should like this. If any more category does not belongs to anything it should stay blank
[
{
mainCategory: 'main_category',
subCategory1: 'sub_category',
subCategory2: 'sub_category1',
subCategory3: '',
subCategory4: '',
subCategory5: ''
}, {
mainCategory: '{if any}',
subCategory1: '{if any}',
subCategory2: '{if any}',
subCategory3: '',
subCategory4: '',
subCategory5: ''
}
];
Any hope to get this stat. ?
$graphLookup reads from the collection specified by its from argument, not from the documents in the pipeline.
In the pipeline you created to change the datatype, use a $merge stage to update the existing documents:
db.post.aggregate([
{$addFields: {
parent_oid: {
$cond: {
if: {$eq: ["$parent_id","0"]},
then: "$parent_id",
else: {$toObjectId: "$parent_id"}
}
}
}
},
{$merge: "post"}
])
Then you can use $graphLookup to form the lists, and transform them to the shape you need:
db.post.aggregate([
{$match: {parent_id: "0" }},
{"$graphLookup": {
"from": "post",
"startWith": "$_id",
"connectFromField": "_id",
"connectToField": "parent_oid",
"as": "response"
}},
{$unwind: "$response"},
{$group: {
_id: "$_id",
main_category: {$first: "$slug"},
subCategories: {$push: {
k: "$response.name",
v: "$response.slug"
}}
}
},
{$replaceRoot: {
newRoot: {
$mergeObjects: [
{mainCategory: "$main_category"},
{$arrayToObject: "$subCategories"}
]
}
}}
])
Output from the sample data:
[
{
"mainCategory": "main_category",
"sub_category": "sub_category",
"sub_category1": "sub_category1"
}
]
Playground

Mongoose aggregate and group by two fields

I want to aggregate a table to lookup in other 3 tables, then make a nested group by.
I have 4 models
Order
Order_Batches which have a ref to order
Batch which have a ref to Order_Batch
Event which have a ref to Batch
so i'm selecting all orders then getting all it's orderbatches then find all batch and get all events done on that batch
Code
let order=await Order.aggregate([
{$lookup:{from:'orderbatches',localField:'_id',foreignField:'order',as:"order_batches"}},
{$unwind: {path: "$order_batches", preserveNullAndEmptyArrays: true}},
{$lookup:{from:'batches',localField:'order_batches._id',foreignField:'orderBatches',as:"batches"}},
{$unwind: {path: "$batches", preserveNullAndEmptyArrays: true}},
{$lookup:{from:'events',localField:'batches._id',foreignField:'batch',as:"events"}},
{$group: {
_id: "$_id",
code: {$first: "$code"},
order_batches: {$push: {
batches: "$batches",
events:"$events"
}}
}},
]);
Output
{
"success": true,
"orders": [
{
"_id": "5a5cbdd91ecaff0f8417a10d",
"code": "0",
"order_batches": [
{
"batches": {
"_id": "5a5cbdd91ecaff0f8417a114",
"updatedAt": "2018-01-15T14:42:33.585Z",
"createdAt": "2018-01-15T14:42:33.585Z",
"number": 1,
"quantity": 10,
"orderBatches": "5a5cbdd91ecaff0f8417a10e",
"removed": false,
"__v": 0,
}
"events": []
},
{
"batches": {
"_id": "5a5cbdd91ecaff0f8417a116",
"updatedAt": "2018-01-15T14:42:33.586Z",
"createdAt": "2018-01-15T14:42:33.586Z",
"number": 2,
"quantity": 10,
"orderBatches": "5a5cbdd91ecaff0f8417a10e",
"removed": false,
"__v": 0,
}
"events": [
{
"_id": "5a5cbdd91ecaff0f8417a117",
"updatedAt": "2018-01-15T14:42:33.587Z",
"createdAt": "2018-01-15T14:42:33.587Z",
"batch": "5a5cbdd91ecaff0f8417a116",
"process": [
"5a5cbdd91ecaff0f8417a115"
],
"removed": false,
"__v": 0
}
]
}
]
}
]
}
Expected Output
{
"success": true,
"orders": [
{
"_id": "5a5cbdd91ecaff0f8417a10d",
"code": "0",
"order_batches": [
{
"batches": {
"_id": "5a5cbdd91ecaff0f8417a114",
"updatedAt": "2018-01-15T14:42:33.585Z",
"createdAt": "2018-01-15T14:42:33.585Z",
"number": 1,
"quantity": 10,
"orderBatches": "5a5cbdd91ecaff0f8417a10e",
"removed": false,
"__v": 0,
"events": []
}
},
{
"batches": {
"_id": "5a5cbdd91ecaff0f8417a116",
"updatedAt": "2018-01-15T14:42:33.586Z",
"createdAt": "2018-01-15T14:42:33.586Z",
"number": 2,
"quantity": 10,
"orderBatches": "5a5cbdd91ecaff0f8417a10e",
"removed": false,
"__v": 0,
"events": [
{
"_id": "5a5cbdd91ecaff0f8417a117",
"updatedAt": "2018-01-15T14:42:33.587Z",
"createdAt": "2018-01-15T14:42:33.587Z",
"batch": "5a5cbdd91ecaff0f8417a116",
"process": [
"5a5cbdd91ecaff0f8417a115"
],
"removed": false,
"__v": 0
}
]
}
}
]
}
]
}
I've done this but also not working
let order=await Order.aggregate([
{$lookup:{from:'orderbatches',localField:'_id',foreignField:'order',as:"order_batches"}},
{$unwind: {path: "$order_batches", preserveNullAndEmptyArrays: true}},
{$lookup:{from:'batches',localField:'order_batches._id',foreignField:'orderBatches',as:"order_batches.batches"}},
{$unwind: {path: "$order_batches.batches", preserveNullAndEmptyArrays: true}},
{$lookup:{from:'events',localField:'order_batches.batches._id',foreignField:'batch',as:"order_batches.batches.events"}},
{$group: {
_id: "$order_batches._id",
// code: {$first: "$code"},
"batches": {
"$push": "$order_batches.batches"
}
}},
{$group: {
_id: "$_id",
code: {$first: "$code"},
"order_batches": {
"$push": "$order_batches"
}
}},
]);
I got it to work
let order=await Order.aggregate([
{$lookup:{from:'orderbatches',localField:'_id',foreignField:'order',as:"order_batches"}},
{$lookup:{from:'styles',localField:'style',foreignField:'parent',as:"style"}},
{$lookup:{from:'processes',localField:'style._id',foreignField:'style',as:"processes"}},
{$unwind: {path: "$order_batches", preserveNullAndEmptyArrays: true}},
{$lookup:{from:'batches',localField:'order_batches._id',foreignField:'orderBatches',as:"order_batches.batches"}},
{$unwind: {path: "$order_batches.batches", preserveNullAndEmptyArrays: true}},
{$lookup:{from:'events',localField:'order_batches.batches._id',foreignField:'batch',as:"order_batches.batches.events"}},
{$group: {
_id: "$order_batches._id",
order_id: {$first: "$_id"},
code:{$first:"$code"},
processes:{$first:"$processes"},
style:{$first:"$style"},
"batches": {
"$push": "$order_batches.batches"
}
}},
{$group: {
_id: "$order_id",
"code":{$first:"$code"},
"style":{$first:"$style"},
processes:{$first:"$processes"},
"order_batches": {
"$push": "$batches"
}
}},
]);