В чем разница между парами координат GeoJSON и Legacy в терминах mongoDb?

Я пытаюсь использовать оператор агрегирования $geoNear для mongoDb, чтобы вычислить расстояния пользователей от текущего местоположения следующим образом:

'$geoNear': {
   near: currentLocation,
   distanceField: 'distance',
   spherical: true,
}

С currentLocation что-то вроде:

{ "type" : "Point", "coordinates" : [  -122.1575745,  37.4457966 ] }

Моя коллекция имеет следующий тип (используя мангуста):

users = [{
  ....
  location : {                   // GeoJSON Point or I think it is ;)
            type: { 
                type: String
            },
            coordinates: []
        }
  ....
}]

Я использую индекс (снова синтаксис Мангуста):

userSchema.index({
  location: '2dsphere'
});

Теперь ПРОБЛЕМА, с которой я сталкиваюсь, заключается в том, что, если я запрашиваю с использованием currentLocation, как упомянуто выше (в форме GeoJSON), я получаю странные расстояния (очень большие числа), но если я использую currentLocation.coordinates, то есть используя устаревшие пары координат ([-122.1575745, 37.4457966]), я получаю правильный результат. Но документы mongoDb для geoNear ясно говорят о том, что мы можем выполнять запросы, используя как точки GeoJSON, так и устаревшие пары координат.

Мне любопытно узнать, в чем именно разница между точками GeoJSON и устаревшими парами координат?

Например, коллекция:

{ "_id" : ObjectId("5277679914c6d8f00b000003"), "location" : { "type" : "Point", "coordinates" : [  106.6202887,  -6.1293536 ] } }
{ "_id" : ObjectId("5277810148219d011c000003"), "location" : { "type" : "Point", "coordinates" : [  106.6202887,  -6.1293536 ] } }
{ "_id" : ObjectId("5281c7ba2dfd7bdc64000003"), "location" : { "type" : "Point", "coordinates" : [  -86.9248483,  33.4480108 ] } }
{ "_id" : ObjectId("5281c8b82dfd7bdc64000004"), "location" : { "type" : "Point", "coordinates" : [  -74.0087126,  40.7136487 ] } }
{ "_id" : ObjectId("5281c9782dfd7bdc64000005"), "location" : { "type" : "Point", "coordinates" : [  -122.1575745,  37.4457966 ] } }

Неверный результат:

[{"location":{"type":"Point","coordinates":[-122.1575745,37.4457966]},"dis":13.69288259318155},
 {"location":{"type":"Point","coordinates":[-86.9248483,33.4480108]},"dis":12697164592.388557},
 {"location":{"type":"Point","coordinates":[-74.0087126,40.7136487]},"dis":16328789117.58145},
 {"location":{"type":"Point","coordinates":[106.6202887,-6.1293536]},"dis":55446284682.14049},
 {"location":{"type":"Point","coordinates":[106.6202887,-6.1293536]},"dis":55446284682.14049}] 

1 ответ

Давайте создадим несколько примеров документов и геопространственный индекс:

> db.foo.insert({name: "Warsaw", location: {"type" : "Point", "coordinates" : [21.016667, 52.233333]}})
> db.foo.insert({name: "Stockholm", location: {"type" : "Point", "coordinates" : [18.068611, 59.329444]}})
> db.foo.ensureIndex({"location": "2dsphere"})

Расстояние между Варшавской ЛП и Стокгольмской ЮВ составляет где-то около 810 км, поэтому давайте проверим, работает ли она, как ожидалось. Сначала мы можем получить документ для Стокгольма.

> Stockholm = db.foo.findOne({name: "Stockholm"})

Теперь мы можем запустить запрос, используя geoNear:

> db.runCommand({ geoNear: 'foo', near: Stockholm.location.coordinates,  spherical: true })
{
    "ns" : "test.foo",
    "results" : [
        {
            "dis" : 6.558558954334308e-10,
            "obj" : {
                "_id" : ObjectId("52876ab0b12c6fc62f5d9311"),
                "name" : "Stockholm",
                "location" : {
                    "type" : "Point",
                    "coordinates" : [
                        18.068611,
                        59.329444
                    ]
                }
            }
        },
        {
            "dis" : 0.12715355275490586,
            "obj" : {
                "_id" : ObjectId("5287697eb12c6fc62f5d9310"),
                "name" : "Warsaw",
                "location" : {
                    "type" : "Point",
                    "coordinates" : [
                        21.016667,
                        52.233333
                    ]
                }
            }
        }
    ],
    "stats" : {
        "time" : 9,
        "nscanned" : 3,
        "avgDistance" : 0.06357677670538088,
        "maxDistance" : 0.12715355275490586
    },
    "ok" : 1
}

Расстояние между Стокгольмом и Стокгольмом близко к 0 как и ожидалось. Расстояние между Стокгольмом и Варшавой составляет 0.12715355275490586, Когда вы выполняете запрос, используя устаревшие пары координат, вы получаете результат в радианах, поэтому мы должны умножить это значение на радиус Земли:

> 0.12715355275490586 * 6371.0
810.0952846015052

Все идет нормально. Давайте проверим, получим ли мы похожий результат, используя geojson в качестве запроса.

> db.runCommand({ geoNear: 'foo', near: Stockholm.location,  spherical: true })
{
    "ns" : "test.foo",
    "results" : [
        {
            "dis" : 0.004183114486663965,
            "obj" : {
                "_id" : ObjectId("52876ab0b12c6fc62f5d9311"),
                "name" : "Stockholm",
                "location" : {
                    "type" : "Point",
                    "coordinates" : [
                        18.068611,
                        59.329444
                    ]
                }
            }
        },
        {
            "dis" : 810998.0748260651,
            "obj" : {
                "_id" : ObjectId("5287697eb12c6fc62f5d9310"),
                "name" : "Warsaw",
                "location" : {
                    "type" : "Point",
                    "coordinates" : [
                        21.016667,
                        52.233333
                    ]
                }
            }
        }
    ],
    "stats" : {
        "time" : 4,
        "nscanned" : 3,
        "avgDistance" : 405499.0395045898,
        "maxDistance" : 810998.0748260651
    },
    "ok" : 1
}

Расстояние между Стокгольмом и Стокгольмом снова близко к 0, Расстояние между Стокгольмом и Варшавой - 810998.0748260651. При выполнении запроса geoNear с использованием GeoJSON расстояния рассчитываются в метрах. 810998.0748260651 примерно равен 810 км, так что ничего странного здесь нет.

> 810998.0748260651 / 1000
810.9980748260651

Разница между обоими решениями может быть меньше, но это всего лишь арифметика FP.

> Math.abs(810.0952846015052 - 810.9980748260651)
0.902790224559908

Когда вы используете find команда с $near оператор. Когда вы создаете простой 2d Индекс унаследованных пар координат можно запросить, используя оба {$near: Stockholm.location.coordinates} а также {$near: {$geometry: Stockholm.location}, Если у вас есть 2dsperhical только {$near: {$geometry: Stockholm.location} буду работать.

Другие вопросы по тегам