MongoDB Aggregation With $lookup for field inside an array - mongodb

Ive been trying to make a MongoDB query with aggregation to produce a data in a structure I like. So i have to collections namely People and Calendar
People
_id : ObjectId,
name: string,
age: number,
occupation: string
address
Calendar
_id: ObjectId,
months: [
{
month: Date,
users: [
{
PersonId: ObjectId(Reference from People._id)
...some other details
},
{
PersonId: ObjectId(Reference from People._id)
...some other details
},
{
PersonId: ObjectId(Reference from People._id)
...some other details
},
]
},
{
month: Date,
users: [same as above]
},
{
month: Date,
users: [same as above]
},
]
So, what I would like the final data to look is like below.
Final Result
Calendar
_id: ObjectId,
months: [
{
month: Date,
users: [
{
PersonId: ObjectId(Reference from People._id)
Person : data from Person Collection
...some other details
},
{
PersonId: ObjectId(Reference from People._id)
Person : data from Person Collection
...some other details
},
{
PersonId: ObjectId(Reference from People._id)
Person : data from Person Collection
...some other details
},
]
},
{
month: Date,
users: [same as above]
},
{
month: Date,
users: [same as above]
},
]
I have been trying with below code but could not get it right.
.aggregate([
{$unwind: "$months"},
{$unwind: "$months.users"},
{
$lookup:
{
from: "Person",
localField: "months.users.PersonId",
foreignField: "_id",
as: "PersonList"
}
},
{$unwind: "$PersonList"},
{
$group:
{
"_id": "$_id",
}
}
])
How do I achieve this?
Is this something do-able?

So you can do something like this,
db.calender.aggregate([
{
$unwind: "$months"
},
{
$unwind: "$months.users"
},
{
$lookup: {
from: "people",
localField: "months.users.personId",
foreignField: "_id",
as: "months.users.people"
}
},
{
$group: {
"_id": {
_id: "$_id",
months: "$months.month"
},
users: {
$addToSet: "$months.users"
}
}
},
{
$group: {
_id: "$_id._id",
months: {
$push: {
month: "$_id.months",
users: "$users"
}
}
}
}
])
You need to add the details to nested array, So we use unwind two times, when we re-group it, we need to use two groups. The months array is uniquely identify by a unique field. (As you said months.month is unique, if it is not unique, give another filed _id which should be unique)
Working Mongo playground

Related

Mongodb aggregate grouping elements of an array type field

I have below data in my collection:
[
{
"_id":{
"month":"Jan",
"year":"2022"
},
"products":[
{
"product":"ProdA",
"status":"failed",
"count":15
},
{
"product":"ProdA",
"status":"success",
"count":5
},
{
"product":"ProdB",
"status":"failed",
"count":20
},
{
"product":"ProdB",
"status":"success",
"count":10
}
]
},
...//more such data
]
I want to group the elements of products array on the name of the product, so that we have record of how what was the count of failure of success of each product in each month. Every record is guaranteed to have both success and failure count each month. The output should look like below:
[
{
"_id":{
"month":"Jan",
"year":"2022"
},
"products":[
{
"product":"ProdA","status":[{"name":"success","count":5},{"name":"failed","count":15}]
},
{
"product":"ProdB","status":[{"name":"success","count":10},{"name":"failed","count":20}]
}
]
},
...//data for succeeding months
]
I have tried to do something like this:
db.collection.aggregate([{ $unwind: "$products" },
{
$group: {
"_id": {
month: "$_id.month",
year: "$_id.year"
},
products: { $push: { "product": "$product", status: { $push: { name: "$status", count: "$count" } } } }
}
}]);
But above query doesn't work.
On which level I need to group fields so as to obtain above output.
Please help me to find out what I am doing wrong.
Thank You!
Your first group stage needs to group by both the _id and the product name, aggregate a list of status counts and then another group stage which then forms the products list:
db.collection.aggregate([
{$unwind: "$products"},
{$group: {
_id: {
id: "$_id",
product: "$products.product",
},
status: {
$push: {
name: "$products.status",
count: "$products.count"
}
}
}
},
{$group: {
_id: "$_id.id",
products: {
$push: {
product: "$_id.product",
status: "$status"
}
}
}
}
])
Mongo Playground

MongoDB use aggregate to format data from multiple collections

I have data in MongoDB collections as below:
Users:
{ name: String, email: String }
Books:
{ name: String, author: ref -> Users }
Chapters:
{ name: String, book: ref -> Books }
Paragraphs:
{ text: String, chapter: ref -> Chapters, created: Date, updated: Date, isRemoved: boolean }
I am trying to get some kind of statistical data in the following format:
[
{
book: { _id, name },
chaptersCount: 10,
paragraphs: {
count, mostRecent: { updated, created }}
author: { name, email },
},
{
...
},
...
]
So far, I have been able to get some data using the aggregate pipeline, but I am lost at this point. I have no idea how to convert it into the format I wish it to be. I can do it programmatically, but filters/sorting need to be applied on each of the final fields and it will be difficult to do that for a huge number of records (say a million).
Here is what I have so far:
const data = await ParagraphsDB.aggregate([
{ $match: { isRemoved: { $exists: false }, project: { $exists: true } } },
{ $lookup: { from: 'chapters', localField: 'chapter', foreignField: '_id', as: 'chapterDoc' }},
{ $unwind: '$chapterDoc' },
{ $lookup: { from: 'books', localField: 'chapterDoc.book', foreignField: '_id', as: 'bookDoc' }},
{ $unwind: '$bookDoc' },
{
$facet: {
paragraphCount: [
{ $count: 'value' },
],
pipelineResults: [
{ $project: { _id: 1, 'chapterDoc._id': 1, 'chapterDoc.name': 1, 'bookDoc._id': 1, 'bookDoc.name': 1 } },
],
},
},
{ $unwind: '$pipelineResults' },
{ $unwind: '$paragraphCount' },
{
$replaceRoot: {
newRoot: {
$mergeObjects: [ '$pipelineResults', { paragraphCount: '$paragraphCount.value' } ],
},
},
},
]);
I started with the Paragraph data because it is the smallest unit that could be sorted upon. How do I achieve the desired result?
Also, once I have formatted the data in the desired format, how can I sort by one of those fields?
Any help will be highly appreciated. Thanks.

