mongoose模式中的Join/lookup聚合

3

我目前正在制作一个成就系统,尝试将其保存在两个集合中。 一个包含名称和ID的集合, 另一个包含用户及其进展的集合。

我一直在研究Aggregation和$lookup,并取得了一些进展,但我不确定需要做什么才能实现我想要的输出。

如果用户存在于成就中,则获取进度值和其用户ID,如果不存在,则将进度值设置为0。

这不是类似于Mysql中的连接吗?在MongoDB中该怎么做呢?

变量userid = 33;

尝试:

db.getCollection('achievements').aggregate([
   {
     $lookup:
       {
         from: "achievement_users",
         localField: "id",
         foreignField: "id",
         as: "inventory_docs"
       }
  }
])

成就架构:

{
    "name" : "testing",
    "id" : 1,
    "max" : 12,
},

{
    "name" : "testing2",
    "id" : 2,
    "max" : 12,

}

成就用户:

{
    "userid" : 33,
    "id" : 1,
    progress: 1,
},
 {
        "userid" : 446,
        "id" : 1,
        progress: 1,
    }

期望输出:

{
name: "testing",
id: 1,
userid: 33,
progress: 1,
    max : 12,

},
{
name: "testing2",
id: 2,
progress: 0,
    max : 12,

},

编辑:

我需要用户已经完成的所有成就,以及未完成的成就(未完成=进度=0)。

可能是用户尚未完成的成就。我也想列出它们,但进度值为0。

更新2:在achievement_users中没有与特定userid匹配的结果时,还需要它给我提供结果。

2个回答

2

如果你想执行类似于LEFT JOIN的操作,这里有一个解决方案:

最初的回答:

db.test.aggregate([{
    $lookup: {
        from: "user",
        localField: "id",
        foreignField: "id",
        as: "inventory_docs"
    }
}, {
    $unwind: {path: "$inventory_docs", preserveNullAndEmptyArrays: true }
}, {
    $addFields: {
        userid: "$inventory_docs.userid",
        progress: {$cond: {if: "$inventory_docs.progress", then: "$inventory_docs.progress", else: 0 }},
    }
}, {
    $project: {
        inventory_docs: 0
    }
}])

$lookup 从连接的集合创建一个数组

$unwind 将数组拆分为记录

$addFields 按您的要求提取所需字段

$cond 将空进度替换为0

$project 进行最终的清理


注:$lookup, $unwind, $addFields, $cond和$project是MongoDB中的聚合管道操作符。

谢谢!我能得到只有用户ID匹配的每个结果吗?而不是所有人? :) - maria
使用时,在管道的开头添加$match {$match: {id: {$in: ['1', '2']}}} - Julien TASSIN
只获取用户行中userid = 33的结果,将仅显示该用户的成就(已完成和未完成)。 - maria
因为我不知道当我只有用户集合中的用户ID时,匹配会是什么样子。 - maria
同时@Julien,我可以从用户集合中反向获取它,这样就可以匹配成就了,但是这样就无法获得我已经完成的成就。有什么好的解决办法吗? :) - maria

1
您可以使用以下聚合。
db.achievement_users.aggregate([
  { "$match": { "userid": 33 }},
  { "$lookup": {
    "from": "achievement",
    "let": { "id": "$id", "progress": "$progress", "userid": "$userid" },
    "pipeline": [
      { "$addFields": {
        "progress": { "$cond": [{ "$eq": ["$id", "$$id"] }, "$$progress", 0] },
        "userid": "$$userid"
      }}
    ],
    "as": "inventory_docs"
  }},
  { "$unwind": "$inventory_docs" },
  { "$replaceRoot": { "newRoot": "$inventory_docs" }}
])

我已经从 user_acheivement 集合开始了聚合。请查看答案。 - Ashh
同时@anthony,如果用户没有成就(进度值为0),我需要它们。因此,我需要它已经完成的所有内容以及未完成的内容(任何行)。 - maria
在这里您可以检查输出结果。https://mongoplayground.net/p/mKgKNZNdVFA。请告知问题所在。 - Ashh
抱歉,我不明白你的意思。你应该更具体地描述你的问题。 - Ashh
1
希望我对那个问题的评论能够帮到你。 - Ashh
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接