【问题标题】:MongoDB aggregation with unwind on empty array在空数组上展开的 MongoDB 聚合
【发布时间】:2015-09-12 12:48:35
【问题描述】:

我在 MongoDB 中有一个文档集合,其中每个文档都有一个包含数组的子文档。

{
  _id: <id>
  key1: "value1",
  key2: "value2",
  ...
  versions: [
    { version: 2, key1: <othervalue2>, key2: <othervalue2>}
    { version: 1, key1: <othervalue2>, key2: <othervalue2>}
  ]
}

我想查询集合并返回包含所有字段的文档,以及与某些参数匹配的数组元素(或当不匹配时为空数组)。使用我当前的代码,只有当数组中至少有一个元素匹配时,我才能得到结果。

我正在使用这些参数进行聚合:

{$match: {_id: <someID>}},
{$unwind: '$versions'},
{$match: {'versions.version': {$gte: <version>}}},
{$group: {_id: '$_id', key1: {$first: '$key1'}, ..., 'versions': {$push: '$versions'}}

例如,查询 arrayelement.version >= 2 应该返回的位置(这适用于当前代码):

{
  _id: <id>
  key1: "value1",
  key2: "value2",
  ...
  versions: [
    { version: 2, key1: <othervalue2>, key2: <othervalue2>}
  ]
}

并查询 arrayelement.version >= 4 应该返回的位置:

{
  _id: <id>
  key1: "value1",
  key2: "value2",
  ...
  versions: []
}

我见过的解决方案都没有解决即使数组为空也返回对象的问题。这甚至可能吗?

【问题讨论】:

  • 您的 $match 聚合不满足条件。因此,在空数组的情况下它不会返回任何结果
  • @gypsyCoder 实际上$unwind 会忽略空数组,因此这些文档永远不会到达第二个$match

标签: mongodb aggregation-framework


【解决方案1】:

您可以检查版本数组是否为空,如果是则添加一些占位符,例如空文档。省略管道的其余部分可能是这样的:

[
    {
        "$project" : {
            "versions" : {
                "$cond" : {
                    "if" : {"$eq" : [{"$size" : "$versions" }, 0]},
                    "then" : [{ }],
                    "else" : "$versions"
                }
            },
            "_id" : 1
        }
    },
    {
        "$unwind" : "$versions"
    }
]

请记住,您的管道中的{$match: {'versions.version'}} 会将其过滤掉,因此您必须调整这部分。

【讨论】:

  • 谢谢!做到了。完整答案如下。
  • 如果你想传递(不包括)空数组,你会怎么做?
【解决方案2】:

感谢 zero323 让我走上正轨。下面是完整的过滤器

{$match: {_id: <someid>}},
{
    "$project" : {
        "versions" : {
            "$cond" : {
                "if" : {$or: [{$eq : [{$size : "$versions" }, 0]},{$eq : ["$version", <version>]}]},
                "then" : [null],
                "else" : "$versions"
            }
        },
        "_id" : 1,
        "key1": 1,
        "key2": 1,
        ...
        "version": 1

    }
},
{$unwind: '$versions'},
{$match: {$or: [{'versions.version':{ $exists: false}},{'versions.version': {$gte: <version>}}]}},
{$group: {_id:'$_id',key1:{$first: '$key1'}, key2:{$first: '$key2'},...,version:{$first:'$version'},versions:{$push: '$versions'}}},
{
    "$project" : {
        "versions" : {
            "$cond" : {
                "if" : {$eq : ["$versions", [null]]},
                "then" : [],
                "else" : "$versions"
            }
        },
        "_id" : 1,
        "key1": 1,
        "key2": 1,
        ...
        "version": 1
    }
}

【讨论】:

    【解决方案3】:

    从 MongoDb 3.2 开始,$unwind 运算符支持preserveNullAndEmptyArrays:&lt;boolean&gt;

    所以当preserveNullAndEmptyArrays:true时,它也会包含没有任何数据或空数据的值。

    代码更改 -

    {$unwind: {path: '$versions', preserveNullAndEmptyArrays: true} },
    

    欲了解更多信息,请访问 - https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/#document-operand-with-options

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-10
      • 2015-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-02
      • 2021-04-28
      • 2021-07-20
      相关资源
      最近更新 更多