首页 > 解决方案 > 如何使用 Mongo 聚合来限制给定输入周围的结果?

问题描述

我查看了管道阶段文档,但没有看到如何执行此操作。

假设你有一个用户,每个用户都有积分。

User    Points
 A        22
 B        11
 C        15
 D         7

因此,我们使用'$sort': { points: -1 }按点对用户进行排序。

是否可以使用 Mongo 聚合阶段来查找给定用户之前和之后的用户?

因此,给定用户 C(按 id),它将返回[A, C, B].

标签: mongodbmongodb-queryaggregation-framework

解决方案


非常有趣的问题。也许存在任何更好的解决方案。

免责声明:我假设用户点是唯一的

我们可以使用$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"
      }
    ]
  }
]

Mongo游乐场

脚步:

  1. 我们保留在单独的字段中:

    • 给定用户(C),
    • 按点对所有用户排序
    • 对所有点进行排序并存储在数组中(我希望 MongoDB 也允许按对象查找数组索引)
  2. 现在我们找到给定的用户索引,计算“之前”/“之后”玩家的索引。

  3. 现在,我们使用 3 个元素(之前、当前、之后)创建结果。

注意:如果给定的用户是第一个/最后一个,我们确保返回null之前/之后的项目。


推荐阅读