乐闻世界logo
搜索文章和话题

Mongoose 如何在同一个查询指令中同时使用 populate 和 aggregate ?

5 个月前提问
3 个月前修改
浏览次数96

6个答案

1
2
3
4
5
6

在 Mongoose 中,populateaggregate 都是处理 MongoDB 文档引用的强大工具。populate 用于自动替换文档中的指定路径,用其引用的文档。aggregate 是一个更强大的工具,它可以执行复杂的数据处理,如分组(grouping)、排序(sorting)、计算字段等。

直到一段时间以前,populateaggregate 无法直接结合使用。然而,最新版本的 Mongoose 允许在 aggregate 管道中使用 $lookup 操作符实现功能类似于 populate 的效果。这意味着你现在可以在同一个查询中使用 aggregate 的强大功能并进行“填充”。

以下是一个使用 Mongoose 中 aggregate 和类似 populate 功能的示例:

假设我们有两个集合,authorsbooks。每本 book 文档都有一个字段 author,其中包含其对应 author 文档的引用。

Mongoose 的 aggregate 方法允许你向管道添加多个阶段,而 $lookup 阶段就可以用于实现类似 populate 的功能:

javascript
const Book = mongoose.model('Book'); Book.aggregate([ { $lookup: { from: 'authors', // 这是在数据库中 authors 集合的名称 localField: 'author', // book 中指向 author 的字段 foreignField: '_id', // author 集合中的 _id 字段 as: 'authorDetails' // 添加到结果文档中的字段名称,其中包含所有匹配的文档 } }, { $unwind: '$authorDetails' // 展开 authorDetails 数组,使其成为单个文档字段 }, // 其他可能的 aggregation stages,比如 $match, $group, $sort 等 // ... ]).exec((err, books) => { if (err) throw err; console.log(books); });

在这个例子中,$lookup 用来联结 books 集合和 authors 集合。localFieldforeignField 分别指定了本地和外部的匹配字段。as 字段指定了查找结果的输出字段。通过这种方式,aggregate 查询也可以返回带有关联数据的文档,类似于 populate 的行为。

需要注意的是,$lookup 只能用于 MongoDB 3.2 及以上版本,并且它要求关联的集合必须在同一个数据库中。而且,$unwind 阶段是可选的,只有当你知道每个匹配项只有一个文档时才需要它。(在一对多的关系中,$unwind 会产生多个文档。)

总结一下,通过结合使用 aggregate$lookup,你可以实现复杂的查询,同时“填充”来自其他集合的数据。这种方法比传统的 populate 提供了更高的灵活性和控制能力。

2024年6月29日 12:07 回复

With the latest version of mongoose (mongoose >= 3.6), you can but it requires a second query, and using populate differently. After your aggregation, do this:

shell
Patients.populate(result, {path: "patient"}, callback);

See more at the Mongoose API and the Mongoose docs.

2024年6月29日 12:07 回复

Edit: Looks like there's a new way to do it in the latest Mongoose API (see the above answer here: https://stackoverflow.com/a/23142503/293492)

Old answer below

You can use $lookup which is similar to populate.

In an unrelated example, I use $match to query for records and $lookup to populate a foreign model as a sub-property of these records:

shell
Invite.aggregate( { $match: {interview: req.params.interview}}, { $lookup: {from: 'users', localField: 'email', foreignField: 'email', as: 'user'} } ).exec( function (err, invites) { if (err) { next(err); } res.json(invites); } );
2024年6月29日 12:07 回复

You have to do it in two, not in one statement.

In async await scenario, make sure await until populate.

shell
const appointments = await Appointments.aggregate([...]); await Patients.populate(appointments, {path: "patient"}); return appointments;

Run code snippetHide results

Expand snippet

or (if you want to limit)

shell
await Patients.populate(appointments, {path: "patient", select: {_id: 1, fullname: 1}});

Run code snippetHide results

Expand snippet

2024年6月29日 12:07 回复

You can do it in one query like this:

shell
Appointments.aggregate([{ $group: { _id: '$date', patients: { $push: '$patient' } } }, { $project: { date: '$_id', patients: 1, _id: 0 } }, { $lookup: { from: "patients", localField: "patient", foreignField: "_id", as: "patient_doc" } } ])

populate basically uses $lookup under the hood. in this case no need for a second query. for more details check MongoDB aggregation lookup

2024年6月29日 12:07 回复

Perform a Join with $lookup

A collection orders contains the following documents:

shell
{ "_id" : 1, "item" : "abc", "price" : 12, "quantity" : 2 } { "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1 } { "_id" : 3 }

Another collection inventory contains the following documents:

shell
{ "_id" : 1, "sku" : "abc", description: "product 1", "instock" : 120 } { "_id" : 2, "sku" : "def", description: "product 2", "instock" : 80 } { "_id" : 3, "sku" : "ijk", description: "product 3", "instock" : 60 } { "_id" : 4, "sku" : "jkl", description: "product 4", "instock" : 70 } { "_id" : 5, "sku": null, description: "Incomplete" } { "_id" : 6 }

The following aggregation operation on the orders collection joins the documents from orders with the documents from the inventory collection using the fields item from the orders collection and the sku field from the inventory collection:

shell
db.orders.aggregate([ { $lookup: { from: "inventory", localField: "item", foreignField: "sku", as: "inventory_docs" } } ])

The operation returns the following documents:

shell
{ "_id" : 1, "item" : "abc", "price" : 12, "quantity" : 2, "inventory_docs" : [ { "_id" : 1, "sku" : "abc", description: "product 1", "instock" : 120 } ] } { "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "inventory_docs" : [ { "_id" : 4, "sku" : "jkl", "description" : "product 4", "instock" : 70 } ] } { "_id" : 3, "inventory_docs" : [ { "_id" : 5, "sku" : null, "description" : "Incomplete" }, { "_id" : 6 } ] }

Reference $lookup

2024年6月29日 12:07 回复

你的答案