如何在同一语句中使用填充和聚合?

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);
        );

【讨论】:

以上是关于如何在同一语句中使用填充和聚合?的主要内容,如果未能解决你的问题,请参考以下文章

如何仅在猫鼬中使用聚合填充嵌套在对象数组中的字段?

如何设置 ggplot2 填充颜色以聚合统计信息?

如何使用单个 SQL 聚合函数查询为同一个聚合函数获取多个结果?

sql 查询出一列内容,如何让它成一行显示。

猫鼬填充如何在同一对象而不是子文档中填充文档

如何在同一查询的另一列中引用聚合列?