Filter nested array with conditions based on multi-level object values and update them - MongoDB aggregate + update

二次信任 提交于 2020-05-14 02:27:05

问题


Considering I have the following documents in a collection (ignoring the _id) :

[
  {
    "Id": "OP01",
    "Sessions": [
      {
        "Id": "Session01",
        "Conversations": [
          {
            "Id": "Conversation01",
            "Messages": [
              {
                "Id": "Message01",
                "Status": "read",
                "Direction": "inbound"
              },
              {
                "Id": "Message02",
                "Status": "delivered",
                "Direction": "internal"
              },
              {
                "Id": "Message03",
                "Status": "delivered",
                "Direction": "inbound"
              },
              {
                "Id": "Message04",
                "Status": "sent",
                "Direction": "outbound"
              }
            ]
          },
          {
            "Id": "Conversation02",
            "Messages": [
              {
                "Id": "Message05",
                "Status": "sent",
                "Direction": "outbound"
              }
            ]
          }
        ]
      },
      {
        "Id": "Session02",
        "Conversations": [
          {
            "Id": "Conversation03",
            "Messages": [
              {
                "Id": "Message06",
                "Status": "read",
                "Direction": "inbound"
              },
              {
                "Id": "Message07",
                "Status": "delivered",
                "Direction": "internal"
              }
            ]
          },
          {
            "Id": "Conversation04",
            "Messages": []
          }
        ]
      }
    ]
  },
  {
    "Id": "OP02",
    "Sessions": [
      {
        "Id": "Session03",
        "Conversations": []
      }
    ]
  },
  {
    "Id": "OP03",
    "Sessions": []
  }
]

First query — aggregate (+$project)

I want to get the list of Messages grouped by their Conversations where:

  • Sessions.Id: "Session01"

and

  • Sessions.Conversations.Messages.Direction $in ["inbound", "outbound"]

and

  • Sessions.Conversations.Messages.Status $in ["sent", "delivered"]

The expected result is:

[
  {
    "Id": "Conversation01",
    "Messages": [
      {
        "Id": "Message03",
        "Status": "delivered",
        "Direction": "inbound"
      },
      {
        "Id": "Message04",
        "Status": "sent",
        "Direction": "outbound"
      }
    ]
  },
  {
    "Id": "Conversation02",
    "Messages": [
      {
        "Id": "Message05",
        "Status": "sent",
        "Direction": "outbound"
      }
    ]
  }
]

A side note:

If on different documents (or on different Sessions) the Sessions.Id: "Session01" condition is verified ("Session01"is not an unique key), the document's Messages that match the other conditions should also be added.

The result output doesn't mention neither the document or Sessions levels.


Second query — update

I want to update the Sessions.Conversations.Messages.Status of all those messages (same condition as before) to "read".

The collection should have now the following documents:

Please note the changes on:

  • Sessions.Conversations.Messages.Id = "Message03"
  • Sessions.Conversations.Messages.Id = "Message04"
  • Sessions.Conversations.Messages.Id = "Message05"

at Sessions.Id = "Session01"

[
  {
    "Id": "OP01",
    "Sessions": [
      {
        "Id": "Session01",
        "Conversations": [
          {
            "Id": "Conversation01",
            "Messages": [
              {
                "Id": "Message01",
                "Status": "read",
                "Direction": "inbound"
              },
              {
                "Id": "Message02",
                "Status": "delivered",
                "Direction": "internal"
              },
              {
                "Id": "Message03",
                "Status": "read",
                "Direction": "inbound"
              },
              {
                "Id": "Message04",
                "Status": "read",
                "Direction": "outbound"
              }
            ]
          },
          {
            "Id": "Conversation02",
            "Messages": [
              {
                "Id": "Message05",
                "Status": "read",
                "Direction": "outbound"
              }
            ]
          }
        ]
      },
      {
        "Id": "Session02",
        "Conversations": [
          {
            "Id": "Conversation03",
            "Messages": [
              {
                "Id": "Message06",
                "Status": "read",
                "Direction": "inbound"
              },
              {
                "Id": "Message07",
                "Status": "delivered",
                "Direction": "internal"
              }
            ]
          },
          {
            "Id": "Conversation04",
            "Messages": []
          }
        ]
      }
    ]
  },
  {
    "Id": "OP02",
    "Sessions": [
      {
        "Id": "Session03",
        "Conversations": []
      }
    ]
  },
  {
    "Id": "OP03",
    "Sessions": []
  }
]

How can I accomplish these results with an aggregate and update_one queries?


Here comes a visual explanation of both queries:


回答1:


I have written the aggregation query

        db.session.aggregate([
          {
            $unwind:"$Sessions"
          },
          {
            $unwind:"$Sessions.Conversations"
          },
          {
            $unwind:"$Sessions.Conversations.Messages"
          },
          {
            $match:{
              "Sessions.Id" : "Session01",
              "Sessions.Conversations.Messages.Direction":{
                $in:[
                  "inbound", "outbound"
                ]
              },
              "Sessions.Conversations.Messages.Status":{
                $in:[
                  "sent", "delivered" 
                ]
              }
            }
          },
          {
            $group:{
              "_id":"$Sessions.Conversations.Id",
              "Messages":{
                $push:"$Sessions.Conversations.Messages"
              }
            }
          }
        ]).pretty()

Output

        {
                "_id" : "Conversation02",
                "Messages" : [
                        {
                                "Id" : "Message05",
                                "Status" : "sent",
                                "Direction" : "outbound"
                        }
                ]
        }
        {
                "_id" : "Conversation01",
                "Messages" : [
                        {
                                "Id" : "Message03",
                                "Status" : "delivered",
                                "Direction" : "inbound"
                        },
                        {
                                "Id" : "Message04",
                                "Status" : "sent",
                                "Direction" : "outbound"
                        }
                ]
        }

Now to Update the document:

I have used the positional-filters

        db.session.update(
          {},
          {
            $set:{
              "Sessions.$[session].Conversations.$[].Messages.$[message].Status":"read"
            }
          },
          {
            "arrayFilters": [{"session.Id":"Session01"},{ "message.Id": "Message05" }] 
          }
        )

This will update the status as read for "session.Id":"Session01" and "message.Id": "Message05"

Hope this will help you. :)

UPDATE

        db.session.update(
          {},
          {
            $set:{
              "Sessions.$[session].Conversations.$[].Messages.$[message].Status":"read"
            }
          },
          {
            "arrayFilters": [
              {
                "session.Id":"Session01"
              },
              { 
                "message.Direction": {
                  $in :[
                    "inbound", 
                    "outbound"
                  ]
                },
                "message.Status": {
                  $in :[
                    "sent", 
                    "delivered"
                  ]
                }
              }
            ] 
          }
        )


来源:https://stackoverflow.com/questions/61157587/filter-nested-array-with-conditions-based-on-multi-level-object-values-and-updat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!