如何在同一语句中使用填充和聚合?
Posted
技术标签:
【中文标题】如何在同一语句中使用填充和聚合?【英文标题】:how to use populate and aggregate in same statement? 【发布时间】:2013-05-16 19:28:04 【问题描述】:这是我的约会收藏:
_id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002")
_id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002")
_id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002")
我使用聚合得到以下结果
date: ISODate("2013-05-13T22:00:00Z"),
patients:[ObjectId("518ee0bc9be1909012000002"),ObjectId("518ee0bc9be1909012000002"),ObjectId("518ee0bc9be1909012000002")]
像这样:
Appointments.aggregate([
$group: _id: '$date', patients: $push: '$patient',
$project: date: '$_id', patients: 1, _id: 0
], ...)
如何填充患者文档
我做到了这一点,但它不起作用......Appointments.find().populate("patient").aggregate
....
换句话说,我可以在同一个语句中使用填充和聚合
请帮忙
【问题讨论】:
考虑将接受的答案更改为 ruffrey 的答案,因为 Mongoose 现在支持这一点。 【参考方案1】:使用最新版本的 mongoose (mongoose >= 3.6),您可以,但它需要第二次查询,并且使用不同的填充。聚合后,执行以下操作:
Patients.populate(result, path: "patient", callback);
在Mongoose API 和Mongoose docs 上查看更多信息。
【讨论】:
我嵌入了文档数组,以及如何将该文档中的值提供给上述答案中的路径。文档有点像这样 ["from" : "objectId", "text" : "This is testing","date" : "date"] 我想给出路径:来自上面的答案。 查看原始问题 -"patient"
是数组中文档的键之一。在您的示例中,假设 Message
是猫鼬模型。 Message.populate(result, paths: "from", callback);
要填充 2 个路径,只需添加到路径列表 Message.populate(result, paths: "path1 path2 path3", callback);
但是当我这样做时我得到一个空字符串。可能是什么原因?
所以这意味着我应该先聚合然后填充结果,我不能在填充内聚合吗?【参考方案2】:
编辑:看起来在最新的 Mongoose API 中有一种新方法(请参阅上面的答案:https://***.com/a/23142503/293492)
下面的旧答案
你可以使用类似于populate的$lookup。
在一个不相关的示例中,我使用 $match 查询记录并使用 $lookup 将外部模型填充为这些记录的子属性:
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);
);
【讨论】:
太好了,有时最好的答案是最简单的,我不知道查找命令会获取我困惑的匹配结果。 是否可以在这里添加select
排除某些字段?
@VahidAlimohamadi 我不太确定.. 但我认为您应该尝试投影您想要的字段,而不是排除在外。【参考方案3】:
您可以像这样在一个查询中执行此操作:
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 基本上在后台使用 $lookup。 在这种情况下,不需要第二次查询。 更多详情请查看MongoDB aggregation lookup
【讨论】:
【参考方案4】:你必须分两步做,而不是一个声明。
在异步等待场景中,确保等待直到填充。
const appointments = await Appointments.aggregate([...]);
await Patients.populate(appointments, path: "patient");
return appointments;
或者(如果你想限制)
await Patients.populate(appointments, path: "patient", select: _id: 1, fullname: 1);
【讨论】:
【参考方案5】:使用 $lookup 执行联接
一个集合订单包含以下文件:
"_id" : 1, "item" : "abc", "price" : 12, "quantity" : 2
"_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1
"_id" : 3
另一个收集清单包含以下文件:
"_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
以下对订单集合的聚合操作使用订单集合中的字段 item 和库存集合中的 sku 字段将订单中的文档与库存集合中的文档连接起来:
db.orders.aggregate([
$lookup:
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
])
该操作返回以下文档:
"_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
【讨论】:
【参考方案6】:简答: 你不能。
长答案: 在聚合框架中,返回的字段由您构建,您可以“重命名”文档属性。
这意味着 Mongoose 无法确定您引用的文档将在最终结果中可用。
在这种情况下,您可以做的最好的事情是在查询返回后填充您想要的字段。是的,这会导致两个数据库调用,但这是 MongoDB 允许我们做的。
有点像这样:
Appointments.aggregate([ ... ], function( e, result )
if ( e ) return;
// You would probably have to do some loop here, as probably 'result' is array
Patients.findOneById( result.patient, function( e, patient )
if ( e ) return;
result.patient = patient;
);
);
【讨论】:
下面的答案提供了一种更简单的方法来完成任务。无需编写自己的循环。【参考方案7】:domain.Farm.aggregate(
$match:
"_id": mongoose.Types.ObjectId(farmId)
,
$unwind: "$SelfAssessment"
,
$match:
"SelfAssessment.questionCategoryID": QuesCategoryId,
"SelfAssessment.questionID": quesId
,function(err, docs)
var options =
path: 'SelfAssessment.actions',
model: 'FarmAction'
;
domain.Farm.populate(docs, options, function (err, projects)
callback(err,projects);
);
);
我得到动作模型填充的结果
"error": false, "object": [
"_id": "57750cf6197f0b5137d259a0",
"createdAt": "2016-06-30T12:13:42.299Z",
"updatedAt": "2016-06-30T12:13:42.299Z",
"farmName": "abb",
"userId": "57750ce2197f0b5137d2599e",
"SelfAssessment":
"questionName": "Aquatic biodiversity",
"questionID": "3kGTBsESPeYQoA8ae2Ocoy",
"questionCategoryID": "5aBe7kuYWIEoyqWCWcAEe0",
"question": "Waterways protected from nutrient runoff and stock access through fencing, buffer strips and off stream watering points",
"questionImage": "http://images.contentful.com/vkfoa0gk73be/4pGLv16BziYYSe2ageCK04/6a04041ab3344ec18fb2ecaba3bb26d5/thumb1_home.png",
"_id": "57750cf6197f0b5137d259a1",
"actions": [
"_id": "577512c6af3a87543932e675",
"createdAt": "2016-06-30T12:38:30.314Z",
"updatedAt": "2016-06-30T12:38:30.314Z",
"__v": 0,
"Evidence": [],
"setReminder": "",
"description": "sdsdsd",
"priority": "High",
"created": "2016-06-30T12:38:30.312Z",
"actionTitle": "sdsd"
],
"answer": "Relevant"
,
"locations": []
], "message": "", "extendedMessage": "", "timeStamp": 1467351827979
【讨论】:
如何检查“选项”中的“SelfAssessment.actions”是否为空?【参考方案8】:我看到有很多答案,我是 mongoldb 的新手,我也想分享我的答案。 我正在使用聚合函数和查找来填充患者。 为了便于阅读,我更改了集合和字段的名称。
希望对你有帮助。
数据库:
db=
"appointmentCol": [
_id: ObjectId("518ee0bc9be1909012000001"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000001")
,
_id: ObjectId("518ee0bc9be1909012000002"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000002")
,
_id: ObjectId("518ee0bc9be1909012000003"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000003")
],
"patientCol": [
"_id": ObjectId("518ee0bc9be1909012000001"),
"name": "P1"
,
"_id": ObjectId("518ee0bc9be1909012000002"),
"name": "P2"
,
"_id": ObjectId("518ee0bc9be1909012000003"),
"name": "P3"
,
]
使用查找聚合查询:
db.appointmentCol.aggregate([
"$lookup":
"from": "patientCol",
"localField": "patientId",
"foreignField": "_id",
"as": "patient"
])
输出:
[
"_id": ObjectId("518ee0bc9be1909012000001"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
"_id": ObjectId("518ee0bc9be1909012000001"),
"name": "P1"
],
"patientId": ObjectId("518ee0bc9be1909012000001")
,
"_id": ObjectId("518ee0bc9be1909012000002"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
"_id": ObjectId("518ee0bc9be1909012000002"),
"name": "P2"
],
"patientId": ObjectId("518ee0bc9be1909012000002")
,
"_id": ObjectId("518ee0bc9be1909012000003"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
"_id": ObjectId("518ee0bc9be1909012000003"),
"name": "P3"
],
"patientId": ObjectId("518ee0bc9be1909012000003")
]
游乐场: mongoplayground.net
【讨论】:
【参考方案9】:我改用查找,它运行良好。请参阅下面的代码。
Post.aggregate([
$group:
// Each `_id` must be unique, so if there are multiple
// posts with the same category, MongoDB will increment `count`.
_id: '$category',
count: $sum: 1
,
//from: is collection name in MongoDB, localField are primary and foreign keys in Model.
$lookup: from: 'categories', localField: '_id', foreignField:'_id', as: 'category'
]).then(categoryCount =>
console.log(categoryCount);
let json = [];
categoryCount.forEach(cat =>
console.log(json);
);
【讨论】:
以上是关于如何在同一语句中使用填充和聚合?的主要内容,如果未能解决你的问题,请参考以下文章