mongodb - 仅当本地字段存在时才执行条件查找?

标签 mongodb mongodb-query aggregation-framework

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

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

对话类型的 Action 文档如下

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

最佳答案

我认为您对此有点想多了,您不需要“条件查找”,最好通过示例来演示。

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

> 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" : [ ]
}

如果我现在向管道添加“第二个”$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如果 "localField""foreignField" 表达式不匹配任何元素,则会故意简单地留下一个“空数组”。除了为目标添加空数组属性外,这不会影响返回的文档结果。

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

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

关于mongodb - 仅当本地字段存在时才执行条件查找?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44241188/

相关文章:

database - 使用mgo golang mongodb动态插入多个文档

java - 使用java在Mongodb中插入数据并使用javascript从Mongo DB中检索数据

javascript - Mongoose — 使用聚合创建带有 $sum 的新属性

mongodb - 如何在 mongodb 的正则表达式 $match 中使用来自聚合的字段?

mongodb - 如何使用spring数据mongodb聚合在组中使用sum和condition

javascript - 使用 $lookup 在 mongodb 中进行外部连接

javascript - 访问 URL 时删除 MongoDB 条目 - node.js

MongoDB - 返回特定字段(不包括层次结构)

c# - 带有 Group Unwind 和 Project 的 MongoDb C# 类型聚合

sql - 如何使用 "like"查询 MongoDB