C # Mongodb поиск через lookup обходится дорого

#c# #mongodb #performance #lookup #database-performance

#c# #mongodb #Производительность #поиск #база данных-производительность

Вопрос:

Я новичок в MongoDB, и у меня проблема с производительностью.

У меня есть 2 коллекции, «A» и «B»
У «A» есть список с идентификаторами «B» (например, 50 элементов)

Чего я хочу добиться, так это запросить «A» через поиск «B».

Пример:
допустим, A — это покемоны, а B — ходы.
Я хочу найти всех покемонов, у которых есть ход с силой > = 30

Что я делаю, это что-то вроде этого:

 db.collection("pokemon")
    .aggregate()
    .lookup("Moves", "moves.move_id", "_id", "full_moves")
    .match({ $gte: { "full_moves.power": 30 }})
  

(Приведенный здесь код является примером и не является C #, но я делаю то же самое в C #)

С моей структурой это работает, я получаю то, что хочу.
Но производительность ужасна.
Если я ограничу результат с помощью Skip (n) и Limit (n 20), для выполнения агрегации потребуется 2 секунды.
Если я этого не сделаю, для выполнения полного поиска потребуется около 28 секунд.

Правильно ли то, что я делаю? Есть ли какой-либо другой способ?

Я знаю, что я должен привести документ «B» в качестве поддокумента «A», но «B» большой, и с «A», имеющим около 50 связей с «B», я думаю, что это неподходящий путь.

Спасибо


Редактировать
Здесь структура документа Pkm (не является окончательной, я просто пытаюсь понять mongodb)

Покемон

 {
    "_id": ObjectId(''),
    "abilities": [
        {
            //Simple object with 3 props
        }
    ],
    "experience": 64,
    "forms": [
        {
            //Simple object with 5 props
        }
    ],
    "height": 7,
    "id": 1,
    "is_default": true,
    "moves": [ //a pokemon can have like 50 moves
        {
            //Complex object with like 20 properties, some are Array but for search purpose I use only not nested properties like power, name etc
        }
    ],
    "name": "bulbasaur",
    "stats": {
        "hp": 0,
        "attack": 0,
        "defense": 0,
        "special-attack": 0,
        "special-defense": 0,
        "speed": 0
    },
    "types": [
        {
        //simple object with 2 properties
        }
    ],
    "weight": 50
}
  

Также я запускаю фильтр в MongodbCompass с объяснением, и в нем говорится, что для сбора операция запроса ({ «moves.move.power»: { $ gte: 30 } }) заняла 4 мс

 {
 "stage": "FETCH",
 "nReturned": 942,
 "executionTimeMillisEstimate": 4,
 "works": 12179,
 "advanced": 942,
 "needTime": 11236,
 "needYield": 0,
 "saveState": 12,
 "restoreState": 12,
 "isEOF": 1,
 "docsExamined": 942,
 "alreadyHasObj": 0
}
  

Так что, вероятно, проблема здесь в проявлении этих данных?
Я использую последнюю версию драйвера C # для mongodb, а для манифеста в виде списка BsonDocument я использую LinQ «ToList()»

Ответ №1:

Этот запрос агрегации может работать немного лучше, поскольку фильтрация выполняется на стороне объединения.

 db.pokemon.aggregate([
   {
      $lookup:
         {
           from: "Moves",
           let: { move_id: "$moves.move_id" },
           pipeline: [
              { $match:
                 { $expr:
                    { $and:
                       [
                         { $gte: [ "$power", 30 ] },
                         { $eq: [ "$_id",  "$$move_id" ] }
                       ]
                    }
                 }
              }
           ],
           as: "full_moves"
         }
    }
])
  

Затем вы также можете добавить индекс в Moves коллекцию { power: 1} , что сделает его немного более эффективным.

Однако вы можете пересмотреть повторное моделирование вашего документа, чтобы он был менее реляционным, если ходы принадлежат покемону. Затем просто поместите ходы внутри документа pokemon.

Комментарии:

1. Привет, спасибо за ваш комментарий. Сложно перевести то, что вы написали на C #, но я попробую и проверю, лучше ли это. Также я постараюсь представить «Move» в качестве поддокумента «Pokemon», По вашему мнению, наличие документа «Pokemon» с массивом из 50 «ходов», где каждый ход имеет 20 свойств, является хорошей моделью документа? Хорошо ли для операций с интенсивным чтением? Спасибо

2. Я добавил документ B внутри массива в качестве вложенного документа. Я добавил индекс и сделал то же самое, что и раньше => $match { $ gte: [ «$ power», 30] } Но время, необходимое для получения информации, такое же, как при использовании $ lookup Я думаю, что здесь я делаю что-то не так._.

3. Это кажется странным, можете ли вы опубликовать всю структуру своих документов в своем вопросе? также, если возможно, можете ли вы сделать .explain() , чтобы мы могли взглянуть на то, что происходит docs.mongodb.com/manual/reference/method/cursor.explain

4. @theramenboy похоже, что это может быть сериализация всех данных, не могли бы вы просто спроецировать несколько полей обратно на project этапе, чтобы посмотреть, есть ли у вас все та же проблема?