본문 바로가기

Database/mongoDB

MongoDB - Find Restaurants with Geospatial Queries

※ MongoDB 공식 매뉴얼에 나와있는 Find Restaurants with Geospatial Queries (예제 - 공간쿼리를 사용한 레스토랑 찾기) 부분을 번역해 보도록 하겠습니다. 번역을 안하는게 나을 것 같은 용어들은 한번만 번역하거나 그대로 두었습니다.

Overview

 

MongoDB 의 geospatial 인덱싱은 GeoJSON 객체 타입으로 구성된 콜렉션에 효율적으로 공간쿼리를 실행할 수 있도록 해줍니다. 이 튜토리얼은 간단한 geospatial 어플리케이션에서 쿼리 작성 과정을 통해 geospatial feature 의 기능들을 소개하고 다른 접근법들을 비교합니다.

 

이 튜토리얼은 geospatial index 의 개념에 대해 간단히 소개하고, $geoWithin / $geoIntersects / $nearSphere 에서의 사용법을 보여줄 것입니다.

 

뉴욕시에 있는 레스토랑들을 유저들이 찾을 수 있게 도와주는 mobile 어플리케이션을 디자인한다고 가정해 봅시다. 이 어플리케이션은 :

 

  • $geoIntersects 를 사용해 유저의 현재 지역을 결정해야 합니다.
  • $geoWithin 을 사용해 그 지역에 있는 레스토랑의 개수를 보여줘야합니다.
  • $nearSphere 을 사용해 유저로부터 지정된 거리 내에 있는 레스토랑들을 찾아야 합니다.

이 튜토리얼에서는 2dsphere index 를 사용해 spherical geometry 에서 데이터를 쿼리할 것입니다.

 

Geospatial Models 이 링크를 통해 spherical & flat geometries 에 대한 더 많은 정보를 확인할 수 있습니다.

 

Distortion (왜곡)

 

지도를 시각화할 때 spherical geometry 는 왜곡되서 보여지는데, 이는 지구와 같은 3차원 구체를 평평한 평면에 투사해야 하는 점 때문입니다.

 

예를 들어 경도 위도 포인트 - (0,0) / (80,0) / (80,80) / (0,80) - 로 정의된 구형 사각형 스펙을 보면, 아래 그림과 같은 영역이 표시 될 것입니다.

 

 

Searching for Restaurants (레스토랑 검색)

Prerequisites (전제조건)

 

위의 두개 링크에서 데이터셋 예제를 다운받으세요. 여기엔 각각의 레스토랑과 지역 콜렉션이 포함되어 있습니다.

 

다운로드 한 데이터셋을 데이터베이스로 임포트 하십시오 :

 

mongoimport <path to restaurants.json> -c=restaurants
mongoimport <path to neighborhoods.json> -c=neighborhoods

 

geospatial index 는 거의 늘 $geoWithin$geoIntersects 쿼리 성능을 향상 시킵니다.

 

지리학적인 데이터이기 때문에 mongo shell 을 사용하여 각 콜렉션에 2dsphere index 를 생성합니다 :

 

db.restaurants.createIndex({ location: "2dsphere" })
db.neighborhoods.createIndex({ geometry: "2dsphere" })

 

Exploring the Data (데이터 살펴보기)

 

mongo shell 안에서 새로 생성된 레스토랑 콜렉션을 조회해보세요 :

 

db.restaurants.findOne()

 

이 쿼리는 다음과 같은 document 를 반환할 것입니다 :

 

{
   location: {
      type: "Point",
      coordinates: [-73.856077, 40.848447]
   },
   name: "Morris Park Bake Shop"
}

 

이 레스토랑 document 는 다음 그림에서 보여지는 위치와 일치합니다 :

 

 

튜토리얼에서 2dsphere index 를 사용하기때문에, geometry data 의 location 필드는 반드시 GeoJSON 포맷을 따라야 합니다.

 

이제 지역 콜렉션을 보겠습니다 :

 

db.neighborhoods.findOne()

 

이 쿼리는 다음과 같은 document 를 반환할 것입니다 :

 

{
   geometry: {
      type: "Polygon",
      coordinates: [[
         [ -73.99, 40.75 ],
         ...
         [ -73.98, 40.76 ],
         [ -73.99, 40.75 ]
      ]]
    },
    name: "Hell's Kitchen"
}

 

반환된 geometry 는 다음 그림에서 그려진 지역과 일치합니다 :

 

 

Find the Current Neighborhood (현재 지역 찾기)

 

