Получить объекты, содержащие максимальные значения для нескольких полей, используя агрегацию в mongodb - PullRequest
2 голосов
/ 11 января 2020

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

Рассмотрим данные ниже:

_id:1, kills:12,  deaths:6,   assists:1
_id:2, kills:2,   deaths:2,   assists:22
_id:3, kills:1,   deaths:2,   assists:3
_id:4, kills:0,   deaths:23,  assists:4
_id:5, kills:6,   deaths:3,   assists:5
_id:6, kills:7,   deaths:1,   assists:6

Ответ должен быть примерно таким:

maxKills:   { _id:1, kills:12,  deaths:6,   assists:1 },
maxDeaths:  { _id:4, kills:0,   deaths:23,  assists:4 },
maxAssists: { _id:2, kills:2,   deaths:2,   assists:22 },

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

db.coll.aggregate([{
    $group: {
      _id: null,
      kills: { $max: "$stats.kills" },
      deaths: { $max: "$stats.deaths" },
      assists: { $max: "$stats.assists" },
    }
}])

Например, здесь у меня есть все максимальные значения, которые я хочу, но я не получаю все совпадения объектов.

---- ОБНОВЛЕНИЕ ----

С этим ответом { ссылка }, я сделал это работает, но я получаю данные в не действительно удобный для пользователя способ.

{
  "$group": {
    "_id": null,
    "maxKills": { "$max": "$stats.kills" },
    "maxDeaths": { "$max": "$stats.deaths" },
    "maxAssists": { "$max": "$stats.assists" },
    "matches": {
      "$push": {
        "champion": "$champion",
        "gameId": "$gameId",
        "kills": "$stats.kills",
        "deaths": "$stats.deaths",
        "assists": "$stats.assists",
      }
    }
  }
},
{
  "$project": {
    "_id": 0,
    "maxKills": 1,
    "maxDeaths": 1,
    "maxAssists": 1,
    "matches": {
      "$setDifference": [
        {
          "$map": {
            "input": "$matches",
            "as": "match",
            "in": {
              $switch: {
                branches: [
                  { case: { $eq: ["$maxKills", "$$match.kills"] }, then: "$$match" },
                  { case: { $eq: ["$maxDeaths", "$$match.deaths"] }, then: "$$match" },
                  { case: { $eq: ["$maxAssists", "$$match.assists"] }, then: "$$match" },
                ],
                default: false
              }
            }
          }
        },
        [false]
      ]
    }
  }
}

Возвращает:

{
  "maxKills": 25,
  "maxDeaths": 20,
  "maxAssists": 39,
  "matches": [
    {
      "champion": {
        "id": 145,
        "name": "Kai'Sa",
      },
      "gameId": 4263819967,
      "kills": 25,
      "deaths": 3,
      "assists": 16
    },
    {
      "champion": {
        "id": 8,
        "name": "Vladimir",
      },
      "gameId": 4262731529,
      "kills": 8,
      "deaths": 20,
      "assists": 3
    },
    {
      "champion": {
        "id": 22,
        "name": "Ashe",
      },
      "gameId": 4340383097,
      "kills": 9,
      "deaths": 7,
      "assists": 39
    },
    {
      "champion": {
        "id": 23,
        "name": "Tryndamere",
      },
      "gameId": 4352236936,
      "kills": 25,
      "deaths": 6,
      "assists": 22
    }
  ]
}

Моя последняя проблема - случаи, когда несколько объектов имеют одинаковое максимальное значение (как в примере выше, 2 совпадения имеют 25 убивает). Я хочу только самый старый в этих случаях.

1 Ответ

2 голосов
/ 11 января 2020

Вы можете сделать это проще, используя $ filter и $ arrayElemAt после $group stage:

db.collection.aggregate([
    {
        $group: {
            _id: null,
            maxKills: { $max: "$kills" },
            maxDeaths: { $max: "$deaths" },
            maxAssists: { $max: "$assists" },
            docs: { $push: "$$ROOT" }
        }
    },
    {
        $project: {
            _id: 0,
            maxKills: { $arrayElemAt: [ { $filter: { input: "$docs", cond: { $eq: [ "$$this.kills", "$maxKills" ] } } }, 0 ] },
            maxDeaths: { $arrayElemAt: [ { $filter: { input: "$docs", cond: { $eq: [ "$$this.deaths", "$maxDeaths" ] } } }, 0 ] },
            maxAssists: { $arrayElemAt: [ { $filter: { input: "$docs", cond: { $eq: [ "$$this.assists", "$maxAssists" ] } } }, 0 ] }
        }
    }
])

Mon go Playground

...