【问题标题】:add fields where condition match to nested array将条件匹配的字段添加到嵌套数组
【发布时间】:2018-10-27 08:18:10
【问题描述】:

我有关注users 收藏

[{
    "_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
    "firstName" : "bruce",
    "friends" : [ ObjectId("5afd1c42af18d985a06ac306"),ObjectId("5afd257daf18d985a06ac6ac") ]
},
{
    "_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
    "firstName" : "clerk",
    "friends" : [],
}]

并拥有friends 收藏

[{
    "_id" : ObjectId("5afd1c42af18d985a06ac306"),
    "recipient" : ObjectId("5afaab572c4ec049aeb0bcba"),
    "requester" : ObjectId("5afadfdf08a7aa6f1a27d986"),
    "status" : 2,
},
{
    "_id" : ObjectId("5afd257daf18d985a06ac6ac"),
    "recipient" : ObjectId("5afadfdf08a7aa6f1a27d986"),
    "requester" : ObjectId("5afbfe21daf4b13ddde07dbe"),
    "status" : 1,
}]

假设我有一个用户使用_id: "5afaab572c4ec049aeb0bcba" 登录并且这个_id 匹配friendsrecipient

现在我必须添加一个字段friendsStatus,其中包含来自friends 集合的status...如果不匹配数组中的任何recipient,那么它的状态应该是0

所以当我得到所有用户时,我的输出应该是

[{
        "_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
        "firstName" : "bruce",
        "friends" : [ ObjectId("5afd1c42af18d985a06ac306") ],
        "friendStatus": 2
},
{
        "_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
        "firstName" : "clerk",
        "friends" : [],
        "friendStatus": 0
}]

提前致谢!!!

【问题讨论】:

    标签: node.js mongodb mongoose mongodb-query aggregation-framework


    【解决方案1】:

    如果您有 MongoDB 3.6,那么您可以将 $lookup 与“子管道”一起使用

    User.aggregate([
      { "$lookup": {
        "from": Friend.collection.name,
        "let": { "friends": "$friends" },
        "pipeline": [
          { "$match": {
            "recipient": ObjectId("5afaab572c4ec049aeb0bcba"),
            "$expr": { "$in": [ "$_id", "$$friends" ] }
          }},
          { "$project": { "status": 1 } }
        ],
        "as": "friends"
      }},
      { "$addFields": {
        "friends": {
          "$map": {
            "input": "$friends",
            "in": "$$this._id"
          }
        },
        "friendsStatus": {
          "$ifNull": [ { "$min": "$friends.status" }, 0 ]
        }
      }}
    ])
    

    对于早期版本,最好实际使用$unwind,以确保您不会违反BSON Limit

    User.aggregate([
      { "$lookup": {
        "from": Friend.collection.name,
        "localField": "friends",
        "foreignField": "_id",
        "as": "friends"
      }},
      { "$unwind": { "path": "$friends", "preserveNullAndEmptyArrays": true } },
      { "$match": {
        "$or": [
          { "friends.recipient": ObjectId("5afaab572c4ec049aeb0bcba") },
          { "friends": null }
        ]
      }},
      { "$group": {
        "_id": "$_id",
        "firstName": { "$first": "$firstName" },
        "friends": { "$push": "$friends._id" },
        "friendsStatus": {
          "$min": { 
            "$ifNull": ["$friends.status",0]
          }
        }
      }}
    ])
    

    这里与最最佳形式存在“一个区别”,因为管道优化实际上并未将$match 条件“汇总”到@ 987654325@自己:

    {
      "$lookup" : {
        "from" : "friends",
        "as" : "friends",
        "localField" : "friends",
        "foreignField" : "_id",
        "unwinding" : {
          "preserveNullAndEmptyArrays" : true
        }
      }
    },
    {
      "$match" : {   // <-- outside will preserved array
    

    由于preserveNullAndEmptyArrays 选项为true,因此“完全优化” 操作将条件实际应用于外部集合“之前” 结果不会被退回。

    因此,unwinding 的唯一目的纯粹是为了避免 $lookup 结果中的通常目标“数组”导致父文档增长超出 BSON 限制。然后在此阶段“之后”应用$match 的附加条件。默认$unwind 不带选项假定false 用于保存,并添加matching 条件来代替执行此操作。这当然会导致没有外部匹配的文档被排除在外。

    因为BSON Limit 并不是真正可取的,但也有将$filter 应用于$lookup 的结果数组:

    User.aggregate([
      { "$lookup": {
        "from": Friend.collection.name,
        "localField": "friends",
        "foreignField": "_id",
        "as": "friends"
      }},
      { "$addFields": {
        "friends": {
          "$map": {
            "input": {
              "$filter": {
                "input": "$friends",
                "cond": {
                  "$eq": [
                    "$$this.recipient",
                    ObjectId("5afaab572c4ec049aeb0bcba")
                  ]
                }
              }
            },
            "in": "$$this._id"
          }
        },
        "friendsStatus": {
          "$ifNull": [
            { "$min": {
              "$map": {
                "input": {
                  "$filter": {
                    "input": "$friends",
                    "cond": {
                       "$eq": [
                         "$$this.recipient",
                          ObjectId("5afaab572c4ec049aeb0bcba")
                       ]
                    }
                  }
                },
                "in": "$$this.status"
              }
            }},
            0
          ]
        }
      }}
    ])
    

    在任何一种情况下,我们基本上都将“附加条件”添加到联接中,不仅是在直接相关的字段上,而且还具有查询的 ObjectId 值对 "recipient" 的附加约束。

    不太确定您对"friendsStatus" 的期望是什么,因为结果是一个数组并且可能不止一个(据我所知),因此只需在此处应用$min 以从数组中提取一个值无论哪种情况。

    每种情况下的控制条件是$ifNull,它适用于"friends" 输出数组中没有任何内容可供提取的情况,然后您只需返回0 的结果即可。

    所有输出都一样:

    {
            "_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
            "firstName" : "bruce",
            "friends" : [
                    ObjectId("5afd1c42af18d985a06ac306")
            ],
            "friendsStatus" : 2
    }
    {
            "_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
            "firstName" : "clerk",
            "friends" : [ ],
            "friendsStatus" : 0
    }
    

    【讨论】:

    猜你喜欢
    • 2016-10-15
    • 1970-01-01
    • 2018-11-21
    • 2021-10-29
    • 2019-07-05
    • 1970-01-01
    • 2022-07-02
    • 1970-01-01
    • 2019-02-11
    相关资源
    最近更新 更多