$ lookup和“ sub”聚合管道极大地降低了查询性能

时间:2019-01-31 09:17:02

标签: 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
        }
    }
])

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

我知道发生这种情况是因为$ lookup运算符必须对进入聚合管道查找阶段的每一行进行一次表扫描。

我尝试设置如下所述的索引:Poor lookup aggregation performance,但这似乎没有任何影响。

我的下一个猜测是$ lookup阶段中的“ sub”管道无法使用索引,因此我将其替换为简单的

"$lookup": {
    "from": "customer",
    "localField": "customerId",
    "foreignField": "_id",
    "as": "customer"
}

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

我尝试了以下索引

  • customerId
  • 上的升序,分解,哈希和文本索引
  • customer.name
  • 上的升序,分解,哈希和文本索引

对于任何关于我做错了什么或如何通过更好的聚合管道可以实现同一目标的想法,我深表感谢。

其他信息: 我正在使用三个成员的副本集。我使用的是MongoDB 4.0。

请注意:我知道我正在使用非关系数据库来实现高度关系目标,但是在此项目中,MongoDB因其具有ChangeStream功能而成为我们的选择。如果有人知道一个具有可比较功能的其他数据库(有关更改的实时推送通知),并且可以在内部运行(因此Firebase退出了),我很想听听它!

谢谢!

2 个答案:

答案 0 :(得分:1)

我发现了为什么不使用索引的原因。

我使用与集合自己的归类不同的归类来查询集合。 但是,集合上的id索引始终使用集合的默认排序规则来实现。

因此未使用索引。

我将集合的排序规则更改为与查询相同的排序规则,现在查询只花费了一部分时间(但仍然很慢:))。

(是的,您必须重新创建集合才能更改排序规则,无法进行即时更改。)

答案 1 :(得分:0)

您是否考虑过为客户提供单个集合,并将任务作为每个文档中的嵌入式数组?这样,您将能够在客户和任务字段上为搜索建立索引。