仅在本地字段存在时执行条件查找?

时间:2017-05-29 11:15:55

标签: mongodb mongodb-query aggregation-framework

我有一个集合,其中包含两种类型的文档。基于我想要执行查找聚合的文档类型。

Action是一个集合,包含对话和消息类型的操作。我想根据MongoDB中的字段以不同方式执行对话和消息的查找。

会话类型的操作文档如下

{ 
    "_id" : ObjectId("592bdeaf45c7201421793871"), 
    "actionType" : "conversation", 
    "created" : NumberInt(1496047280), 
    "users" : [
        ObjectId("590c53a85fba594a59fe3d0f"), 
        ObjectId("590c50175df715499129e41b")
    ], 
    "conversationId" : ObjectId("592bdeaf45c7201421793870"), 
    "missionId" : ObjectId("590c50fa5df715499129e41c")
}

消息的行动文件

{ 
    "_id" : ObjectId("592bdeaf45c7201421793871"), 
    "actionType" : "message", 
    "created" : NumberInt(1496047280), 
    "messageId" : ObjectId("592bdeaf45c7201421793870")
}

我希望在特定时间之后执行操作,并希望执行查找以从对象ID获取对话和消息数据。

我试过这个

let matchQuery = {
          users : new ObjectId(userId),
          created:
            {
              $gt : createdTime
            }};

       let aggregation = [
          {
            $match : matchQuery
          },
          {$cond : 
            {if:
              {
                $users:{
                  $exists:true
                }
            }, 
          then: {
            $unwind : "$users",
            $lookup: {
                 from : "users",
                 localfield:"$users",
                 foreignfield:"_id",
                 as:"userDetail"
                       },
            $group:{
                  "users" : { $push : "userDetail"}
                    }
                  }
              }
            },
            {$cond : 
            {if:
              {
                $conversationId:{
                  $exists:true
                }
            }, 
          then: {
           $lookup : {
                            from:"conversations",
                            localfield:"conversationId",
                            foreignfield:"_id",
                            as:"conversationDetail"
                          }
                  }
              }
            },
            {$cond : 
            {if:
              {
                $missionId:{
                  $exists:true
                }
            }, 
          then: {
           $lookup : {
                            from:"services_v2",
                            localfield:"missionId",
                            foreignfield:"_id",
                            as:"missionDetail"
                          }
                  }
              }
            },
            {$cond : 
            {if:
              {
                $messageId:{
                  $exists:true
                }
            }, 
          then: {
           $lookup : {
                            from:"messagev2",
                              localfield:"messageId",
                              foreignfield:"_id",
                              as:"messageDetail"
                          }
                  }
              }
            },
          {
            $project : {
              "_id" : 1,
              "actionType" : 1,
              "userDetail":1,
              "conversationDetail":1,
              "missionDetail":1,
              "messageDetail":1
}}
        ];

connection.collection('actions')
.aggregate(aggregation).toArray((err,result)=> {  
    if(err){
      console.log(err);
    }
    console.log(result);
 })
};

1 个答案:

答案 0 :(得分:7)

我认为你过分思考这一点并且你不需要进行条件查找"并且它可能最好以示例来证明。

将这些文档放在单独的集合中,首先是对话:

> db.conversation.find()
{ "_id" : ObjectId("592ccbf8fceb6b40e6489759"), "message" : "I'm here" }

然后是消息集:

> db.message.find()
{ "_id" : ObjectId("592ccc0bfceb6b40e648975a"), "text" : "Something here" }

然后我有一个主集合,它在不同的文档中引用了每个文档:

> db.master.find()
{ 
  "_id" : ObjectId("592ccc73fceb6b40e648975b"),
  "a" : 1, 
  "conversation" : ObjectId("592ccbf8fceb6b40e6489759")
}
{ 
  "_id" : ObjectId("592ccc95fceb6b40e648975c"),
  "a" : 2,
  "message" : ObjectId("592ccc0bfceb6b40e648975a")
}

