I'm trying to get an element value based on a result _id in an aggregation.
This is the aggregation:
$project: {
_id: 0,
brand: "$_id",
"options": {
$mergeObjects: "$ram"
},
sum: {
$add: [
"$sm",
1
]
}
}
And I want brand to get the name from other collection named "brands" which looks like this
[
{
"_id": ObjectId("617b0dbacda6cbd1a0403f68"),
"SerialNumber": "45454234324",
"name": "hp"
},
{
"_id": ObjectId("617b0dbacda6cbd1a0403f69"),
"SerialNumber": "azazz5245454az",
"name": "asus"
}]
What I want to get is the name of brand using using its _id based on the result _id.
Using SQL its something like this:
Get brands.name where _id=brands._id
i managed to do it using $lookup
{
$lookup:
{
from: "brands",
localField: "brand",
foreignField: "_id",
as: "brand"
}
},
{
$set: {
brand: "$brand.brand"
}
},
Related
How can make I lookup on in with productId and categoryId.
Below is the sample of JSON
"data": {
"status": 1,
"offerId": "634017ad34c7b3545bd47681",
"offerType": "buyOneGetOne",
"categoryId": [
"62de9b22dcafd44290ab03c1",
"62dfe695ec4d69cfeab0265d"
],
"productId": [
"633170230e88e2d859d15ee6",
"62de9d102b2b72f80eda7585",
"62de9d1e2b2b72f80eda7588"
],
"title": "Buy One Get One Deal ",
"description": "buy one get more",
"amountType": "amount",
"amount": 12341,
"startDate": "2022-10-04T06:43:39.000Z",
"endDate": "2022-10-08T06:43:39.000Z"
}
I'm going to assume that you are using ObjectId as _id in your categories and products collection. If I understood your question your document as the following minimal structure:
_id: '1',
data: {
...
}
So, you are looking for something like:
db.nameOfYourCollectionWithData.aggregate([{
$addFields: {
'data.categoryId': {
$map: {
input: '$data.categoryId',
'in': {
$toObjectId: '$$this'
}
}
},
'data.productId': {
$map: {
input: '$data.productId',
'in': {
$toObjectId: '$$this'
}
}
}
}
}, {
$lookup: {
from: 'categories',
localField: 'data.categoryId',
foreignField: '_id',
as: 'data.categoryId'
}
}, {
$lookup: {
from: 'products',
localField: 'data.productId',
foreignField: '_id',
as: 'data.productId'
}
}])
First, you have to convert your array ids into ObjectId. After do this, you can perform the lookup stages directly. If your document is 'data', just remove any reference to data in the aggregate pipeline.
If your _id in categories and products are string instead of ObjectId, just remove the $addFields stage, because is no longer neccessary.
when i do a lookup in mongodb i enter an as field and the data is loaded into this field. But if I have an array of objects and one of the fields is a REF field then I always have the problem that the data from the lookup is always loaded into a separate field and I cannot find it with the array. How can I do a lookup that loads the data directly into the array element.
for example
i have a organisation with an array of users and there status and role in the organisation
{
_id: "60cc87da3a530000173f6d33",
"baseData": {
"name": "Organisation 1",
"email": "org#gmail.com",
"image": "",
"description": "desc",
"users": [
{
userID: "60cc87803a530000173f6d2d",
userStatus: "active" ,
userOrgRole: "Admin"
},
{
userID: "60cc87803a530000173f9h4u",
userStatus: "active" ,
userOrgRole: "User"
}
]
}
}
when i made a lookup an define the as field as baseData.users the user array are overwirte with the user data. But how i can add the data to each users array.
for example this is the result i need
{
_id: "60cc87da3a530000173f6d33",
"baseData": {
"name": "Organisation 1",
"email": "org#gmail.com",
"image": "",
"description": "desc",
"users": [
{
userID: "60cc87803a530000173f6d2d",
userStatus: "active" ,
userOrgRole: "Admin"
// userdata data from lookup for each array
},
{
userID: "60cc87803a530000173f9h4u",
userStatus: "active" ,
userOrgRole: "User"
// userdata data from lookup for each array
}
]
}
Try this way, add these stages to aggregation pipeline. It will help to add user details..
{
$unwind: {
path: "$baseData.users",
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: "users", // replace with your collection name
localField: "baseData.users.userID",
foreignField: "_id",
as: "baseData.users",
},
},
{
$group: {
_id: "$_id",
baseData: {
$push: "$baseData",
},
},
},
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
Suppose you have the following documents in my collection:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"name": "Asset1"
"shapes":[
{
"_id": "5cf10fea4cb6352abcfe094b",
"shape":"square",
"color":"blue"
},
{
"_id": "5cf10fea4cb6352abcfe094c",
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"name": "Asset2"
"shapes":[
{
"_id": "5c7524f76792cf28f80793e3"
"shape":"square",
"color":"black"
},
{
"_id": "5c7524f76792cf28f80793e4"
"shape":"circle",
"color":"green"
}
]
}
}
I do not have problem in finding a particular color like this also it is working
Test.find(
{"shapes.color": "red"},
//Match the exact color
{
products: {
$elemMatch: { color: "red" }
}
}
)
The main problem is I want to add this Asset1 and take only color red not the whole array by referencing ObjectId("562e7c594c12942f08fe4192") it in test2 collection. How do I do this using Mongoose?
This is the Test2 Schema where I did referencing
const Test2Schema = new Schema({
anothername: String,
test1Shape: {
type: mongoose.Schema.Types.ObjectId,
ref: "Test"
},
});
Use the below query, it uses $unwind, $match , $lookup and $out
$unwind - To unwind the Shapes array
$match - To filter the matching color red records
$lookup - To lookup the matching documents in Test2 Collection
$out - To override the Test2 collection with the merged document
db.Test1.aggregate([
{
$unwind: "$shapes"
},
{
$match: {
"shapes.color": "red"
}
},
{
$lookup: {
from: "Test2",
localField: "_id",
foreignField: "_id",
as: "mergeResult"
}
},
{
$out: "Test2"
}
]);
I have a visits collection where I successfully count the number of visits per location
Visits model:
{
"_id": {
"$oid": "5a3969e2f4ea3e33ac5a523d"
},
"locationId": "5a395ccf210a1d35d0df4a58"
}
locationId above is of type 'Object' as I learned lookup localField and foreignField must be of same type
nodejs code =>
let sort = { "count": -1, "locationId": 1 };
Visit.aggregate([
{
$match:{
$and: [
{ accountId: req.session.passport.user },
{
'details.dateTimeIn': {
$gte: new Date(dateFrom), $lte: new Date(dateTo)
}
}
]
}
},
{
"$group": {
//_id: name,
_id: "$locationId",
count: { $sum: 1 }
}
},
{ $sort: sort }
])
Output is half ok:
[
{
"_id":"5a395ccf210a1d35d0df4a58",
"count":20
}
]
Instead of showing location id id like to show location name. Schema for locations collection is:
{
"_id": {
"$oid": "5a395ccf210a1d35d0df4a58"
"name": "Tower A",
"__v": 0
}
}
Research suggests I need to use $lookup to get that JOIN effect
So I tried
{
"$lookup": {
from: "locations",
localField: "_id",
foreignField: "_id",
as: "locationdetails"
}
}
but the match seems broken. The closest I got was a list of all locations in 'locationdetails'
But with code above here is the empty locationdetails
[
{
"_id":"5a395ddf1d221918d0041313",
"count":20,
"locationdetails":[
]
}
]
What am I missing ?