首页 > 解决方案 > 优化 mongoDB 查询

问题描述

我有一个庞大的文档集合,也就是说,一般来说,意味着存储数百万个文档。

一般来说,典型的文档可能非常复杂和动态,但是每个文档中都应该存在一些常量字段。这些字段包括:GlobalDeviceStatus, ManualTests, SemiAutomaticTests, AutomaticTests. 所有三种类型的测试都由对象数组表示。每个这样的对象可能包含相当多的字段,但也有一些恒定的字段。这些是componentNamecomponentTestStatus

{
    "data": {
        "globalDeviceStatus": false,
        "qaOfficerID": 12121,
        "ManualTests": [{
                "componentName": "camera",
                "componentTestStatus": true,
                "x": 10
            },
            {
                "componentName": "wifi",
                "componentTestStatus": false,
                "mnum": 711
            }
        ],
        "SemiAutomaticTests": [{
                "componentName": "someComponent",
                "componentTestStatus": true,
                "someParameter": true
            },
            {
                "componentName": "oneMoreComponent",
                "componentTestStatus": false
            }
        ],
        "AutomaticTests": [{
                "componentName": "anotherComponent",
                "componentTestStatus": true
            },
            {
                "componentName": "someVeryImportantComponent",
                "componentTestStatus": false
            }
        ]
    },
    "userID": 1
}

每个文档代表一个测试。如果 的值GlobalDeviceStatus结果是,false则测试失败。这也意味着它的 json 应该包含至少一个失败的组件(相反,GlobalDeviceStatus等于的测试true不包含失败的组件,这是非常合乎逻辑的)。我需要的是计算每个组件的失败次数,作为我的输出,我需要这样的东西:

{
    "componentName": 120,
    "someOtherComponentName": 31
}

每个 componentName 可能只属于一种测试类型。也就是说,如果在一个文档中它在SemiAutomaticTests测试中,它就不能迁移到AutomaticTests另一个文档中。

要进行此类计算,我使用以下 mongo 管道:

COUNT_CRASHES = [
            {
                "$match": {
                    "$or": [{
                        "data.ManualTests.componentTestStatus": false
                    }, {
                        "data.AutomaticTests.componentTestStatus": false
                    }, {
                        "data.SemiAutomaticTests.componentTestStatus": false
                    }]
                }
            }, {
                "$project": {
                    "tests": {
                        "$concatArrays": [{
                            "$filter": {
                                "input": "$data.ManualTests",
                                "as": "mt",
                                "cond": {
                                    "$eq": ["$$mt.componentTestStatus", false]
                                }
                            }
                        }, {
                            "$filter": {
                                "input": "$data.AutomaticTests",
                                "as": "at",
                                "cond": {
                                    "$eq": ["$$at.componentTestStatus", false]
                                }
                            }
                        }, {
                            "$filter": {
                                "input": "$data.SemiAutomaticTests",
                                "as": "st",
                                "cond": {
                                    "$eq": ["$$st.componentTestStatus", false]
                                }
                            }
                        }]
                    }
                }
            }, {
                "$unwind": "$tests"
            }, {
                "$group": {
                    "_id": "$tests.componentName",
                    "count": {
                        "$sum": 1
                    }
                }
            }
        ]

它以与上面指定的格式不同的格式返回数据,但这并不重要,现在真正重要的是它需要大约 7 秒,有时是返回时间的两倍(约 14 秒)。数据库中有 350k 个文档。

我想尽可能减少时间。

标签: mongodb

解决方案


除非您将文档重组为 where "ManualTests""AutomaticTests"并且"SemiAutomaticTests"将成为字段值而不是字段本身(这可能会允许更精简的管道),否则您可能需要创建三个这样的索引来加速$match

db.collection.createIndex({ "data.ManualTests.componentTestStatus": 1 })
db.collection.createIndex({ "data.AutomaticTests.componentTestStatus": 1 })
db.collection.createIndex({ "data.SemiautomaticTests.componentTestStatus": 1 })

另请注意,您的投影可以缩短为:

"$project": {
    "tests": {
        "$filter": {
            "input": { "$concatArrays": [ "$data.ManualTests", "$data.AutomaticTests", "$data.SemiAutomaticTests" ] },
            "as": "t",
            "cond": {
                "$eq": ["$$t.componentTestStatus", false]
            }
        }
    }
}

推荐阅读