如何 $filter where 条件与数组匹配?

aru*_*414 3 mongodb mongodb-query aggregation-framework

在 $lookup 之后,我将得到一个名为 lastViewed 的连接,如下所示:

{
  "_id" : "5955ea4fd8099718330ab191"

    lastViewed: [
        {
           "_id" : ObjectId("595218a7d346d27fb0bc1705"),
           "userId" : ObjectId("58c796d4344b9da4dfbe027b"),
           "groupId" : ObjectId("5955ea4fd8099718330ab191"),
           "lastViewedTime" : ISODate("2017-06-19T09:39:07.374Z")
        },
        {
           "_id" : ObjectId("595218a7d346d27fb0bc1764"),
           "userId" : ObjectId("58c796d4344b9da4dfbe027b"),
           "groupId" : ObjectId("5955ea4fd8099718330ab162"),
          "lastViewedTime" : ISODate("2017-05-11T09:39:07.374Z")
        }
      ]
}
Run Code Online (Sandbox Code Playgroud)

我想使用 $filter 然后使用 $project 值,我的查询位于从 params 获取 userId 的位置下方

$project: { "viewedDetails": {
                        "$filter": {
                            "input": "$lastViewed",
                            "as": "lastViewed",
                            "cond": { $and: [{ "$eq": ["$$lastViewed.userId", mongoose.Types.ObjectId(userId)] }, { "$eq": ["$$lastViewed.groupId", '$_id'] }] }
                        }
                    }
                }
Run Code Online (Sandbox Code Playgroud)

我得到了 userId 58c796d4344b9da4dfbe027b 的确切输出,如下所示

{
  "_id" : "5955ea4fd8099718330ab191"

    viewedDetails: [
        {
           "_id" : ObjectId("595218a7d346d27fb0bc1705"),
           "userId" : ObjectId("58c796d4344b9da4dfbe027b"),
           "groupId" : ObjectId("5955ea4fd8099718330ab191"),
           "lastViewedTime" : ISODate("2017-06-19T09:39:07.374Z")
        }
      ]
}
Run Code Online (Sandbox Code Playgroud)

现在我将模式更改为并得到 $lookup 结果为

{
  "_id" : "5955ea4fd8099718330ab191"

    lastViewed: [
        {
           "_id" : ObjectId("595218a7d346d27fb0bc1705"),
           "userId" : ObjectId("58c796d4344b9da4dfbe027b"),
           "members":[
                  {
                    "groupId" : ObjectId("5955ea4fd8099718330ab162"),
                    "lastViewedTime" : ISODate("2017-05-11T09:39:07.374Z")
                  }
                  {
                    "groupId" : ObjectId("5955ea4fd8099718330ab191"),
                    "lastViewedTime" : ISODate("2016-05-19T09:39:07.374Z")
                  }
              ]               
        }
      ]
}
Run Code Online (Sandbox Code Playgroud)

那么我的对象数组过滤器不起作用,我的查询是

 $project: { "viewedDetails": {
                        "$filter": {
                            "input": "$lastViewed",
                            "as": "lastViewed",
                            "cond": { $and: [{ "$eq": ["$$lastViewed.userId", mongoose.Types.ObjectId(userId)] }, 
                                             { "$eq": ["$$lastViewed.members.groupId", '$_id'] }] }
                        }
                    }
                },
Run Code Online (Sandbox Code Playgroud)

我怎样才能在不使用 $unwind 然后使用 $project 的情况下得到这个?

Nei*_*unn 8

不需要$unwind纯粹为了比较,因为有多种运算符可以通过与列表比较来确定逻辑结果。你真的只需要改变cond这里的$filter

过滤“成员”需要包装$map以反映更改:

对于 MongoDB 3.4,您可以使用$in

"viewedDetails": {
  "$map": {
    "input": {
      "$filter": {
        "input": "$lastViewed",
        "as": "lastViewed",
        "cond": { 
          "$and": [
            { "$eq": ["$$lastViewed.userId", mongoose.Types.ObjectId(userId)] }, 
            { "$in": [ "$_id", "$$lastViewed.members.groupId" ] }
          ]
        }
      }
    },
    "as": "v",
    "in": {
      "_id": "$$v._id",
      "userId": "$$v.userId",
      "members": { 
        "$filter": {
          "input": "$$v.members",
          "as": "m",
          "cond": { "$eq": [ "$$m.groupId", "$_id" ] }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

它将值与返回的数组中的每个值进行比较,"$$lastViewed.members.groupId"并返回trueorfalse取决于它是否匹配。

如果无法使用$in,则只需将初始的$filter更改为以下之一:

早于tat使用$setIsSubset

   "cond": { 
     "$and": [
       { "$eq": ["$$lastViewed.userId", mongoose.Types.ObjectId(userId)] }, 
       { "$setIsSubset": [ ["$_id"], "$$lastViewed.members.groupId" ] }
     ]
   }
Run Code Online (Sandbox Code Playgroud)

甚至$setIntersection$size

   "cond": { 
     "$and": [
       { "$eq": ["$$lastViewed.userId", mongoose.Types.ObjectId(userId)] }, 
       { "$gt": [ 
         { "$size": { 
           "$setIntersection": [
             ["$_id"], 
             "$$lastViewed.members.groupId"
           ]
         }},
         0
       ] }
     ]
   }
Run Code Online (Sandbox Code Playgroud)

导致具有“多个”0元素的“集合”的“交集”意味着该值存在于同一引用数组中。

请注意,在这些形式中,我们将"$_id"值设置为数组,["$_id"]因为“集合比较”是在“集合”之间而不是单个字段之间。


证明

这是您提供的包含语法更正的文档:

{
    "_id" : ObjectId("5955ea4fd8099718330ab191"),
    "lastViewed" : [ 
        {
            "_id" : ObjectId("595218a7d346d27fb0bc1705"),
            "userId" : ObjectId("58c796d4344b9da4dfbe027b"),
            "members" : [ 
                {
                    "groupId" : ObjectId("5955ea4fd8099718330ab162"),
                    "lastViewedTime" : ISODate("2017-05-11T09:39:07.374Z")
                }, 
                {
                    "groupId" : ObjectId("5955ea4fd8099718330ab191"),
                    "lastViewedTime" : ISODate("2016-05-19T09:39:07.374Z")
                }
            ]
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

这是正在运行的管道阶段:

db.collection.aggregate([
  { "$project": {
    "viewedDetails": {
      "$map": {
        "input": {
          "$filter": {
            "input": "$lastViewed",
            "as": "lastViewed",
            "cond": { 
              "$and": [
                { "$eq": [ "$$lastViewed.userId", ObjectId("58c796d4344b9da4dfbe027b") ] }, 
                { "$in": [ "$_id", "$$lastViewed.members.groupId" ] }
              ]
            }
          }
        },
        "as": "v",
        "in": {
          "_id": "$$v._id",
          "userId": "$$v.userId",
          "members": { 
            "$filter": {
              "input": "$$v.members",
              "as": "m",
              "cond": { "$eq": [ "$$m.groupId", "$_id" ] }
            }
          }
        }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

这是输出:

{
    "_id" : ObjectId("5955ea4fd8099718330ab191"),
    "viewedDetails" : [ 
        {
            "_id" : ObjectId("595218a7d346d27fb0bc1705"),
            "userId" : ObjectId("58c796d4344b9da4dfbe027b"),
            "members" : [ 
                {
                    "groupId" : ObjectId("5955ea4fd8099718330ab191"),
                    "lastViewedTime" : ISODate("2016-05-19T09:39:07.374Z")
                }
            ]
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)