How to extends MongoDB aggregation? - mongodb

I've written a pipeline that looks like:
db.getCollection('task_groups').aggregate(
[
{
$match: {status: {$ne: 3}}
},
{
$lookup: {
from: 'tasks',
localField: '_id',
foreignField: 'taskGroupId',
as: 'tasks'
}
},
{
$lookup: {
from: 'publications',
localField: 'publicationId',
foreignField: '_id',
as: 'publication'
}
},
{
$unwind: '$publication'
},
{
$lookup: {
from: 'accounts',
localField: 'publication.accountId',
foreignField: '_id',
as: 'account'
}
},
{
$unwind: '$account'
}
]
)
Output looks like:
Array of tasks has a property "someoneAccountId". It's a ObjectId from "accounts" collection. How can I extend the pipeline to get an account for each "task"?

You can use the other syntax for $lookup (Join Conditions and Uncorrelated Sub-queries)
So you can update your query to:
db.getCollection('task_groups').aggregate([{
$match: {
status: {
$ne: 3
}
}
},
{
$lookup: {
from: 'tasks',
let: {
targetId: '$_id',
},
pipeline: [{
$match: {
$expr: {
$eq: ['$taskGroupId', '$$targetId']
}
}
}, {
$lookup: {
from: 'accounts',
localField: 'someoneAccountId',
foreignField: '_id',
as: 'account' // You might change it to another name if you want
}
}, {
$unwind: '$account'
}],
as: 'tasks'
}
},
{
$lookup: {
from: 'publications',
localField: 'publicationId',
foreignField: '_id',
as: 'publication'
}
},
{
$unwind: '$publication'
},
{
$lookup: {
from: 'accounts',
localField: 'publication.accountId',
foreignField: '_id',
as: 'account'
}
},
{
$unwind: '$account'
}
])

Related

condition in mondodb aggregation

I'm using MongoDB. I need different types of booking(booking, classBooking, workshopBooking, membershipBooking). There are 2 statuses of booking (approve, rejected). I need only approved booking.
Problem is when there were not approved booking in any specific field entire records get empty.
I have coded as below.
var payData = await countryModel
.aggregate([
{ $match: filter },
{
$lookup: {
from: "venues",
localField: "_id",
foreignField: "country",
as: "venue",
},
},
{
$lookup: {
from: "bookings",
localField: "venue._id",
foreignField: "venue",
as: "booking",
},
},
// { $match: { "booking.status": "approve" } },
{
$lookup: {
from: "classbookings",
localField: "venue._id",
foreignField: "venue",
as: "classbooking",
},
},
// { $match: { "classbooking.status": "approve" } },
{
$lookup: {
from: "workshobookings",
localField: "venue._id",
foreignField: "venue",
as: "workshopbooking",
},
},
// { $match: { "workshopbooking.status": "approve" } },
{
$lookup: {
from: "membershipbookings",
localField: "venue._id",
foreignField: "venue",
as: "membershipbooking",
},
},
// { $match: { "membershipbooking.status": "approve" } },
])
.exec();
Suppose there are membership booking, but not other booking. I get empty because pipeline get empty because of previous records empty records.
I want if there are not approve records then that field should empty list'[]', otherwise list of that booking

MongoDb aggregate check if id exists in array of objects

