如何使用嵌套数组文档进行mongoose聚合

时间:2015-06-06 13:21:05

标签: node.js mongodb mongoose

我有一个Mongodb集合,民意调查以及以下架构

{  
 "options" : [ 
               {   
                 "_id" : Object Id,  
                 "option" : String,  
                 "votes" : [ Object Id ] // object ids of users who voted
               },..... 
             ]   
}

假设我有节点js中用户的 userId ,我想向其发送此信息。 我的任务是

(1)在上面的json对象中包含一个额外的字段(我使用mongoose)。

as

  

" myVote" :option._id

我需要找到

的option._id

options [someIndex] .votes包含userId

(2)更改现有的"投票"每个选项中的字段表示特定选项的投票数,如示例

中所示

示例:

{  
  "options" : [ 
               {   
                 "_id" : 1,  
                 "option" : "A",  
                 "votes" : [ 1,2,3 ]
               },
               {   
                 "_id" : 2,  
                 "option" : "B",  
                 "votes" : [ 5 ]
               },
               {   
                 "_id" : 3,  
                 "option" : "C",  
                 "votes" : [  ]
               }
             ]   
}

因此,如果用户id = 5的用户想要查看投票,那么我需要发送以下信息:

预期结果:

{  
  "my_vote" : 2,           // user with id 5 voted on option with id 2
  "options" : [ 
               {   
                 "_id" : 1,  
                 "option" : "A",  
                 "votes" : 3              //num of votes on option "A"
               },
               {   
                 "_id" : 2,  
                 "option" : "B",  
                 "votes" : 1             //num of votes on option "B"
               },
               {   
                 "_id" : 3,  
                 "option" : "C",  
                 "votes" : 0            //num of votes on option "C"
               }
             ]   
}

2 个答案:

答案 0 :(得分:1)

既然问题是你实际上在当前的接受答案中并没有真正提供这个问题,而且还有一些不必要的东西,还有另外一种方法:

var userId = 5; // A variable to work into the submitted pipeline

db.sample.aggregate([
    { "$unwind": "$options" },
    { "$group": {
        "_id": "$_id",
        "my_vote": { "$min": {
            "$cond": [
                { "$setIsSubset": [ [userId], "$options.votes" ] },
                "$options._id",
                false
            ]
        }},
        "options": { "$push": {
            "_id": "$options._id",
            "option": "$options.option",
            "votes": { "$size": "$options.votes" }
        }}
    }}
])

当然,这会为您提供每个文档的输出:

{
    "_id" : ObjectId("5573a0a8b67e246aba2b4b6e"),
    "my_vote" : 2,
    "options" : [
            {
                    "_id" : 1,
                    "option" : "A",
                    "votes" : 3
            },
            {
                    "_id" : 2,
                    "option" : "B",
                    "votes" : 1
            },
            {
                    "_id" : 3,
                    "option" : "C",
                    "votes" : 0
            }
    ]
}

所以你在这里做的是使用$unwind来分解数组以进行检查。以下$group阶段(以及您需要的唯一其他阶段)使用$min$push运算符进行重构。

在每个操作中,$cond操作通过$setIsSubset测试数组内容,并返回匹配的_id值或false。在重构内部数组元素时,指定所有元素而不仅仅是$push参数中的顶级文档,并使用$size运算符来计算数组中的元素。

您还提到了另一个关于使用$unwind处理空数组的问题的链接。这里的$size运算符将执行正确的操作,因此在$unwind中不需要$unwind并且在这种情况下投影“虚拟”值,其中数组为空。

请注意,除非您实际上跨文档“聚合”,否则通常会建议您在客户端代码而不是聚合框架中执行此操作。使用db.sample.aggregate([ { "$project": { "my_vote": { "$setDifference": [ { "$map": { "input": "$options", "as": "o", "in": { "$cond": [ { "$setIsSubset": [ [userId], "$$o.votes" ] }, "$$o._id", false ]} }}, [false] ] }, "options": { "$map": { "input": "$options", "as": "o", "in": { "_id": "$$o._id", "option": "$$o.option", "votes": { "$size": "$$o.votes" } } }} }} ]) 有效地在聚合管道中为每个文档中包含的数组的每个元素创建一个新文档,这会产生很大的开销。

对于仅对不同文档起作用的此类操作,客户端代码更有效地单独处理每个文档。

如果你真的必须坚持服务器处理是这样做的方式,那么使用$map代替这可能是最有效的:

my_vote

所以这只是“预测”每个文档的重复结果。 { "_id" : ObjectId("5573a0a8b67e246aba2b4b6e"), "options" : [ { "_id" : 1, "option" : "A", "votes" : 3 }, { "_id" : 2, "option" : "B", "votes" : 1 }, { "_id" : 3, "option" : "C", "votes" : 0 } ], "my_vote" : [ 2 ] } 虽然不一样,因为它是单个元素数组(或者可能是多个匹配),聚合框架缺少运算符以减少非数组元素而没有进一步的开销:

06/12/2015 12:00 PM

答案 1 :(得分:0)

Check out this question

这不是问同样的事情,但是如果没有多个查询,就没有办法做你要求的事情。 我会修改您直接返回的JSON ,因为您只是显示已包含在查询结果中的额外信息。

  1. 保存您要查询的userID
  2. 获取查询结果(对象中的选项数组),搜索数组中每个元素的votes
  3. 如果您找到了正确的投票,请附上_id(如果您没有找到投票,可以添加'n / a')。
  4. 编写一个执行2和3的函数,然后只需将userID传递给它,然后返回一个附加了myVote的新对象。

    我认为这样做不会比在Mongoose中做另一个查询慢。