Как использовать агрегацию Монго, чтобы ограничить результаты вокруг заданного ввода? - PullRequest
0 голосов
/ 02 ноября 2019

Я просмотрел этапы конвейера, документы , но не видел, как это сделать.

Предположим, у вас есть пользователь, и у каждого пользователя есть очки.

User    Points
 A        22
 B        11
 C        15
 D         7

Итак, мы используем '$sort': { points: -1 } для упорядочения пользователей по точкам.

Можно ли использовать этап агрегирования Монго для поиска пользователей до и после данного пользователя?

Итакдля данного пользователя C (по идентификатору) он вернет [A, C, B].

1 Ответ

1 голос
/ 02 ноября 2019

Очень интересный вопрос. Возможно, существует какое-то лучшее решение.

Отказ от ответственности : я предполагаю, что пользовательские баллы уникальны

Мы можем использовать $ facet , чтобы получить ожидаемый результат, но высокая стоимость (очень большой запрос)

db.collection.aggregate([
  {
    $facet: {
      "givenUser": [
        {
          $match: {
            "user": "C"
          }
        }
      ],
      "allUser": [
        {
          $sort: {
            "Points": -1
          }
        }
      ],
      "orderedPoints": [
        {
          $sort: {
            "Points": -1
          }
        },
        {
          $group: {
            _id: null,
            Points: {
              $push: "$Points"
            }
          }
        },
        {
          $unwind: "$Points"
        }
      ]
    }
  },
  {
    $project: {
      allUser: 1,
      currIndex: {
        $indexOfArray: [
          "$orderedPoints.Points",
          {
            $arrayElemAt: [
              "$givenUser.Points",
              0
            ]
          }
        ]
      },
      beforeIndex: {
        $add: [
          {
            $indexOfArray: [
              "$orderedPoints.Points",
              {
                $arrayElemAt: [
                  "$givenUser.Points",
                  0
                ]
              }
            ]
          },
          -1
        ]
      },
      afterIndex: {
        $add: [
          {
            $indexOfArray: [
              "$orderedPoints.Points",
              {
                $arrayElemAt: [
                  "$givenUser.Points",
                  0
                ]
              }
            ]
          },
          1
        ]
      }
    }
  },
  {
    $project: {
      result: [
        {
          $arrayElemAt: [
            "$allUser",
            {
              $cond: {
                if: {
                  $lt: [
                    "$beforeIndex",
                    0
                  ]
                },
                then: 999,
                else: "$beforeIndex"
              }
            }
          ]
        },
        {
          $arrayElemAt: [
            "$allUser",
            "$currIndex"
          ]
        },
        {
          $arrayElemAt: [
            "$allUser",
            "$afterIndex"
          ]
        }
      ]
    }
  }
])

[
  {
    "result": [
      {
        "Points": 22,
        "_id": ObjectId("5a934e000102030405000000"),
        "user": "A"
      },
      {
        "Points": 15,
        "_id": ObjectId("5a934e000102030405000002"),
        "user": "C"
      },
      {
        "Points": 11,
        "_id": ObjectId("5a934e000102030405000001"),
        "user": "B"
      }
    ]
  }
]

MongoPlayground

Шаги:

  1. Мы держим в отдельных полях:

    • Данный пользователь (C),
    • Заказ всех пользователей по точкам
    • Заказ всех точек и хранение внутримассив (хотелось бы, чтобы MongoDB также позволял находить индекс массива по объектам)
  2. Теперь мы находим заданный индекс пользователя, рассчитываем индексы для игроков «до» / «после».

  3. Теперь мы создаем результат с 3 элементами (до, текущий, после).

Примечание: Если данный пользователь является первым / последним, мы гарантируем возврат null для элементов до / после.

...