您可以在单个聚合管道中按ID查找多个文档吗?

时间:2018-05-16 22:23:27

标签: mongodb mongoose aggregation-framework

我正在编写用户详细信息API视图,并希望添加is_following标记,以指示经过身份验证的API用户(用户A )是否关注了&# 39;他们正在查看的个人资料(用户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
  2. 匹配经过身份验证的API用户(用户A
  3. 以某种方式将用户A following数组存储在汇总管道中,以便以后用于我们希望查看的用户
  4. 再次与其他用户匹配,用户A 有兴趣查看的用户(用户B ),_id
  5. 使用投影,附加一个新的布尔字段is_following,表示用户B _id是否在经过身份验证的API用户中(用户A )我们之前存储的following数组。即登录用户正在关注他们当前正在查看的用户的个人资料。
  6. 我不确定的第2步和第3步,我不确定是否可以在单个聚合查询中实现抓取具有不同$match条件的两个文档的方法

    编辑:添加预期输出

    如果经过身份验证的用户(用户A )为_id 2,则已查看的用户用户B _id 1:< / p>

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

    如果经过身份验证的用户(用户A )为_id 2,并且已查看的用户用户B _id 3:

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

1 个答案:

答案 0 :(得分:0)

在您的问题中不是100%明确,但您似乎在查找相同数据的简单$lookup,并与结果用户进行比较。您可能实际上一次只希望这个用户,因此该用户的$match通常是&#34;选项&#34;这里。

除了标准$lookup结果之外,其他内容并不多,然后使用$map与&#34; parent&#34;进行比较。 _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" ]
            }}
          ]
        }
      }
    }
  }}
])

对于早期版本,您只需要明确命名字段&#34;&#34;在您要返回的$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适用于此类比较。一个比较一个值与一个数组和另一个&#34;两个&#34;阵列。

或者,您甚至无法访问$lookup.populate(),然后只需&#34;重新映射&#34;数组内容的方式大致相同:

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()方法进行比较。请注意,Array.includes()无法在此处应用,而不会将ObjectId值重新映射到字符串,因为&#34;对象比较&#34;的工作原理。

此处的所有表格都会得出相同的结果:

{
        "_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
                }
        ]
}