在对象数组中,在Mongoose聚合管道中填充字段属性



我有一个产品模型,它具有以下模式

const product = {
...other fields,
ratings: [
{
star: Number,
postedBy: {
type: ObjectId,
ref: "User",
},
},
],
}

我想填充评级。postedBy在聚合管道中

在普通的查找查询中,我只会这样做

const product = Product
.find({})
.populate({
path: "ratings.postedBy",
select: "_id name email picture",
});

那么我就会得到这个形状为

的查询
"ratings": [
{
"_id": "63fac53330e5bc099651d33c",
"star": 4,
"postedBy": {
"_id": "6241b25f8b01924f5c75cd82",
"name": "Adrian",
"email": "adrianjohn@gmail.com",
"picture": "https://graph.facebook.com/jujialdald19132/picture"
}
},
{
"_id": "63fb22a9d284a9066d97bf1f",
"star": 5,
"postedBy": {
"_id": "6241b32d8b01924f5c75cd85",
"name": "tommy",
"email": "tommy@gmail.com",
"picture": ""
}
}
]

如何在聚合管道中做完全相同的事情?

这是我尝试过的

const product = await Product.aggregate([
...other pipeline stages
{
//populate ratings.postedBy
$lookup: {
from: "users", //table name
localField: "ratings.postedBy",
foreignField: "_id",
as: "ratings.postedBy",
pipeline: [{ $project: { _id: 1, name: 1, star: 1 } }],
},
},
]);
反过来,我得到的查询形状为
"ratings": {
"postedBy": [
{
"_id": "6241b25f8b01924f5c75cd82",
"name": "Adrian",
"email": "adrianjohn@gmail.com"
},
{
"_id": "6241b32d8b01924f5c75cd85",
"name": "tommy",
"email": "tommy@gmail.com"
}
]
}

请告知。

非常感谢。

完整的工作示例:

import mongoose from "mongoose";
import { faker } from '@faker-js/faker';
import { config } from '../../src/config';
import util from 'util';
mongoose.set('debug', true);
const userSchema = new mongoose.Schema({
name: String,
avatar: String
});
const User = mongoose.model('user', userSchema);
const productSchema = new mongoose.Schema({
name: String,
ratings: [{ star: Number, postedBy: { type: mongoose.Types.ObjectId, ref: 'user' } }]
})
const Product = mongoose.model('product', productSchema);
(async function main() {
try {
// seed
await mongoose.connect(config.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false });
const fakeUsers = faker.helpers.multiple(() => ({ name: faker.internet.userName(), avatar: faker.image.avatar() }), { count: 5 })
const userDocs = await User.create(fakeUsers)
await Product.create([
{ name: 'product a', ratings: [{ star: 4, postedBy: userDocs[0]._id }, { star: 5, postedBy: userDocs[1]._id }] },
{ name: 'product b', ratings: [{ star: 10, postedBy: userDocs[1]._id }] },
])
// populate
const populatedResult = await Product.find({ name: 'product a' }).populate({ path: 'ratings.postedBy', select: '_id name' }).exec();
console.log('populate result: ', util.inspect(populatedResult, false, null))
// aggregate
const aggregatedResult = await Product.aggregate([
{
'$match': {
'name': 'product a'
}
}, {
'$unwind': {
'path': '$ratings'
}
}, {
'$lookup': {
'from': 'users',
'localField': 'ratings.postedBy',
'foreignField': '_id',
'as': 'user'
}
}, {
'$unwind': {
'path': '$user'
}
}, {
'$project': {
'_id': 1,
'name': 1,
'ratings': {
'_id': '$ratings._id',
'star': '$ratings.star',
'postedBy': {
'_id': '$user._id',
'name': '$user.name'
}
}
}
}, {
'$group': {
'_id': '$_id',
'name': {
'$first': '$name'
},
'ratings': {
'$push': '$ratings'
}
}
}
])
console.log('aggregate result: ', util.inspect(aggregatedResult, false, null))
} catch (error) {
console.error(error);
} finally {
await Promise.all([
mongoose.connection.dropCollection('users'),
mongoose.connection.dropCollection('products')
])
await mongoose.connection.close()
}
})();

填充结果和聚合结果的日志:

populate result:  [
{
_id: 6464b09c2d35d0ab8aad31a8,
name: 'product a',
ratings: [
{
_id: 6464b09c2d35d0ab8aad31a9,
star: 4,
postedBy: { _id: 6464b09b2d35d0ab8aad319e, name: 'Francisca69' }
},
{
_id: 6464b09c2d35d0ab8aad31aa,
star: 5,
postedBy: { _id: 6464b09b2d35d0ab8aad319f, name: 'Elise.Reinger95' }
}
],
__v: 0
}
]
aggregate result:  [
{
_id: 6464b09c2d35d0ab8aad31a8,
name: 'product a',
ratings: [
{
postedBy: { _id: 6464b09b2d35d0ab8aad319e, name: 'Francisca69' },
_id: 6464b09c2d35d0ab8aad31a9,
star: 4
},
{
postedBy: { _id: 6464b09b2d35d0ab8aad319f, name: 'Elise.Reinger95' },
_id: 6464b09c2d35d0ab8aad31aa,
star: 5
}
]
}
]

包版本:

"mongodb": "^3.6.3",
"mongoose": "^5.13.17",

最新更新