Оптимизация запросов агрегации MongoDB: сопоставить -> развернуть -> сопоставить - развернуть-> сопоставить - PullRequest
0 голосов
/ 23 апреля 2020

Входные данные

{
    "_id" : ObjectId("5dc7ac6e720a2772c7b76671"),
    "idList" : [ 
        {
            "queueUpdateTimeStamp" : "2019-12-12T07:16:47.577Z",
            "displayId" : "H14",
            "currentQueue" : "10",
            "isRejected" : true,
            "isDispacthed" : true
        },
        {
            "queueUpdateTimeStamp" : "2019-12-12T07:16:47.577Z",
            "displayId" : "H14",
            "currentQueue" : "10",
            "isRejected" : true,
            "isDispacthed" : false
        }
    ],
    "poDetailsId" : ObjectId("5dc7ac15720a2772c7b7666f"),
    "processtype" : 1
}

Выходные данные

{
    "_id" : ObjectId("5dc7ac6e720a2772c7b76671"),
    "idList":
     {
            "queueUpdateTimeStamp" : "2019-12-12T07:16:47.577Z",
            "displayId" : "H14",
            "currentQueue" : "10",
            "isRejected" : true,
            "isDispacthed" : true
    },
    "poDetailsId" : ObjectId("5dc7ac15720a2772c7b7666f"),
    "processtype" : 1
}

Запрос 1 (unwind, затем match )

     aggregate([
     {
         $unwind: { path: "$idList" }
     },
     {
         $match: { 'idList.isDispacthed': isDispatched }
     }
     ])

Запрос 2 (match, затем unwind, затем match)

     aggregate([
     {
         $match: { 'idList.isDispacthed': isDispatched }
     },
     {
         $unwind: { path: "$idList" }
     },
     {
         $match: { 'idList.isDispacthed': isDispatched }
     }
     ])

Мой вопрос / мой Обеспокоенность

(предположим, у меня есть большое количество документов (50k +) в этой коллекции и предполагается, что у меня есть другие поиски и прогнозы после этого запроса в том же конвейере)

match -> unwind -> match VS unwind ->match

  1. Есть ли разница в производительности между этими двумя запросами?
  2. Есть ли другой (лучший) способ записи этот запрос?

1 Ответ

1 голос
/ 23 апреля 2020

Все зависит от оптимизатора планировщика запросов MongoDB:

Операции конвейера агрегации имеют фазу оптимизации, которая пытается изменить форму конвейера для повышения производительности.

Чтобы увидеть, как оптимизатор преобразовывает конкретный конвейер агрегации, включив параметр объяснение в метод db.collection.aggregate ().

https://docs.mongodb.com/manual/core/aggregation-pipeline-optimization/

Создайте индекс для poDetailsId и выполните этот запрос:

db.getCollection('collection').explain().aggregate([
     {
         $unwind: "$idList"
     },
      {
         $match: { 
           'idList.isDispacthed': true, 
           "poDetailsId" : ObjectId("5dc7ac15720a2772c7b7666f") 
         }
     }  
])

{
    "stages" : [ 
        {
            "$cursor" : {
                "query" : {
                    "poDetailsId" : {
                        "$eq" : ObjectId("5dc7ac15720a2772c7b7666f")
                    }
                },
                "queryPlanner" : {
                    "plannerVersion" : 1,
                    "namespace" : "test.collection",
                    "indexFilterSet" : false,
                    "parsedQuery" : {
                        "poDetailsId" : {
                            "$eq" : ObjectId("5dc7ac15720a2772c7b7666f")
                        }
                    },
                    "queryHash" : "2CF7E390",
                    "planCacheKey" : "A8739F51",
                    "winningPlan" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "poDetailsId" : 1.0
                            },
                            "indexName" : "poDetailsId_1",
                            "isMultiKey" : false,
                            "multiKeyPaths" : {
                                "poDetailsId" : []
                            },
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 2,
                            "direction" : "forward",
                            "indexBounds" : {
                                "poDetailsId" : [ 
                                    "[ObjectId('5dc7ac15720a2772c7b7666f'), ObjectId('5dc7ac15720a2772c7b7666f')]"
                                ]
                            }
                        }
                    },
                    "rejectedPlans" : []
                }
            }
        }, 
        {
            "$unwind" : {
                "path" : "$idList"
            }
        }, 
        {
            "$match" : {
                "idList.isDispacthed" : {
                    "$eq" : true
                }
            }
        }
    ],
    "ok" : 1.0
}

Как видите, MongoDB изменит эту агрегацию на:

db.getCollection('collection').aggregate([
     {
         $match: { "poDetailsId" : ObjectId("5dc7ac15720a2772c7b7666f") }
     }
     {
         $unwind: "$idList"
     },
     {
         $match: { 'idList.isDispacthed': true }
     }  
])

Логически, $match -> $unwind -> $match лучше, поскольку вы фильтруете (по индексу) подмножество записей вместо полного сканирования (работа со 100 сопоставленными документами ≠ всеми документами).

Если для операции агрегирования требуется только подмножество данных в коллекции, используйте этапы $match, $limit и $skip, чтобы ограничить документы, которые вводятся в начале трубопровод. При размещении в начале конвейера операции $match используют подходящие индексы для сканирования только соответствующих документов в коллекции .

https://docs.mongodb.com/manual/core/aggregation-pipeline/#early -фильтрация

После манипулирования документами MongoDB не может применять индексы.

...