问题
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
unwind
to unstructured thecomments[]
arraylookup
to joinUser
collection- Since lookup provides an array, we can get the first element of the array using
$arrayElemAt
with 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,
group
helps 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