使用$ lookup和"sub"聚合管道,查询性能极慢

Ste*_*fel 6 mongodb mongodb-query aggregation-framework

假设我有两个收藏,任务客户.

客户通过客户中的"customerId"字段与任务建立1:n的关系.

我现在有一个视图,我需要显示客户名称的任务.我还需要能够过滤和排序客户名称.这意味着我无法在以下查询中的$ lookup之前执行$ limit或$ match阶段.

所以这是我的示例查询:

db.task.aggregate([
    {
        "$match": {
            "_deleted": false
        }
    },
    "$lookup": {
        "from": "customer",
        "let": {
            "foreignId": "$customerId"
        },
        "pipeline": [
            {
                "$match": {
                    "$expr": {
                        "$and": [
                            {
                                "$eq": [
                                    "$_id",
                                    "$$foreignId"
                                ]
                            },
                            {
                              "$eq": [
                                "$_deleted",
                                false
                              ]
                            }
                        ]
                    }
                }
            }
        ],
        "as": "customer"
    },
    {
        "$unwind": {
            "path": "$customer",
            "preserveNullAndEmptyArrays": true
            }
    },
    {
        "$match": {
            "customer.name": 'some_search_string'
        }
    },
    {
        "$sort": {
            "customer.name": -1
        }
    },
    {
        "$limit": 35
    },
    {
        "$project": {
            "_id": 1,
            "customer._id": 1,
            "customer.name": 1,
            "description": 1,
            "end": 1,
            "start": 1,
            "title": 1
        }
    }
])
Run Code Online (Sandbox Code Playgroud)

当集合的大小增加时,此查询变得非常慢.拥有1000个任务和20个客户,已经需要大约500毫秒来交付结果.

我知道,这是因为$ lookup运算符必须为进入聚合管道查找阶段的每一行执行表扫描.

我试图设置如下所述的索引:查找聚合性能差,但似乎没有任何影响.

我的下一个猜测是$ lookup阶段的"sub"-pipeline无法使用索引,所以我用一个简单的替换它

"$lookup": {
    "from": "customer",
    "localField": "customerId",
    "foreignField": "_id",
    "as": "customer"
}
Run Code Online (Sandbox Code Playgroud)

但仍然没有使用索引或对性能没有任何影响.(说实话,我不知道两者都是这种情况,因为.explain()不能用于聚合管道.)

我尝试了以下索引:

  • 升序,desecending,哈希和文本索引上的客户ID
  • 在递增,desecending,哈希和文本索引customer.name

我很感激任何关于我做错了什么的想法,或者我如何通过更好的聚合管道实现同样的目标.

附加信息:我正在使用三个成员的副本集.我在使用MongoDB 4.0.

请注意:我知道我正在使用非关系型数据库来实现高度关系目标,但在这个项目中,MongoDB是我们的选择,因为它具有ChangeStream功能.如果有人知道具有类似功能的不同数据库(更改的实时推送通知),这可以在内部运行(因此Firebase退出),我很乐意听到它!

提前致谢!

小智 0

您是否考虑过为客户提供一个集合,其中任务作为每个文档中的嵌入数组?这样,您就可以对客户和任务字段的搜索建立索引。