问题
I have these three collections (Products, Users & Comments)
1. Product Model
var ProductSchema = new Schema({
title: { type: String, text: true, required: true },
description: { type: String, required: true },
category: [{ type: String, required: true }],
type: [{ type: String, required: true }],
isUsed: { type: Boolean, },
manufacturer: { type: String },
price: { type: Number },
});
2. User Model
var UserSchema = new Schema({
firstName: { type: String, text: true, required: true },
lastName: { type: String, text: true, required: true },
country: [{ type: String, required: true }],
phoneNumber: [{ type: String, required: true }]
});
3. Comment Model
var CommentSchema = new Schema({
comment: { type: String, text: true, required: true },
commented_by: { type: Schema.Types.ObjectId, ref: 'User', required: true },
commented_on: { type: Schema.Types.ObjectId, ref: 'Product', required: true }
});
Then, when a request is sent to get a specific product, this is the mongoose code that runs in the background.
var aggregate = Product.aggregate([
{ "$match": { _id: ObjectId(req.params.id) } }, /** which is the product id **/
{ "$limit": 1 },
{
$lookup: {
from: 'comments',
let: { id: "$_id" },
pipeline: [
{ $match: { $expr: { $eq: [ "$commented_on.product", "$$id" ] } } },
],
as: "comments"
}
},
{
$addFields: {
comments: "$comments",
comments_no: { $size: "$comments" },
hasCommented: { $in: [ObjectId(req.user._id), "$comments.commented_by" ] }, /** req.user._id is the user's id **/
}
},
{
$project: {
_id: 1,
title: 1,
description: 1,
category: 1,
type: 1,
price: 1,
comments: 1,
hasCommented: 1,
comments_no: 1,
}
}
]);
Output
{
"_id": "5f992b5338f5f035f35911c5",
"title": "Some title here..."
"description": Some description here ...
"price": 1000,
"category": "Clothing",
"type": "shoe",
"comments": [
{
"comment": "Nice Product",
"commented_by": "5f992b5338f5f035f35911b2",
"commented_on": "5f992b5338f5f035f35911c5"
},
{
"comment": "I like this product",
"commented_by": "5f992b5338f5f035f35911a2",
"commented_on": "5f992b5338f5f035f35911c5"
},
{
"comment": "Great, I like it!",
"commented_by": "5f992b5338f5f035f35911c8",
"commented_on": "5f992b5338f5f035f35911c5"
}
],
"hasCommented": true,
"comments_no": 3
}
Expected Result
{
"_id": "5f992b5338f5f035f35911c5",
"title": "Some title here..."
"description": Some description here ...
"price": 1000,
"category": "Clothing",
"type": "shoe",
"comments": [
{
"comment": "Nice Product",
"commented_by": {
"_id": "5f992b5338f5f035f35911b2",
"firstName": "Jack",
"lastName": "Sparrow"
},
"commented_on": "5f992b5338f5f035f35911c5"
},
{
"comment": "I like this product",
"commented_by": {
"_id": "5f992b5338f5f035f35911a2",
"firstName": "John",
"lastName": "Doe"
},
"commented_on": "5f992b5338f5f035f35911c5"
},
{
"comment": "Great, I like it!",
"commented_by": {
"_id": "5f992b5338f5f035f35911c8",
"firstName": "Mary",
"lastName": "Jane"
},
"commented_on": "5f992b5338f5f035f35911c5"
}
],
"hasCommented": true,
"comments_no": 3
}
When getting a specific product, I am able now to list comments of the product with it but the problem is on all listed comments on "commented_by" section I need to populate it (need the firstName, lastName...) field with it.
Any idea how can I do this?
回答1:
What you have done is correct. After the lookup between Product and Comment, you need to have another lookup to join User also.
You need to add following stages to achieve your target
unwindto unstructured thecomments[]arraylookupto joinUsercollection- Since lookup provides an array, we can get the first element of the array using
$arrayElemAtwith the help of safety operator$ifNull. (If thecomments []is empty, then the script throws error. To handle that we use$ifNull) - We already unstructured the array,
grouphelps to regroup it
The stages are given below
{
$unwind: "$comments"
},
{
$lookup: {
from: "User",
let: {
id: "$comments.commented_by"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$_id",
"$$id"
]
}
}
},
],
as: "comments.commented_by"
}
},
{
$addFields: {
"comments.commented_by": {
$ifNull: [
{
$arrayElemAt: [
"$comments.commented_by",
0
]
},
{}
]
}
}
},
{
$group: {
_id: "$_id",
title: {
$first: "$title"
},
hasCommented: {
$first: "$hasCommented"
},
comments: {
$push: "$comments"
}
}
}
Working Mongo playground
Note : FYI,The variable and collection name may be different.
来源:https://stackoverflow.com/questions/64661069/mongodb-populating-field-on-aggregation-query