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

9avjhtql  于 2023-05-18  发布在  Go
关注(0)|答案(1)|浏览(113)

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

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

我想在mongoose中填充ratings.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"
                }
            ]
        }

敬请指教。
非常感谢。

c0vxltue

c0vxltue1#

一个完整的工作示例:

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",

相关问题