I have written this aggregate and it works fine
db.ParcelStatus.aggregate([{
{
$lookup: {
from: "Parcel",
localField: "parcelId",
foreignField: "_id",
as: "parcel"
}
},
{
$unwind: {
path: "$parcel",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "ParcelStatus",
localField: "parcel._id",
foreignField: "parcelId",
as: "parcel.parcelStatuses"
}
},
{
$lookup: {
from: "Customer",
localField: "parcel.customerData.customerId",
foreignField: "_id",
as: "parcel.customerData.customer"
}
},
{
$unwind: "$parcel.customerData.customer"
}
])
Now in ParcelStatus array that is INCLUDED inside PARCEL object i need to check that
if(parcel.ParcelStatus.includes((x) => x.statusRepositoryId === 'ID's from frontend')
//then run $match on root against statusRepositoryId === 'SPECIFIC STATIC ID'
I don't know how i can do that inside aggregate. Your help will be much appreciated
Just add this $match stage:
{
$match: {
"parcel.parcelStatuses.statusRepositoryId": {$in: idArrayFromClient}
}
}

Multiple lookups in a pipeline

I have an aggregate call as listed below. I believe the massive delay is caused when the roles lookup happens because there are applicants, hired, and rejected people to lookup from the resources table. There are over 10,000 entries in the resource collection so the query is taking about 6 seconds. Is there something I am doing that is incredibly wrong here? I don't see how I can use indexes because all of the lookups are done by with the _id which is already indexed by default.
The applications, hired, and rejected fields are just arrays of object Ids e.g.
applicants: [
ObjectId('asldkajsdlkj'),
ObjectId('asldkjaoksdjak')
]
Any help would be greatly appreciated.It was taking 6 seconds on an M0 instance and is no faster on an M10 instance.
return db.collection('projects').aggregate([
{
$match: {
agents: ObjectId('SOMETHING')
}
},
{
$lookup: {
from: "agents",
localField: "agents",
foreignField: "_id",
as: "agents"
}
},
{
$lookup: {
from: "agencies",
localField: "agency",
foreignField: "_id",
as: "agency"
}
},
{
$lookup: {
from: "roles",
let: { "roles": "$roles" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$roles" ] } } },
{
$lookup: {
from:"resources",
let: { "applicants": "$applicants" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$applicants" ] } } }
],
as: "applicants"
}
},
{
$lookup: {
from: "resources",
let: { "hired": "$hired" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$hired" ] } } }
],
as: "hired"
}
},
{
$lookup: {
from: "resources",
let: { "rejected": "$rejected" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$rejected" ] } } }
],
as: "rejected"
}
},
{
$lookup: {
from: "agents",
localField: "hiring_agent",
foreignField: "_id",
as: "hiring_agent"
}
}
],
as: "roles"
}
}
], {
allowDiskUse: true
})
MongoDB is designed to store "boiled" data. It means, in project collection, you need to store redundant information to transform into desired result.
In your case, performance decreases with inner $lookup with let - pipeline for role collection due to this error.
Try to change:
db.collection('projects').aggregate([
{
$match: {
agents: ObjectId('SOMETHING')
}
},
{
$lookup: {
from: "agents",
localField: "agents",
foreignField: "_id",
as: "agents"
}
},
{
$lookup: {
from: "agencies",
localField: "agency",
foreignField: "_id",
as: "agency"
}
},
{
$lookup: {
from: "roles",
let: { "roles": "$roles" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$roles" ] } } },
{
$lookup: {
from:"resources",
localField: "applicants",
foreignField: "_id",
as: "applicants"
}
},
{
$lookup: {
from: "resources",
localField: "hired",
foreignField: "_id",
as: "hired"
}
},
{
$lookup: {
from: "resources",
localField: "rejected",
foreignField: "_id",
as: "rejected"
}
},
{
$lookup: {
from: "agents",
localField: "hiring_agent",
foreignField: "_id",
as: "hiring_agent"
}
}
],
as: "roles"
}
}
], {
allowDiskUse: true
})

How to join Master Detail Tables with master condition in MongoDB

How to add a condition for xads this query? (Exp: lads.status=1 )
db.xads.aggregate([
{
$lookup: {
from: 'xaditems',
localField: '_id',
foreignField: 'masterId',
as: 'xadItems'
},
}
]);
Works! Thanks DHIRAJ KATEKAR
db.xads.aggregate([
{
$match:{"status":1}
},
{
$lookup: {
from: 'xaditems',
localField: '_id',
foreignField: 'masterId',
as: 'xadItems'
},
}
]);
db.xads.aggregate([
{
$match:{"status":1} //add you filters in match
},
{
$lookup: {
from: 'xaditems',
localField: '_id',
foreignField: 'masterId',
as: 'xadItems'
},
}
]);

Remove field from embedded projection document after lookup

I have the following mongo aggregate query:
return db.collection('projects').aggregate([
{
$match: {
agents: ObjectId(agent)
}
},
{
$lookup: {
from: "agents",
localField: "agents",
foreignField: "_id",
as: "agents"
}
},
{
$lookup: {
from: "roles",
localField: "roles",
foreignField: "_id",
as: "roles"
}
},
{
$lookup: {
from: "agencies",
localField: "agency",
foreignField: "_id",
as: "agency"
}
},
{
$lookup: {
from: "resources",
localField: "roles.applicants",
foreignField: "_id",
as: "roles.applicants"
}
}
])
It works as it should, embedding the proper documents. However, the "password_hash" field is showing for each applicant. I want to remove that field. If I try to project and set roles.applicants.password_hash: 0 I actually end up getting the entire applicant without the password hash, but the rest of the roles fields are no longer there. So I get something that looks like:
roles: {
applicants: {
name: "Josh"
}
}
That should be
roles: {
title: "Super Hero",
applicants: {
name: "Josh"
}
}
I figured it out. Here is how I did it. First, the projection wasn't the issue with why the roles document was missing fields. It was the lookup.
What I did was changed the roles lookup to use the pipeline method on the nested documents and then did another pipeline on applicants, which first matched the applicants from the resource collection and then handled the projection. It looks like this.
{
$lookup: {
from: "roles",
let: { "roles": "$roles" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$roles" ] } } },
{
$lookup: {
from: "resources",
let: { "applicants": "$applicants" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$applicants" ] } } },
{
$project: {
first_name: 1
}
}
],
as: "applicants"
}
}
],
as: "roles"
}
}
The entire aggregate looks like this
return db.collection('projects').aggregate([
{
$match: {
agents: ObjectId(agent)
}
},
{
$lookup: {
from: "agents",
localField: "agents",
foreignField: "_id",
as: "agents"
}
},
{
$lookup: {
from: "agencies",
localField: "agency",
foreignField: "_id",
as: "agency"
}
},
{
$lookup: {
from: "roles",
let: { "roles": "$roles" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$roles" ] } } },
{
$lookup: {
from: "resources",
let: { "applicants": "$applicants" },
pipeline: [
{ $match: { $expr: { $in: [ "$_id", "$$applicants" ] } } },
{
$project: {
first_name: 1
}
}
],
as: "applicants"
}
}
],
as: "roles"
}
}
])