代码之家  ›  专栏  ›  技术社区  ›  mk_89

如果对象的子对象包含某个值,则从列表中返回该对象

  •  0
  • mk_89  · 技术社区  · 6 年前

    我已经在这个问题上纠结了一段时间,我觉得我很接近,但就是想不出解决办法。

    我有一个浓缩的模式,如下所示:

    {
            "_id": {
                "$oid": "5a423f48d3983274668097f3"
            },
            "id": "59817",
            "key": "DW-15450",
            "changelog": {
                "histories": [
                    {
                        "id": "449018",
                        "created": "2017-12-13T11:11:26.406+0000",
                        "items": [
                            {
                                "field": "status",
                                "toString": "Released"
                            }
                        ]
                    },
                    {
                        "id": "448697",
                        "created": "2017-12-08T09:54:41.822+0000",
                        "items": [
                            {
                                "field": "resolution",
                                "toString": "Fixed"
                            },
                            {
                                "field": "status",
                                "toString": "Completed"
                            }
                        ]
                    }
                ]
            },
            "fields": {
                "issuetype": {
                    "id": "1",
                    "name": "Bug"
                }
            }
        }
    

    我想抓住所有 changelog.histories 有一个 changelog.histories.items.toString 的值 Completed .

    下面是我的管道

    "pipeline" => [
    [
        '$match' => [
            'changelog.histories.items.toString' => 'Completed'
        ]
    ],
    [
        '$unwind' => '$changelog.histories'
    ],
    [
        '$project' => [
            'changelog.histories' => [
                '$filter' => [
                    'input' => '$changelog.histories.items',
                    'as' => 'item',
                    'cond' => [
                        '$eq' => [
                            '$$item.toString', 'Completed'
                        ]
                    ]
                ]
            ]
        ]
    ]
    ]
    

    因此,理想情况下,我希望返回以下内容

    {
    "id": "448697",
    "created": "2017-12-08T09:54:41.822+0000",
    "items": [
        {
            "field": "resolution",
            "toString": "Fixed"
        },
        {
            "field": "status",
            "toString": "Completed"
        }
    ]
    

    }

    1 回复  |  直到 6 年前
        1
  •  1
  •   Marcello    6 年前

    你可以试试这样的。

    db.changeLogs.aggregate([
      { $unwind: '$changelog.histories' },
      { $match: {'changelog.histories.items.toString': 'Completed'} },
      { $replaceRoot: { newRoot: "$changelog.histories" } }
    ]);
    

    此解决方案执行COLLSCAN,因此在收集量较大的情况下成本较高。如果您有严格的性能要求,可以创建如下索引。

    db.changeLogs.createIndex({'changelog.histories.items.toString': 1})
    

    然后,为了利用索引,您必须按以下方式更改查询。

    db.changeLogs.aggregate([
      { $match: {'changelog.histories.items.toString': 'Completed'} },
      { $unwind: '$changelog.histories' },
      { $match: {'changelog.histories.items.toString': 'Completed'} },
      { $replaceRoot: { newRoot: "$changelog.histories" } }
    ]);
    

    第一阶段过滤具有 至少一个历史记录项 Completed 状态此阶段使用索引。第二阶段展开向量。第三阶段再次过滤在 完整的 状态最后,第四阶段将根返回项替换为文档。

    编辑

    根据您的评论,这是另一种解决方案 id key 返回文档中的字段(同时继续使用索引)。

    db.changeLogs.aggregate([
      { $match: {'changelog.histories.items.toString': 'Completed'} },
      { $unwind: '$changelog.histories' },
      { $match: {'changelog.histories.items.toString': 'Completed'} },
      { $project: { _id: 0, id: 1, key: 1, changelog: 1 }}
    ]);