【问题标题】:Can you lookup multiple documents by id within a single aggregation pipeline?您可以在单个聚合管道中按 id 查找多个文档吗?
【发布时间】:2018-05-17 10:35:20
【问题描述】:

我正在编写一个用户详细信息 API 视图,并希望添加一个 is_following 标志来指示经过身份验证的 API 用户(用户 A)是否正在关注他们正在查看的个人资料的用户( 用户 B)。

在请求时,我只有经过身份验证的 API 用户的 _id。我可以在一次查询中为经过身份验证的 API 用户获取 following 字段,然后继续为他们有兴趣查看的用户进行第二次查询,但我认为将这项工作传递给数据库会更有效在单个聚合中(如果可能)。

假设我有 users 集合的以下架构:

[
    {
        _id: 1,
        name: 'David',
        following: [2, 3] // David follows Sam and Lucy
    },
    {
        _id: 2,
        name: 'Sam',
        following: [1] // Sam only follows David
    },
    {
        _id: 3,
        name: 'Lucy',
        following: [1, 2] // Lucy follows David and Sam
    },
]

我想知道是否可以按照我目前想到的方式编写聚合:

  1. 匹配_id 验证的 API 用户(用户 A
  2. 以某种方式将 用户 Afollowing 数组存储在聚合管道中,以便以后针对我们希望查看的用户使用
  3. 通过_id 再次匹配其他用户,即用户 A 有兴趣查看的用户(用户 B
  4. 使用投影,追加一个新的布尔字段is_following,表示用户B_id是否在经过身份验证的API用户(用户A)的following 我们之前存储的数组。即登录的用户正在关注他们当前正在查看的个人资料的用户。

这是我不确定的第 2 步和第 3 步,我不确定这种抓取具有不同 $match 标准的两个文档的方法是否可以在单个聚合查询中实现。

编辑:添加预期输出

如果认证用户(user A)是_id2,查看用户user B_id1:

{
    _id: 1,
    name: 'David',
    following: [2, 3],
    is_following: true
}

如果认证用户(user A)是_id2,而被查看的用户user B_id3:

{
    _id: 3,
    name: 'Lucy',
    following: [1, 2],
    is_following: false
}

【问题讨论】:

  • 不太清楚你在问什么。也许您实际上应该根据提供的数据样本(或者如果您认为它仍然适用,则为当前),通过演示来展示您期望的结果类型。有些东西“看起来像”,但显示您对请求的预期结果会使其更加清晰。另外请不要使用ObjectId("1")之类的东西,这是无效的数据,不能被试图在演示中解决的人使用。要么只显示一个真实的ObjectId 值,要么只使用其他有效的值。比如_id: 1代替。
  • @NeilLunn 关于 ObjectId 的好点。我添加了几个所需输出的示例,希望能让这个问题更容易理解。
  • 所以我对你的预期输出有点困惑,因为它似乎颠倒了。您认为这些结果属于哪个用户( A 或 B )?只看最后一个 3 跟随 1 和 2,因此 1 是 3 的跟随者,而 2 不是。您不想让每个潜在关注者上的is_following 查看他们是否关注“父”用户吗?或不?奇怪的是,您在该人实际上在跟随另一个人的情况下给出了 3 的错误输出。不跟随他们的是“其他人”。
  • 只是给你一个答案。看看它,如果你真的认为这个过程有什么不对的地方,那就告诉我。据我所知,您所问的逻辑模式是关注者也在关注用户,这就是结果所表明的。
  • @NeilLunn 感谢您的回答,并为您的困惑感到抱歉!集合中的每个用户都持有他们关注的用户,而不是关注他们的用户。我正在尝试确定请求并最终输出的用户(用户 B)是否被经过身份验证的 API 用户(用户 A)跟踪。用户 B 可能会关注用户 A,但只有当用户 A 关注用户 B 时,is_following 字段才应设置为 true,因为该字段旨在从经过身份验证的用户的角度来看。 “我在跟踪这个人吗?”,而不是“他们在跟踪我吗?”希望这有助于解决问题:)

标签: mongodb mongoose aggregation-framework


【解决方案1】:

您的问题不是 100% 清楚,但您似乎正在寻找一个简单的 $lookup 与结果用户的比较相同的数据。您可能真的一次只希望单个用户使用此功能,因此该用户的 $match 通常是此处的“选项”。

除了标准的$lookup结果,然后使用$map与“父”_id进行比较并将新属性添加到数组中之外,它并没有太多。

最好使用 MongoDB 3.6,您可以申请 $mergeObjects:

Person.aggregate([
  // { "$match": { "_id": 3 } },
  { "$lookup": {
    "from": Person.collection.name,
    "localField": "following",
    "foreignField": "_id",
    "as": "following"
  }},
  { "$addFields": {
    "following": {
      "$map": {
        "input": "$following",
        "in": {
          "$mergeObjects": [
            "$$this",
            { "is_following": {
              "$in": [ "$_id", "$$this.following" ]
            }}
          ]
        }
      }
    }
  }}
])

对于早期版本,您只需要在 $map 中“明确”地命名要返回的字段即可:

Person.aggregate([
  // { "$match": { "_id": 3 } },
  { "$lookup": {
    "from": Person.collection.name,
    "localField": "following",
    "foreignField": "_id",
    "as": "following"
  }},
  { "$project": {
    "_id": 1,
    "name": 1,
    "following": {
      "$map": {
        "input": "$following",
        "in": {
          "_id": "$$this._id",
          "name": "$$this.name",
          "following": "$$this.following",
          "is_following": { "$setIsSubset": [["$_id"], "$$this.following"] }
        }
      }
    }
  }}
])

基本情况是每个元素都由$map 检查,您可以比较他们自己的"followers" 数组和当前用户_id 的值。 $in$setIsSubset 都适用于这种比较。一个比较一个值与一个数组,另一个比较“两个”数组。

在您甚至无法访问$lookup 的情况下,您可以使用.populate(),然后以几乎相同的方式简单地“重新映射”数组内容:

let people = await Person.find().lean().populate('following');

people = people.map(p => ({ 
  ...p,
  following: p.following.map(f => ({
    ...f,
    is_following: f.following.some(e => e._id.equals(p._id))
  }))
}));

使用Array.some()ObjectID.equals() 方法来进行比较。请注意,由于“对象比较”的工作原理,如果不将 ObjectId 值重新映射到字符串,Array.includes() 将无法在此处应用。

这里的所有表格都得出相同的结果:

{
        "_id" : 1,
        "name" : "David",
        "following" : [
                {
                        "_id" : 2,
                        "name" : "Sam",
                        "following" : [
                                1
                        ],
                        "is_following" : true
                },
                {
                        "_id" : 3,
                        "name" : "Lucy",
                        "following" : [
                                1,
                                2
                        ],
                        "is_following" : true
                }
        ]
}
{
        "_id" : 2,
        "name" : "Sam",
        "following" : [
                {
                        "_id" : 1,
                        "name" : "David",
                        "following" : [
                                2,
                                3
                        ],
                        "is_following" : true
                }
        ]
}
{
        "_id" : 3,
        "name" : "Lucy",
        "following" : [
                {
                        "_id" : 1,
                        "name" : "David",
                        "following" : [
                                2,
                                3
                        ],
                        "is_following" : true
                },
                {
                        "_id" : 2,
                        "name" : "Sam",
                        "following" : [
                                1
                        ],
                        "is_following" : false
                }
        ]
}

【讨论】:

    猜你喜欢
    • 2021-01-30
    • 1970-01-01
    • 2019-05-23
    • 2021-09-18
    • 1970-01-01
    • 2020-11-10
    • 2023-04-02
    • 1970-01-01
    • 2020-08-31
    相关资源
    最近更新 更多