【问题标题】:Mongo query to join elements of arrays contained in documentsMongo查询以连接文档中包含的数组元素
【发布时间】:2021-09-21 19:11:38
【问题描述】:

我有一个集合,其中包含有关活动和与会者的信息。集合中的文档看起来像这样

{event: event_1, attendees: [A, B, C]} // A, B and C attended at event_1 
{event: event_2, attendees: [A, B]}    // A and B attended at event_2
{event: event_3, attendees: [A]}       // A only attended at event_3

我想要构建一个查询,让我可以知道每位与会者参加了多少活动,以及他与其他与会者见面的次数。换句话说,我想获得这样的东西

{attendee: A, howManyEvents: 3, togetherWith: B, howManyTimes: 2}
{attendee: A, howManyEvents: 3, togetherWith: C, howManyTimes: 1}
{attendee: B, howManyEvents: 2, togetherWith: A, howManyTimes: 2}
{attendee: B, howManyEvents: 2, togetherWith: C, howManyTimes: 1}
{attendee: C, howManyEvents: 1, togetherWith: A, howManyTimes: 1}
{attendee: C, howManyEvents: 1, togetherWith: B, howManyTimes: 1}

这样的列表可以让我知道,例如查询{attendee: B, togetherWith: A},只要 B 参加一个活动,它也会遇到 A(howManyEventshowManyTimes 都是 2)。同样,1/3 的时间 A 去参加一个活动也有 C。

