Mon goose Geo Near Search - Как сортировать на заданном расстоянии? - PullRequest
4 голосов
/ 21 апреля 2020

Я использую mon goose и ближайший запрос с maxDistance для фильтрации элементов рядом с заданным местоположением GPS. Однако ближайший запрос переопределяет другую сортировку. Я хотел бы найти все элементы в пределах 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 ]

3 голосов
/ 29 апреля 2020

В поисковом запросе вам нужно использовать 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);
});

. Для того чтобы $ был близок к работе, вам необходим индекс 2dsphere для связанной коллекции:

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

В mongodb $ вблизи документов указано:

$ вблизи, сортирует документы по расстоянию. Если вы также включите sort () для запроса, sort () переупорядочивает соответствующие документы, фактически переопределяя операцию сортировки, уже выполненную $ near. При использовании sort () с геопространственными запросами рассмотрите возможность использования оператора $ geoWithin, который не сортирует документы, вместо $ near.

Поскольку вам не нужна сортировка по расстоянию, например Ni c указывать с помощью $ 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);
});

Чтобы вычислить радиус, мы делим километр на 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 голос
/ 29 апреля 2020

$near сортирует документы по расстоянию, что является здесь ненужным. Вероятно, лучше использовать $geoWithin, который не сортирует документы. Что-то вроде:

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

Документы $center содержат более подробную информацию.

...