我有一个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._idoptions [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"
}
]
}
答案 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)
这不是问同样的事情,但是如果没有多个查询,就没有办法做你要求的事情。 我会修改您直接返回的JSON ,因为您只是显示已包含在查询结果中的额外信息。
userID
。votes
。_id
(如果您没有找到投票,可以添加'n / a')。编写一个执行2和3的函数,然后只需将userID
传递给它,然后返回一个附加了myVote
的新对象。
我认为这样做不会比在Mongoose中做另一个查询慢。