现在,如果我执行$lookup操作(这有点类似于"左连接"):

db.master.aggregate([
  { "$lookup": {
    "from": "conversation",
    "localField": "conversation",
    "foreignField": "_id",
    "as": "conversation"
  }}
])

然后我得到一个这样的结果,当然在文档上投射一个没有"localField"匹配的空数组:

{
    "_id" : ObjectId("592ccc73fceb6b40e648975b"),
    "a" : 1,
    "conversation" : [
        {
            "_id" : ObjectId("592ccbf8fceb6b40e6489759"),
            "message" : "I'm here"
        }
    ]
}
{
    "_id" : ObjectId("592ccc95fceb6b40e648975c"),
    "a" : 2,
    "message" : ObjectId("592ccc0bfceb6b40e648975a"),
    "conversation" : [ ]
}

如果我现在添加到管道a" second"链接到邮件集合的$lookup操作:

db.master.aggregate([
  { "$lookup": {
    "from": "conversation",
    "localField": "conversation",
    "foreignField": "_id",
    "as": "conversation"
  }},
  { "$lookup": {
    "from": "message",
    "localField": "message",
    "foreignField": "_id",
    "as": "message"
  }}
])

然后我们看到类似的效果,其中没有属性的文档现在有一个空数组,但是属性确实存在的地方我们现在有来自特定集合的条目:

{
    "_id" : ObjectId("592ccc73fceb6b40e648975b"),
    "a" : 1,
    "conversation" : [
        {
            "_id" : ObjectId("592ccbf8fceb6b40e6489759"),
            "message" : "I'm here"
        }
    ],
    "message" : [ ]
}
{
    "_id" : ObjectId("592ccc95fceb6b40e648975c"),
    "a" : 2,
    "message" : [
        {
            "_id" : ObjectId("592ccc0bfceb6b40e648975a"),
            "text" : "Something here"
        }
    ],
    "conversation" : [ ]
}

您可以保留原样(这似乎是您目前所尝试的最终状态),或者现在使用它进行其他操作,例如合并到一个数组中:

db.master.aggregate([
  { "$lookup": {
    "from": "conversation",
    "localField": "conversation",
    "foreignField": "_id",
    "as": "conversation"
  }},
  { "$lookup": {
    "from": "message",
    "localField": "message",
    "foreignField": "_id",
    "as": "message"
  }},
  { "$project": {
    "a": 1,
    "combined": {
      "$concatArrays": [
        { "$map": {
          "input": "$conversation",
          "as": "el",
          "in": {
            "type": "conversation",
            "_id": "$$el._id",
            "message": "$$el.message"
          }
        }},
        { "$map": {
          "input": "$message",
          "as": "el",
          "in": {
            "type": "message",
            "_id": "$$el._id",
            "text": "$$el.text"
          }
        }}
      ]
    }
  }}
])

输出为:

{
    "_id" : ObjectId("592ccc73fceb6b40e648975b"),
    "a" : 1,
    "combined" : [
        {
            "type" : "conversation",
            "_id" : ObjectId("592ccbf8fceb6b40e6489759"),
            "message" : "I'm here"
        }
    ]
}
{
    "_id" : ObjectId("592ccc95fceb6b40e648975c"),
    "a" : 2,
    "combined" : [
        {
            "type" : "message",
            "_id" : ObjectId("592ccc0bfceb6b40e648975a"),
            "text" : "Something here"
        }
    ]
}

关键在于$lookup将故意简单地留下一个空洞的#arrray"如果"localField""foreignField"表达式与任何元素都不匹配。除了为目标添加空数组属性之外,这不会影响返回的文档结果。

现在你可以"松散"使用$unwind的文档,但只有在省略"preserveNullAndEmptyArrays"选项时才会发生这种情况,该选项可用于处理此类事件。

但是对于"鉴别器的一般用法"基于$lookup,然后只需为您想要的每个集合使用单独的管道阶段" link"到。