유저의 모바일 기기가 꽤 정확한 유저 위치를 줄 수 있다고 가정하면, $geoIntersects 를 사용해 유저의 현재 지역을 찾는것은 간단합니다.

 

유저가 -73.93414657 (경도), 40.82302903 위도에 위치해있다고 생각해 봅시다. 현재 지역을 찾기위해선, GeoJSON 포맷의 $geometry 필드를 사용해서 포인트를 명시해야 합니다.

 

db.neighborhoods.findOne({ geometry: { $geoIntersects: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] } } } })

 

이 쿼리는 다음과 같은 결과를 반환합니다 :

 

{
    "_id" : ObjectId("55cb9c666c522cafdb053a68"),
    "geometry" : {
        "type" : "Polygon",
        "coordinates" : [
            [
                [
                    -73.93383000695911,
                    40.81949109558767
                ],
                ...
            ]
        ]
    },
    "name" : "Central Harlem North-Polo Grounds"
}

 

Find all Restaurants in the Neighborhood  (지역 내의 모든 레스토랑 찾기)

 

주어진 지역내에 있는 모든 레스토랑을 찾는 쿼리 또한 가능합니다. 다음의 쿼리들은 유저를 포함한 지역을 찾은 후에 그 지역내의 레스토랑 수를 카운팅합니다 :

 

var neighborhood = db.neighborhoods.findOne( { geometry: { $geoIntersects: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] } } } } )
db.restaurants.find( { location: { $geoWithin: { $geometry: neighborhood.geometry } } } ).count()

 

이 쿼리는 아래 시각화된 그림에서 볼 수 있듯이 요청된 지역내에 127개의 레스토랑이 있다는 것을 보여줍니다 :

 

 

Find Restaurants within a Distance (거리 내에 있는 레스토랑 찾기)

 

점으로부터 명시된 거리 내의 모든 레스토랑을 찾기 위해선, $geoWithin$centerSphere 을 모두 사용해 정렬되지 않은 결과값을 받거나, 거리값으로 정렬된 결과를 원한다면 $nearSphere$maxDistance 를 사용해야 합니다.

 

Unsorted with $geoWithin ($geoWithin 을 사용한 정렬되지 않은 결과값)

 

원형의 지역내에 모든 레스토랑을 찾기 위해선, $geoWithin$centerSphere 를 모두 사용해야 합니다. $centerSphere 은 중심점과 라디안을 이용한 반지름 지정을 통해 원형 지역을 나타내는 MongoDB 만의 특별한 문법입니다.

 

$geoWithin 은 정렬된 형태로 document 를 반환하지 않습니다. 따라서 유저 기준 가장 먼 document 가 제일 처음에 보여질 수도 있습니다.

 

다음은 유저로부터 5마일 내에 있는 모든 레스토랑을 찾는 쿼리입니다 :

 

db.restaurants.find({ location:
   { $geoWithin:
      { $centerSphere: [ [ -73.93414657, 40.82302903 ], 5 / 3963.2 ] } } })

 

$centerSphere 의 두번째 인자로 라디안을 이용한 반지름 값을 받기 때문에, 지구의 반지름으로 해당 마일 값을 나눠줘야 합니다.

거리 단위에 대한 변환 정보는 Calculate Distance Using Spherical Geometry 이 링크에서 볼 수 있습니다.

 

Sorted with $nearSphere ($nearSphere 을 사용한 정렬된 결과값)

 

또한 미터단위로 $maxDistance 를 명시해 $nearSphere 을 사용할 수도 있습니다. 다음은 유저 기준 5마일 내의 모든 레스토랑을 가까운 것에서 먼 순서로 정렬된 결과값을 받는 쿼리입니다 :

 

var METERS_PER_MILE = 1609.34
db.restaurants.find({ location: { $nearSphere: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] }, $maxDistance: 5 * METERS_PER_MILE } } })

 

출처 : https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/

 

Find Restaurants with Geospatial Queries — MongoDB Manual

Distortion Spherical geometry will appear distorted when visualized on a map due to the nature of projecting a three dimensional sphere, such as the earth, onto a flat plane. For example, take the specification of the spherical square defined by the longit

docs.mongodb.com

 

'Database > mongoDB' 카테고리의 다른 글

$geoWithin  (0) 2020.03.31
$geoIntersects  (0) 2020.03.25
Geospatial Query Operators (Geospatial 쿼리 연산자)  (0) 2020.03.25
GeoJSON Objects (GeoJSON 객체)  (0) 2020.03.18
MongoDB - Geospatial Queries (지리공간 쿼리)  (0) 2020.03.02