MongoDB按数组元素排序文档

27

我有如下文档结构:

{
    map: 'A',
    points: [
        {
            type: 'type1',
            distanceToSpawn: 110
        },
        {
            type: 'type4',
            distanceToSpawn: 40
        },
        {
           type: 'type6',
           distanceToSpawn: 30
        }
    ]
},
{
    map: 'B',
    points: [
        {
            type: 'type1',
            distanceToSpawn: 100
        },
        {
            type: 'type2',
            distanceToSpawn: 60
        },
        {
            type: 'type3',
            distanceToSpawn: 25
        }
    ]
},
{
    map: 'C',
    points: [
        {
            type: 'type2',
            distanceToSpawn: 90
        },
        {
            type: 'type3',
            distanceToSpawn: 1
        },
        {
            type: 'type6',
            distanceToSpawn: 76
        }
    ]
}

我希望获取所有具有点类型 type1 的地图,并按照到生成点的距离 distanceToSpawn 升序排序。

期望结果:

{
    map: 'B',
    points: [
        {
            type: 'type1',
            distanceToSpawn: 100
        }
    ]
},
{
    map: 'A',
    points: [
        {
            type: 'type1',
            distanceToSpawn: 110
        }
    ]
}

我尝试了类似于:

db.maps.find({'points.type': {$eq : 'type1'}}, {map: 1, 'points.$':1}).sort({'points.distanceToSpawn': 1}).limit(10)

但是这个东西没有按升序排序地图。

谢谢。

1个回答

29

你不能通过数组实现这个功能,这里的主要问题是因为你想在匹配的元素上进行“排序”。如果想按照这种方式对结果进行排序,则需要使用.aggregate()方法。可以选择以下任一方式:

对于现代MongoDB版本:

db.maps.aggregate([
    { "$match": { "points.type": "type1" }},
    { "$addFields": {
        "order": {
            "$filter": {
              "input": "$points",
              "as": "p",
              "cond": { "$eq": [ "$$p.type", "type1" ] }
            }
        }
    }},
    { "$sort": { "order": 1 } }
])

适用于MongoDB 2.6到3.0版本

db.maps.aggregate([
   { $match: { 'points.type': 'type1' } },
    {
     $project: {
       points: {
        $setDifference: [
          {
            $map: {
              input: '$points',
              as: 'p',
              in: {
                $cond: [
                  { $eq: ['$$p.type', 'type1'] },
                  '$$p',
                  false,
                ]
              }
            }
          },
          [false]
        ]
      }
    }
  },
  { $sort: { 'points.distanceToSpawn': 1 } },
]);

在MongoDB 2.6版本之前,或者效率较低:

db.maps.aggregate([
    { "$match": { "points.type": "type1" }},
    { "$unwind": "$points" },
    { "$match": { "points.type": "type1" }},
    { "$group": {
        "_id": "$_id",
        "points": { "$push": "$points" }
    }},
    { "$sort": { "points.ditanceToSpawn": 1 } }         
])

这是唯一能够匹配正确元素并在“排序”操作中将其考虑在内的方式。默认的光标排序将会考虑数组元素中那些与您选择的“类型”不匹配的字段的值。


请记住,在使用 $sort 操作符之前使用 $unwind 操作符时,它无法利用索引。(我在尝试按数组字段排序时遇到了相同的问题,总是使用数组的第一个项目进行比较。) - phwa4563
9
针对“现代MongoDB版本”的示例,sort是否应该基于order.distanceToSpawn而不仅是order?如果不是,为什么? - Graham Clark
2
你会为这个排序创建什么样的索引? - jadus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接