MongoDB Aggregate Query Group By And Count

I have a subscribers collection:
{id: ObjectId ('52dskf45f10dsd4775'), agencyCode: '4579520',
listOfSubscriptions: [
{name: 'william', CreatedAt: ISODate ('2019-01-02'), subscriptionType: 'smartphones'},
{name: 'marianna', CreatedAt: ISODate ('2019-02-02'), subscriptionType: 'smartphones'},
{name: 'Freewind', CreatedAt: ISODate ('2019-04-02'), subscriptionType: 'smartphones'}
]
}
The agencys collection contains the following data:
{agencyCode: '4579520', BU: 'finances', company: ObjectId (' 445700500b24dsjdfm ')}
{agencyCode: '45007', BU: 'finances', company: ObjectId (' 445700500b24dsjdfm ')}
Collection subscriptionsType:
{id:ObjectId ('5lkf81gf45005drkj') ,nameSubscription: 'intense',SubscriptionType: 'smartphones' }
{id:ObjectId ('eb4512ezope780') ,nameSubscription: 'other',SubscriptionType: 'other' }
I want to do a cont on the subscribers by type of subscription.
Here is my request mongodb:
subscribers.aggregate(
[
{
$lookup: {
from: "agencys",
localField: "agencyCode",
foreignField: "agencyCode",
as: "data_agency"
}
},
{ $unwind: "$data_agency" },
{ $unwind: "$listOfSubscriptions" },
{
$match: {
$and: [
{ $agencyCode: '4579520' },
{ "data_agency.BU": 'finances' },
{ "data_agency.company": mongoose.Types.ObjectId('445700500b24dsjdfm') },
]
}
},
{
$group: {
"_id": "$listOfSubscriptions.subscriptionType",
countLines: { $sum: 1 },
}
},
]
result of my request:
{
"_id": "smartphones",
"countLines": 3
}
the result of my request does not display the rest of the subscription type.
I want to display all subscription types like this for the agency '4579520':
{
"_id": "smartphones",
"countLines": 3
},
{
"_id": "other",
"countLines": 0
}
Thanks,

Using $slice with $map on $lookup in MongoDB?

-
Hi , i have an aggregation query with lookup, i need to project specific fields from this lookup and slice them. This is what I've done so far.
{
$lookup: {
from: 'users',
localField: 'users',
foreignField: '_id',
as: 'users',
}},
I've added the unwind statement
{
$unwind: {
path: '$users',
preserveNullAndEmptyArrays: true
}},
I've added the group statement
{
$group: {
_id: {
_id: '$_id',
createdAt: '$createdAt',
updatedAt: '$updatedAt'
},
users: {
$addToSet: '$users',
}
}
},
And to project specific fields in array of users i did:
{
$project: {
_id: '$_id._id',
createdAt: '$_id.createdAt',
updatedAt: '$_id.updatedAt',
// users: {
// $slice: [
// "$users",
// skip,
// limit
// ]
// },
users: {
$map: {
input: '$users',
as: 'user',
in: {
email: '$$user.email',
name: '$$user.name',
username: '$$user.username',
updatedAt: '$$user.updatedAt'
}
}
}
}},
My question is , How can i use $slice in this scope ?
I don't know how 'legit' is this but , I've added fields to $addToSet statement in $group , so now i can use $slice with mapped fields.
{
$group: {
_id: {
_id: '$_id',
createdAt: '$createdAt',
updatedAt: '$updatedAt',
},
users: {
$addToSet: {
_id: '$users._id',
email: '$users.email',
name: '$users.name',
username: '$users.username',
updatedAt: '$users.updatedAt'
}
}
}
}
Now i can easily do $slice in $project statement.
{
$project: {
_id: '$_id._id',
users: {
$slice: [
"$users",
skip,
limit
]
}
},
}
If someone has a better solution , i would like to know.

Mongo Aggregation Grouping and Mapping of Object Array

I'm need to group a mongo collection and aggregate.
Example Collection
[{id:"1", skill: "cooking"},{id:"1", skill: "fishing"}]
Lookup Collection
[{ name: "cooking", value: 3 }, { name: "fishing", value: 2 }]
Desired Result
[{id: "1", skills: [{ value: 3, "cooking" }, { value: 2, "fishing"}]}]
Here's how far I am.
db.talent.aggregate([
{
$group: '$id'
skills: { $addToSet: '$skill' }
},
])
Result:
[{id: "1", skills: ["cooking", "fishing"]}]
I'm wondering if this is even possible.
I miss SQL, need help!
We can do this using $lookup, $group and $project in the aggregation pipeline
Shown below is the mongodb shell query
db.example_collection.aggregate([
{
$lookup: {
from: "lookup_collection",
localField: "skill",
foreignField: "name",
as: "skills"
}
},
{
$group: {
_id: "$id",
skills: {
$push: "$skills"
}
}
},
{
$project: {
"id": "$_id",
"skills.name": 1,
"skills.value": 1,
"_id": 0
}
}
])