Группируйте массив объектов по ключу и суммарным значениям из результата агрегации мангустов

#javascript #node.js #mongodb #mongoose

Вопрос:

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

Агрегация выглядит следующим образом

  result = await TokenBalance.aggregate([
        {
            $match: {
                $and: [
                    { ethervalue: { $gte: minBalance } },
                    {
                        ethervalue: { $lte: maxBalance }
                    }
                ]
            }
        },
        { $limit:limit }

    ])
 

Это возвращает массив объектов этого формата

  {
    "_id": "61013d6dda7d7c0015af5ccf",
    "balances": [
     {
       "address": "0x1fc3ddeb035310930a444c0fa59c01618d5902af",
       "symbol": "HBTC",
       "balance": 5.21419339e-10,
       "usdvalue": 0.000020969961637162402
     },
     {
       "address": "0x1fc3ddeb035310930a444c0fa59c01618d5902af",
       "symbol": "NSBT",
       "balance": 1.258566,
       "usdvalue": 27.427343477595258
     },
     {
       "address": "0x1fc3ddeb035310930a444c0fa59c01618d5902af",
       "symbol": "CRV",
       "balance": 517.985955847106,
       "usdvalue": 806.7017064052314
     },
     {
       "address": "0x1fc3ddeb035310930a444c0fa59c01618d5902af",
       "symbol": "USDT",
       "balance": 0.003469,
       "usdvalue": 0.003470159747979122
     }
   ],
   "address": "0x1fc3ddeb035310930a444c0fa59c01618d5902af",
   "ethervalue": 0.7604598621232733,
   "createdAt": "2021-07-28T11:20:13.927Z",
   "updatedAt": "2021-07-28T11:20:13.927Z",
   "__v": 0
},
 

Что мне нужно, так это свойство «балансы», которое будет обрабатываться как сгруппированное по символу, и для каждого из этих символов суммируйте поля «Баланс» и «Значение usd».

Я бы предпочел, чтобы это было сделано в агрегации, если это возможно, но я, похоже, не могу сделать это правильно, даже не в чистых узлах.

Я хочу, чтобы результат был таким:

 [
 {
  symbol: USDC, balance: xxx, usdvalue: yyy
 },
 {
  symbol: USDT, balance: zzz, usdvalue: jjj
 }
]
 

Ответ №1:

Вы можете использовать следующий подход,

  • $unwind для деконструкции balances массива
  • $group по symbol сумме и balance usdvalue
  • $addFields чтобы переименовать _id поле в symbol и и удалить _id поле
 result = await TokenBalance.aggregate([
  {
    $match: {
      $and: [
        { ethervalue: { $gte: minBalance } },
        { ethervalue: { $lte: maxBalance } }
      ]
    }
  },
  { $unwind: "$balances" },
  {
    $group: {
      _id: "$balances.symbol",
      balance: { $sum: "$balances.balance" },
      usdvalue: { $sum: "$balances.usdvalue" }
    }
  },
  {
    $addFields: {
      symbol: "$_id",
      _id: "$REMOVE"
    }
  },
  { $limit:limit }
])
 

Игровая площадка

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

1. Спасибо. Я ограничил результаты этого запроса 5 объектами, и на его завершение ушло почти 8 минут. Не могли бы вы найти какие-либо способы улучшить это или способы выяснить, почему это занимает так много времени?

2. Я не уверен, сколько документов у вас в коллекции? а максимальное количество документов в balances массиве?

3. коллекция tokenbalances сейчас содержит 3844656 документов. Вот почему я хотел ограничить его минимальными и максимальными значениями эфира. Массив балансов может содержать до 200 объектов, но в основном они находятся в среднем на 15-20.

4. 1) для улучшения процесса сканирования вы можете создать единый индекс на ethervalue поле, 2) он улучшится, если вы добавите этап $limit для основных документов после этапа $match и до этапа $unwind.

5. Я постараюсь, что большое вам спасибо за это сейчас