gpt4 book ai didi

node.js - 如何使用嵌套数组文档进行 Mongoose 聚合

转载 作者:太空宇宙 更新时间:2023-11-04 00:54:22 25 4
gpt4 key购买 nike

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

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

假设我在 Node js中有我想要向其发送此信息的用户的userId。我的任务是

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

作为

"myVote" : option._id

我需要找到选项._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"
}
]
}

最佳答案

由于您实际提出的问题在当前接受答案中并未真正提供,而且它做了一些不必要的事情,因此还有另一种方法:

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 可以有效地在聚合管道中为每个文档中包含的数组的每个元素创建一个新文档,这会产生大量开销。

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

<小时/>

如果您确实必须坚持服务器处理是执行此操作的方法,那么使用 $map 可能是最有效的。相反:

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" }
}
}}
}}
])

所以这只是“投影”每个文档的重新处理结果。但 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
]
}

关于node.js - 如何使用嵌套数组文档进行 Mongoose 聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30683354/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com