I have two collections which are of schema like
driver
_id:5f9c1d897ea5e246945cd73a
agent_name:"Ratnabh Kumar Rai"
agent_email:"ra2614#gmail.com"
agent_phone:"70****63331"
and
reviews
_id:5f9d54cb3a3ee10c6829c0a4
order_id:"5f9d40f096e4a506e8684aba"
user_id:"5f9bcb66f7a5bf4be0ad9973"
driver_id:"5f9c1d897ea5e246945cd73a"
rating:3
text:""
so i want to calculate the avg driver rating . I tried to use lookup aggregation so that i can get all details and then later calculate the sum...what i did was
let doc = await db
.collection("drivers")
.aggregate([
{
$project: {
_id: {
$toString: "$_id",
},
},
},
{
$lookup: {
from: "reviews",
localField: "_id",
foreignField: "driver_id",
as: "driver_info",
},
},
{
$project: {
agent_email: 1,
orderReview: "$driver_info",
},
},
])
.toArray();
But i am getting result as
{
_id: '5f9d63eb8737e82fbc193dd9',
orderReview: [ [Object], [Object], [Object], [Object], [Object] ]
}
which is partially correct as i also need to get details from my localfield collection that is drivers field, as of now you can see i am only getting id of driver in my projection i also did "agent_email:1" but not getting email
You're actually only projecting _id in the first pipeline and hence only _id is passed to further pipelines, If you need email in further pipelines you need to project it too
let doc = await db.collection("drivers").aggregate([
{
$project: {
_id: {
$toString: "$_id",
},
agent_email: "$agent_email"
},
},
{
$lookup: {
from: "reviews",
localField: "_id",
foreignField: "driver_id",
as: "driver_info",
},
},
{
$project: {
agent_email: 1,
orderReview: "$driver_info",
},
},
])
MongoDB PlayGround : https://mongoplayground.net/p/h7D-tYJ7sLU
[Update]
I realized that you're doing this for getting average and if you need it to be done in a single aggregate query, here it is how you can do it.
Using unwind operator you can flat the reviews array as objects and then group by _id and use the $avg aggregation operator
db.collection("drivers").aggregate([
{
$project: {
_id: {
$toString: "$_id",
},
agent_email: "$agent_email"
},
},
{
$lookup: {
from: "reviews",
localField: "_id",
foreignField: "driver_id",
as: "driver_info",
},
},
// Makes the driver info flat with other information
{
"$unwind": "$driver_info"
},
{
// Now you can group them
$group: {
_id: "$_id",
// Calculates avg on rating field
avg: {
"$avg": "$driver_info.rating"
},
// To make email field appear in next pipeline.
// You can do it for other fields too like Name, etc
agent_email: {
$first: "$agent_email"
}
}
},
{
$project: {
// select the fields you want to display
agent_email: 1,
avg: 1
},
},
])
MonogoDb playground Link
Related
My OBJ
[{
_id:XXXXXXXXXX,
role:admin
},
{
_id:XXXXXXXXXX,
role:superUser
}]
and need results using aggregation how to solve this using aggregation
[{
name:'username'
role:'test'
}
]
I suppose you need the following
let db1 = db.get().collection(`temp1`);
let db2 = db.get().collection(`temp2`);
await db1.aggregate([
{
$lookup: {
from: "temp2",
localField: "_id", // field in the orders collection
foreignField: "_id", // field in the items collection
as: "users"
}
},
{
$replaceRoot: { newRoot: { $mergeObjects: [{ $arrayElemAt: ["$users", 0] }, "$$ROOT"] } }
},
{ $project: { users: 0 } }
]).toArray()
I have a JobApplication collection which contains a jobseeker reference like so:
_id
job_post
jobseeker
60b62fcf36c4bd19fc2a1733
60a59ca4b43549049baef311
60802ba5be283f6ffcf07821
and I want to find the number of jobseekers grouped by theirs countries
I tried to do it like this:
return await JobApplication.aggregate([
{
$lookup: {
from: Jobseeker.collection.collectionName,
localField: 'jobseeker',
foreignField: '_id',
as: 'jobseeker'
}
},
{
$unwind: '$jobseeker'
},
{
$group: {
_id: "$jobseeker.current_country_of_residence",
count: { $sum: 1 }
}
}
])
but I'm getting a duplicated data like this:
"applicants_by_locations": [
{
"_id": "606f4d2bbca7c6f2be0625f9",
"count": 1
},
{
"_id": "606f4d2bbca7c6f2be0625f9",
"count": 2
}]
I'm getting the same country _id twice instead of one _id with count: 3.
I'm not sure what's going on here!!
Updated Code:
await JobApplication.aggregate([
{
$lookup: {
from: Jobseeker.collection.collectionName,
localField: 'jobseeker',
foreignField: '_id',
as: 'jobseeker'
} },
{ $unwind: '$jobseeker' },
{ $group: {
_id: "$jobseeker.current_country_of_residence",
count: { $sum: 1 }
} },
{
$group: {
_id: "$_id",
count: { $sum: "$count" }
} }
])
i am new in aggregation framework and have the following problem/question.
My collections looks like this:
users
_id: ObjectId('604caef28aa89e769585f0c4')
baseData: {
firstName: "max"
username: "max_muster"
...
...
}
activitiesFeed
_id: ObjectId('604cb97c99bbd77b54047370')
userID: "604caef28aa89e769585f0c4" // the user id
activity: "604caf718aa89e769585f0c8" // the activity id
viewed: false
activities
_id: ObjectId('604caf718aa89e769585f0c8')
"baseData": {
"userID":"604caef28aa89e769585f0c4",
"type":0,
"title":"Alessandro send you a friend request.",
"description":"",
"actionID":"604caef28aa89e769585f0c4"
},
aggregate function
const response = ActivityFeed.aggregate([
{
$match: { userID: ObjectId(args.user) },
},
{
$lookup: {
from: 'activities',
localField: 'activity',
foreignField: '_id',
as: 'lookup_activities',
},
},
{ $unwind: '$activities' },
{
$lookup: {
from: 'users',
localField: 'lookup_activities.baseData.actionID',
foreignField: '_id',
as: 'lookup_users',
},
},
]);
How i can made a lookup with the first lookup result?
I made a lookup to the activities collection. This collection holds the id for the second lookup baseData.userID. But with this approach i have no result with the second lookup.
The reason why i made a join to the activities collection is that the actionID can hold a userID or a document id from another collection. It depend on the type in the activities collection.
Is it in the aggregation framework possible to made a lookup which depends on the type and of the previous lookup?
Thank you in advance.
All I did is fixing your aggregation. You unwind the wrong field and please make sure all your ID fields are ObjectId. I highly recommend using MongoDB Compass to assist your aggregation if you are new to the aggregation function. They have stage by stage assistance GUI which will really make your life easier.
mongoplayground
db.activitiesFeed.aggregate([
{
$match: {
userID: ObjectId("604caef28aa89e769585f0c4")
},
},
{
$lookup: {
from: "activities",
localField: "activity",
foreignField: "_id",
as: "lookup_activities",
},
},
{
$unwind: "$lookup_activities"
},
{
$lookup: {
from: "users",
localField: "lookup_activities.baseData.actionID",
foreignField: "_id",
as: "lookup_users",
},
},
])
Sample Used
db={
"users": [
{
_id: ObjectId("604caef28aa89e769585f0c4"),
baseData: {
firstName: "max",
username: "max_muster"
}
}
],
"activitiesFeed": [
{
_id: ObjectId("604cb97c99bbd77b54047370"),
userID: ObjectId("604caef28aa89e769585f0c4"),
activity: ObjectId("604caf718aa89e769585f0c8"),
viewed: false
}
],
"activities": [
{
_id: ObjectId("604caf718aa89e769585f0c8"),
"baseData": {
"userID": ObjectId("604caef28aa89e769585f0c4"),
"type": 0,
"title": "Alessandro send you a friend request.",
"description": "",
"actionID": ObjectId("604caef28aa89e769585f0c4")
},
}
]
}
I have the following 3 collections schema in my Node.js app
Users{
_id
Name
}
Vendors{
_id
userId
CompanyName
}
CustomerFavoriteVendors{
_id
customerId
vendorId
}
Now I am trying to retrieve a list of all Vendors, while displaying their full user details as well as if they are Favorited by currently logged in customer, so I've built the following vendors aggregate function yet I can only retrieve vendors whom were Favorited, so I am wondering what I am missing here to retrieve all vendors despite if they were favorited or not? Thanks for your effort and support
let size = 10;
let offset = 0;
let query = [];
query.push(
{ $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "userdetail" } },
{ $unwind: "$userdetail" },
{ $lookup: { from: "customerfavoritevendors", localField: "_id", foreignField: "vendorId", as: "customerfav" } },
{ $unwind: "$customerfav" },
{ $group: { _id: null, content: { $push: '$$ROOT' },count: { $sum: 1 } } },
{ $project: { content: { $slice: [ '$content', offset, size ] }, count: 1, _id: 1 } },
);
const vendorsList = await Vendors.aggregate(query);
I am aggregating a large data where i need to group the data according to their types and also i need to lookup the data from another collections.inside $group i want my lookup's data.
my code for aggregation goes like :
NotificationSchema.aggregate([{
$match: condition
}, {
$group: {
_id: "$type",
details: {
$push: "$$ROOT"
},
count: {
$sum: 1
}
}
}, {
$sort: {
_id: -1
}
}, {
$lookup: {
from: "vehicles",
localField: "details.device_id",
foreignField: "device_id",
as: "vehicle"
}
}], function(err, result) {
if (err) {
res.status(500);
return res.json({
result: err
});
}
console.log('res', result[0].details[0]);
res.json({
result: result
});
});
if i remove or comment the $group code i get the data with Vehicle array but using $group i get vehicle array empty, as i have only two types in records in the database, i get two empty array of vehicles. but i have 102 records so i need 102 arrays of vehicles how can i get such result.
what i am getting in console right now is
res [ { _id: 'Vehicle Delay Alert!',
details:
[ [Object],
....57 object...
[Object] ],
count: 57,
vehicle: [] },
and inside every object i dont find vehicle array so i wish to remove vehicle array from here and get a vehicle array that is generated from $lookup inside every object.
Any suggestions are highly appreciated.
You $lookup from details.device_id which comes from an array. To $lookup from a regular field, you can place $lookup after the $match :
NotificationSchema.aggregate([{
$match: condition
}, {
$lookup: {
from: "vehicles",
localField: "device_id",
foreignField: "device_id",
as: "vehicle"
}
}, {
$group: {
_id: "$type",
details: {
$push: "$$ROOT"
},
count: {
$sum: 1
}
}
}, {
$sort: {
_id: -1
}
}])