【问题标题】:How can I group correctly in mongo by two fields?如何按两个字段在 mongo 中正确分组?
【发布时间】:2021-10-20 04:25:26
【问题描述】:

我无法对文档进行分组。我有以下字段:

{
    field: "someId1",
    data: [{"foo":"bar"}]
},
{
    field: "someId2",
    associatedField: "someId1" //same as field value above
    data: [{"foo":"bar1"},{"foo":"bar2"}]
},
{
    field: "someId3",
    data:[{"foo":"bar3"}]
}

对这些条目进行分组后我想要的是这样的:

{
    field: "someId1",
    associatedField: "someId2",
    data:[{"foo":"bar"},{"foo":"bar1"},{"foo":"bar2"}]
},
{
    field: "someId3",
    data:[{"foo":"bar3"}]
}

我还有另一个限制,并非所有条目都有关联字段(如第三个条目),所以每当我尝试按 {field, associatedField} 分组时,其中一些仍然未分组。

更清楚地说,因为第 2 个文档与第 1 个文档相关联,我希望将它们分组并仅关联 1 个数据数组,其中包含 1 和 2 的所有值。

有没有办法解决这个问题?

谢谢!

【问题讨论】:

  • 你能用词写字段或表达式(字段上的操作),你要分组吗? {field, associatedField} 是什么意思?为什么前 2 个在同一个组中结束,为什么 associatedField 变成了 2 个?
  • @Takis_ 我已经修改了描述,希望现在更清楚

标签: mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

查询会做这些

  • lookup(self-lookup) => 如果 field=associatedField 或 assosiatedFiled=field 则加入,如果 _id 相同则不要加入以避免自加入
  • 从所有加入的数据中,合并它们的数据,并将它们与父数据合并
  • 因为这些数据出现了 2 次,对于 doc1 加入了 doc2,而 doc2 加入了 doc1,我只保留了具有相关字段的文档(另一个文档已被删除)或没有加入任何文档(例如带有 " someid3" 被保留,因为没有加入任何人)

在您的数据上尝试一下,也许这是您想要做的,但我不知道这对我来说似乎很奇怪,我认为最好更改您的架构。

Test code here

数据在

[{
    "_id" :1,
    "field": "someId1",
    "data": [{"foo":"bar"}]
},
{
    "_id" :2,
    "field": "someId2",
    "associatedField": "someId1" ,
    "data": [{"foo":"bar1"},{"foo":"bar2"}]
},
{
    "_id" :3,
    "field": "someId3",
    "data":[{"foo":"bar3"}]
}]

查询(Ruben Vega 第一部分,帮我写)

aggregate(
[ {
  "$lookup" : {
    "from" : "1",
    "let" : {
      "id_p" : "$_id",
      "field_p" : "$field",
      "associatedField_p" : "$associatedField"
    },
    "pipeline" : [ {
      "$match" : {
        "$expr" : {
          "$and" : [ {
            "$ne" : [ "$$id_p", "$_id" ]
          }, {
            "$or" : [ {
              "$eq" : [ "$$associatedField_p", "$field" ]
            }, {
              "$eq" : [ "$$field_p", "$associatedField" ]
            } ]
          } ]
        }
      }
    } ],
    "as" : "temp"
  }
}, {
  "$addFields" : {
    "data" : {
      "$concatArrays" : [ "$data", {
        "$reduce" : {
          "input" : "$temp.data",
          "initialValue" : [ ],
          "in" : {
            "$let" : {
              "vars" : {
                "a" : "$$value",
                "d" : "$$this"
              },
              "in" : {
                "$concatArrays" : [ "$$a", "$$d" ]
              }
            }
          }
        }
      } ]
    }
  }
}, {
  "$match" : {
    "$expr" : {
      "$or" : [ {
        "$eq" : [ {
          "$size" : "$temp"
        }, 0 ]
      }, {
        "$and" : [ {
          "$ne" : [ "$associatedField", null ]
        }, {
          "$ne" : [ {
            "$type" : "$associatedField"
          }, "missing" ]
        } ]
      } ]
    }
  }
}, {
  "$unset" : [ "temp" ]
} ]
)

结果(2个文件和你想要的结果一样)

{
  "_id": 2,
  "field": "someId2",
  "associatedField": "someId1",
  "data": [
    {
      "foo": "bar1"
    },
    {
      "foo": "bar2"
    },
    {
      "foo": "bar"
    }
  ]
}
{
  "_id": 3,
  "field": "someId3",
  "data": [
    {
      "foo": "bar3"
    }
  ]
}

【讨论】:

    【解决方案2】:

    您可以使用 $lookup 聚合和 $concatArrays 获得您想要的。

    查看playground

    db.collection.aggregate([
        {
            "$lookup": {
                "from": "collection",
                "localField": "associatedField",
                "foreignField": "field",
                "as": "temp"
            }
        },
        {
            "$unwind": {
                path: "$temp",
                preserveNullAndEmptyArrays: true
            }
        },
        {
            "$project": {
                field: 1,
                data: {
                    "$concatArrays": [
                        {
                            $ifNull: ["$data", []]
                        },
                        {
                            $ifNull: ["$temp.data", []]
                        }
                    ]
                }
            }
        }
    ])
    

    【讨论】:

    • 这个查询的问题是我有两次来自“someId1”的数据。我需要的是从 3 个文档中获得 2 个:一个包含来自 1 和 2 的数据,第二个包含来自 3 的数据。
    • 为了得到这个结果,我认为你应该有一个不同的结构。至少我想不出最后只得到 2 个文件的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2019-11-04
    • 2014-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多