有没有办法构建一个返回这样的列表的查询?

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    查询产生您想要的结果。
    要理解它,它可以帮助逐步进行并查看输出。

    查询

    • 映射添加with字段,每次缺少1个成员(当前成员)
    • 放松
    • 按参与者分组以总结不同的事件 => howManyTimes
    • 放松
    • 按参加者分组,然后 sum togetherWith : 1 => togetherWith(总数)

    *如果您与 NULL 一起看到它的 1 个成员的活动,它的重要信息是如果一个人只参加了 1 个活动并且是单独的

    Test code here

    db.collection.aggregate([
      {
        "$set": {
          "a": {
            "$map": {
              "input": "$attendees",
              "in": {
                "attendee": "$$a",
                "event": "$event",
                "togetherWith": {
                  "$setDifference": [
                    "$attendees",
                    [
                      "$$a"
                    ]
                  ]
                }
              },
              "as": "a"
            }
          }
        }
      },
      {
        "$project": {
          "_id": 0,
          "a": 1
        }
      },
      {
        "$unwind": {
          "path": "$a"
        }
      },
      {
        "$replaceRoot": {
          "newRoot": "$a"
        }
      },
      {
        "$unwind": {
          "path": "$togetherWith",
          "preserveNullAndEmptyArrays": true
        }
      },
      {
        "$set": {
          "togetherWith": {
            "$ifNull": [
              "$togetherWith",
              null
            ]
          }
        }
      },
      {
        "$group": {
          "_id": "$attendee",
          "howManyEvents": {
            "$addToSet": "$event"
          },
          "together": {
            "$push": {
              "togetherWith": "$togetherWith",
              "howManyTimes": 1
            }
          }
        }
      },
      {
        "$set": {
          "attendee": "$_id"
        }
      },
      {
        "$project": {
          "_id": 0
        }
      },
      {
        "$set": {
          "howManyEvents": {
            "$size": "$howManyEvents"
          }
        }
      },
      {
        "$unwind": {
          "path": "$together"
        }
      },
      {
        "$replaceRoot": {
          "newRoot": {
            "$mergeObjects": [
              "$together",
              "$$ROOT"
            ]
          }
        }
      },
      {
        "$project": {
          "together": 0
        }
      },
      {
        "$group": {
          "_id": {
            "attendee": "$attendee",
            "togetherWith": "$togetherWith"
          },
          "howManyEvents": {
            "$first": "$howManyEvents"
          },
          "howManyTimes": {
            "$sum": "$howManyTimes"
          }
        }
      },
      {
        "$replaceRoot": {
          "newRoot": {
            "$mergeObjects": [
              "$_id",
              "$$ROOT"
            ]
          }
        }
      },
      {
        "$project": {
          "_id": 0
        }
      }
    ])
    

    Query2(替代解决方案方面和更多数组操作)

    • 第一张地图是添加{:togetherWith ".." :howManyTimes 1} 致每一位关注者
    • 2 展开以获得类似的结果
      {
      "attendee": "A",
      "togetherWith": "B",
      "howManyTimes": 1,
      "event": "event_1"
      },
      {
      "attendee": "A",
      "togetherWith": "C",
      "howManyTimes": 1,
      "event": "event_1"
      }
      ...
      
    • 2 个组在一个方面
      • 统计不同的事件(有多少事件)
      • 求和多少次(多少次)
    • 然后它是一个结合了这两个数组的信息的地图
    • 包含所有信息的该数组的 1 次展开
    • 简单的转换

    *如果您看到结果还包含 "howManyTimes": 0 成员,这发生在事件只有 1 个成员时,因为 A 没有用,因为我们有来自其他文档的 howManyEvents, 但是对于“Z”来说它很有用,所以我保留了那些

    Test code here

    db.collection.aggregate([
      {
        "$set": {
          "a": {
            "$map": {
              "input": "$attendees",
              "in": {
                "attendee": "$$a",
                "event": "$event",
                "togetherWith": {
                  "$filter": {
                    "input": {
                      "$map": {
                        "input": "$attendees",
                        "in": {
                          "$cond": [
                            {
                              "$ne": [
                                "$$a",
                                "$$a1"
                              ]
                            },
                            {
                              "togetherWith": "$$a1",
                              "howManyTimes": 1
                            },
                            null
                          ]
                        },
                        "as": "a1"
                      }
                    },
                    "cond": {
                      "$ne": [
                        "$$f",
                        null
                      ]
                    },
                    "as": "f"
                  }
                }
              },
              "as": "a"
            }
          }
        }
      },
      {
        "$project": {
          "_id": 0,
          "a": 1
        }
      },
      {
        "$unwind": {
          "path": "$a"
        }
      },
      {
        "$unwind": {
          "path": "$a.togetherWith",
          "preserveNullAndEmptyArrays": true
        }
      },
      {
        "$project": {
          "attendee": "$a.attendee",
          "togetherWith": "$a.togetherWith.togetherWith",
          "howManyTimes": "$a.togetherWith.howManyTimes",
          "event": "$a.event"
        }
      },
      {
        "$facet": {
          "a": [
            {
              "$group": {
                "_id": "$attendee",
                "howManyEvents": {
                  "$addToSet": "$event"
                }
              }
            },
            {
              "$set": {
                "attendee": "$_id"
              }
            },
            {
              "$project": {
                "_id": 0
              }
            },
            {
              "$set": {
                "howManyEvents": {
                  "$size": "$howManyEvents"
                }
              }
            }
          ],
          "b": [
            {
              "$group": {
                "_id": {
                  "attendee": "$attendee",
                  "togetherWith": "$togetherWith"
                },
                "howManyTimes": {
                  "$sum": "$howManyTimes"
                }
              }
            },
            {
              "$replaceRoot": {
                "newRoot": {
                  "$mergeObjects": [
                    "$_id",
                    "$$ROOT"
                  ]
                }
              }
            },
            {
              "$project": {
                "_id": 0
              }
            }
          ]
        }
      },
      {
        "$set": {
          "b": {
            "$map": {
              "input": "$b",
              "in": {
                "$let": {
                  "vars": {
                    "howManyEvents": {
                      "$arrayElemAt": [
                        {
                          "$filter": {
                            "input": "$a",
                            "cond": {
                              "$eq": [
                                "$$m.attendee",
                                "$$m1.attendee"
                              ]
                            },
                            "as": "m1"
                          }
                        },
                        0
                      ]
                    }
                  },
                  "in": {
                    "$mergeObjects": [
                      "$$m",
                      {
                        "howManyEvents": "$$howManyEvents.howManyEvents"
                      }
                    ]
                  }
                }
              },
              "as": "m"
            }
          }
        }
      },
      {
        "$unwind": {
          "path": "$b"
        }
      },
      {
        "$replaceRoot": {
          "newRoot": "$b"
        }
      }
    ])
    

    【讨论】:

    • 非常好,它有效。谢谢。好吧,实际上它似乎适用于相对较小的集合,但如果集合很大,它似乎不会给我预期的结果。我知道这听起来很奇怪,但我仍在调查。很可能是我没有做正确的检查。无论如何,对于我可以轻松预测结果的小型收藏,它运作良好。
    • 我不知道,如果你发现一个样品不起作用,如果你可以寄给我重新检查它。或者如果我有时间,我可能会用我的示例数据重新检查它。
    • 您的示例有效。我怀疑我在某个地方犯了错误。我会及时向大家发布。谢谢
    【解决方案2】:

    我不认为有任何直接的方法可以做到这一点,但您可以尝试如下聚合管道,

    • $addFields 添加attendees 字段的副本
    • $unwind解构attendees数组
    • $filter 迭代 togetherWith 的循环并从复制字段中删除当前 attendee
    • $unwind解构togetherWith数组
    • $group by attendee 并构造togetherWith 的数组并计算howManyEvents 的总和
    • $unwind解构togetherWith数组
    • $group by attendeetogetherWith 并获得 howManyTimes 的计数
    • $project 格式化结果
    db.collection.aggregate([
      { $addFields: { togetherWith: "$attendees" } },
      { $unwind: "$attendees" },
      {
        $addFields: {
          togetherWith: {
            $filter: {
              input: "$togetherWith",
              cond: { $ne: ["$$this", "$attendees"] }
            }
          }
        }
      },
      { $unwind: "$togetherWith" },
      {
        $group: {
          _id: "$attendees",
          togetherWith: { $push: "$togetherWith" },
          howManyEvents: { $sum: 1 }
        }
      },
      { $unwind: "$togetherWith" },
      {
        $group: {
          _id: {
            attendee: "$_id",
            togetherWith: "$togetherWith"
          },
          howManyEvents: { $first: "$howManyEvents" },
          howManyTimes: { $sum: 1 }
        }
      },
      {
        $project: {
          _id: 0,
          attendee: "$_id.attendee",
          togetherWith: "$_id.togetherWith",
          howManyEvents: 1,
          howManyTimes: 1
        }
      }
    ])
    

    Playground

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-18
      • 2021-06-10
      • 2020-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多