猫鼬地理附近搜索-如何在给定的距离内排序?

时间:2020-04-21 12:13:14

标签: mongodb mongoose geojson

我正在使用猫鼬和带有maxDistance的near查询来过滤靠近给定gps位置的元素。但是,near查询会覆盖其他排序。我想要的是找到给定点的maxDistance内的所有元素,然后按其他属性排序。 这是我目前正在做的一个例子:

模式:

mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    score: {
        type: Number,
        required: true,
        default: 0
    },
    location: {
        type: {
            type: String,
            default: 'Point',
        },
        coordinates: {
            type: [Number]
        }
    },
    ....
});

查询:

model.find({
  "location.coordinates": {
    "$near": {
      "$maxDistance": 1000,
      "$geometry": {
        "type": "Point",
        "coordinates": [
          10,
          10
        ]
      }
    }
  }
}).sort('-score');

在查找后添加.sort在这里无济于事,并且无论如何都会以接近的顺序返回项目。

2 个答案:

答案 0 :(得分:3)

在查找查询中,您需要使用location而不是location.coordinates

router.get("/test", async (req, res) => {
  const lat = 59.9165591;
  const lng = 10.7881978;
  const maxDistanceInMeters = 1000;

  const result = await model
    .find({
      location: {
        $near: {
          $geometry: {
            type: "Point",
            coordinates: [lng, lat],
          },
          $maxDistance: maxDistanceInMeters,
        },
      },
    })
    .sort("-score");

  res.send(result);
});

要使$ near工作,您需要在相关集合上使用2dsphere索引:

db.collection.createIndex( { "location" : "2dsphere" } )

在mongodb $near文档中它说:

$ near按距离对文档进行排序。如果您还包括一个sort() 查询sort()有效地对匹配文档进行重新排序 覆盖$ near已经执行的排序操作。使用时 使用地理空间查询进行sort(),请考虑使用$ geoWithin运算符, 不会对文档进行排序,而是对$ near进行排序。

由于您对按距离排序不感兴趣,因为Nic不需要使用$ near,因此最好像这样使用$geoWithin

router.get("/test", async (req, res) => {
  const lat = 59.9165591;
  const lng = 10.7881978;
  const distanceInKilometer = 1;
  const radius = distanceInKilometer / 6378.1;

  const result = await model
    .find({
      location: { $geoWithin: { $centerSphere: [[lng, lat], radius] } },
    })
    .sort("-score");

  res.send(result);
});

要计算半径,我们按照here所述将公里数除以6378.1,将英里数除以3963.2。

因此,它将找到半径1公里内的位置。

示例文档:

[
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.7741692,
                59.9262198
            ]
        },
        "score": 50,
        "_id": "5ea9d4391e468428c8e8f505",
        "name": "Name1"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.7736078,
                59.9246991
            ]
        },
        "score": 70,
        "_id": "5ea9d45c1e468428c8e8f506",
        "name": "Name2"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.7635027,
                59.9297932
            ]
        },
        "score": 30,
        "_id": "5ea9d47b1e468428c8e8f507",
        "name": "Name3"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.7635027,
                59.9297932
            ]
        },
        "score": 40,
        "_id": "5ea9d4971e468428c8e8f508",
        "name": "Name4"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.7768093,
                59.9287668
            ]
        },
        "score": 90,
        "_id": "5ea9d4bd1e468428c8e8f509",
        "name": "Name5"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.795769,
                59.9190384
            ]
        },
        "score": 60,
        "_id": "5ea9d4e71e468428c8e8f50a",
        "name": "Name6"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.1715157,
                59.741873
            ]
        },
        "score": 110,
        "_id": "5ea9d7d216bdf8336094aa92",
        "name": "Name7"
    }
]

输出:(在1公里之内并按降序排序)

[
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.7768093,
                59.9287668
            ]
        },
        "score": 90,
        "_id": "5ea9d4bd1e468428c8e8f509",
        "name": "Name5"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.7736078,
                59.9246991
            ]
        },
        "score": 70,
        "_id": "5ea9d45c1e468428c8e8f506",
        "name": "Name2"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.795769,
                59.9190384
            ]
        },
        "score": 60,
        "_id": "5ea9d4e71e468428c8e8f50a",
        "name": "Name6"
    },
    {
        "location": {
            "type": "Point",
            "coordinates": [
                10.7741692,
                59.9262198
            ]
        },
        "score": 50,
        "_id": "5ea9d4391e468428c8e8f505",
        "name": "Name1"
    }
]

答案 1 :(得分:1)

$near按距离对文档进行排序,这很浪费。最好使用不对文档进行排序的$geoWithin。像这样:

model.find({
  "location.coordinates": { 
     $geoWithin: { $center: [ [-74, 40.74], <radius> ] } } }
).sort({score: -1});

$center文档具有更多详细信息。