개발과 기록의 조화

[MongoDB] 위치 기반 쿼리 본문

Database/MongoDB

[MongoDB] 위치 기반 쿼리

dlaudtjr03 2020. 2. 3. 13:02
해당 게시물은 학습 및 기록 목적으로 작성되었습니다. 사실과 다른 내용이 있을 수 있으며, 오류가 있거나 궁금한 점은 댓글이나 dlaudtjr07@gmail.com 으로 메일 주시면 감사하겠습니다.


위치 기반 쿼리

MongoDB에서는 위치 기반 데이터 관련 쿼리 또한 지원하는데, 보통 위치 정보를 저장할 때는 위도, 경도를 사용합니다. 이러한 데이터를 가지고 특정한 값들을 찾는 경우가 종종 있습니다.

 

위치 기반 데이터

 

MongoDB에서는 위치 기반의 데이터를 GeoJSON 객체 혹은 레거시 좌표점(legacy coortdinate pairs)의 형태로 저장하는데, 둘의 차이점을 정확하게 몰라 찾아봤습니다.

 

https://stackoverflow.com/questions/20016269/what-is-difference-between-geojson-legacy-coordinate-pairs-in-terms-of-mongodb

 

What is difference between GeoJSON & Legacy coordinate pairs in terms of mongoDb?

I am trying to use $geoNear aggregation operator of mongoDb to calculate distances of users from current location in following way : '$geoNear': { near: currentLocation, distanceField: 'dist...

stackoverflow.com

스택 오버플로우에 두 형태의 차이점에 대해 답변이 올라와 있는데, 산술식의 차이가 있다고 답변합니다. 자세한 내용은 위의 링크를 참고하시면 되겠습니다.

 

GeoJson의 형태는 다음과 같습니다.

<Field> : {<type>:<GeoJSON type>, cordinates : coordinates>}

 

Type에는 Point, Polygon, MultiPoint 등의 여러 타입이 존재합니다만, MongoDB 공식 도큐먼트에서는 Point를 예제로 진행합니다.

 

 

위치 기반 인덱스

 

지형 정보에 대한 쿼리를 지원하기 위한 인덱스입니다. 2가지의 종류로 나뉘어집니다. 위치 기반 데이터를 통해서 쿼리를 진행할 때에는 반드시 인덱스 설정이 필요합니다.

종류 설명
2dsphere 지구와 같은 구 형태의 지형을 기반으로 계산
2d x,y출의 평면 지형을 기반으로 계산

아래와 같이 인덱스에 2d 혹은 2dsphere 옵션으로 위치 기반 인덱스를 걸어 줄 수 있습니다. location field는 GeoJSON 혹은 레거시 좌표점의 데이터입니다.  

db.<collection>.createIndex( { <location field> : "<2d or 2dsphere>" } )

 

 

위치 기반 쿼리 연산자

 

MongoDB는 아래와 같은 위치 기반 쿼리 연산자를 제공합니다.

연산자 설명
$geoIntersects

주어진 영역과 Document들의 영역에 교집합이 있었는지의 여부를 찾아주는 연산자. GeoJSON 데이터만 허용, 2dsphere 인덱스에서 지원.

$geometry 표현식을 이용해 이를 정의함

$geoWithin

입력한 데이터 영역 안에 포함된 Document를 반환하는 연산자. GeoJSON, 레거시 좌표점 둘 다 이용 가능

GeoJSON은 $geometry로 정의

레거시 좌표점은 $box,$polygom,$center,$centerSphere로 정의

$near

입력한 데이터 영역 안에 포함된 Document들을 가까운 순서대로 정렬해 반환.

GeoJSON은 2dsphere 인덱스를 이용

레거시 좌표점은 2d 인덱스를 이용

$nearSphere 구형 지형 검색 시 사용

 

또한 MongoDB는 $geoNear 연산자를 통한 aggregation 쿼리를 이용해 다양한 옵션 설정을 통한 파이프라인 구성이 가능합니다.

{ $geoNear: { <geoNear options> } }
필수 옵션 설명
spherical

true 설정 시 $nearSphere 방식으로 계산

false 설정 시 #near 방식으로 게산

distanceField 거리를 출력할 필드명
near

기준 좌표점

2dsphere 인덱스는 GeoJSON 형식을 이용

2d 인덱스는 레거시 좌표점 형식을 이용

선택 옵션 설명
limit 가져올 문서의 최대 개수. Default : 100
num limit과 같은 기능. 둘 다 설정시 num 우선 적용
maxDistance

검색 최대 거리 지정

GeoJSON       : M 단위

레거시 좌표점 : radian 단위

query Document 필터링
distanceMultiplier

구한 거리 결과에 추가 연산 실시

얻은 결과를 입맞게 맞게 변환 가능

includeLocs 거리 계산에 사용된 좌표점을 출력한 필드
minDistance

검색 최소 거리

설정 시 이 거리 이하의 Document는 제외시킴

key

인덱스가 설정된 필드를 지정

하나의 컬렉션의 인덱스가 여러 필드로 지정되어져 있다면 필

수 옵션

위에서 설명한 $geoNear 연산자는 다른 특수 인덱스(ex:TEXT 인덱스)와는 같이 사용할 수 없습니다. 이에 aggregation 파이프라인 설정에서 가장 먼저 정의되어야 합니다. 

 

 

예제 데이터 삽입

 

예제 데이터를 삽입해 봅시다.

db.places.insert(
	{
		name:"Central Park",
		location:{type:"Point",coordinates:[-73.97,40.77]},
		category:"Parks"
    }     
)
db.places.insert(
	{
		name:"Sara D. Roosevelt Park",
		location:{type:"Point",coordinates:[-73.9928,40.7193]},
		category:"Parks"
    }
)
db.places.insert(
	{
		name:"Polo Grounds",
		location:{type:"Point",coordinates:[-73.9375,40.8303]},
		category:"Stadiums"
    }
)

 

데이터를 입력한 다음 location 필드에 2dsphere 옵션을 가진 인덱스를 생성해 봅시다.

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

 

그 다음에 $near 연산자를 이용, GeoJSON 데이터를 입력해 해당 데이터 지점에서 1000미터 이상 5000미터 이하의 데이터(Document)를 가장 가까운 것부터 오름차순으로 정렬해 반환하는 예제를 작성해 봅시다.

 

db.places.find(
	{
		location : {$near : {
			$geometry: {type : "Point" , coordinates:[-73.9667, 40.78]},
			$minDistance: 1000, $maxDistance: 5000}
		}
	}
)



//Result
{
        "_id" : ObjectId("5e378f318c3c78f313913916"),
        "name" : "Central Park",
        "location" : {
                "type" : "Point",
                "coordinates" : [
                        -73.97,
                        40.77
                ]
        },
        "category" : "Parks"
}

 

이번에는 geoNear 연산자를 이용해 쿼리의 필터와 일치하는 데이터(Document)를 설정한 GeoJSON 데이터 지점에서 가장 가까운 순서로 반환해 봅시다.

db.places.aggregate(
	[
		{
			$geoNear:{
				near:{
					type: "Point", coordinates: [-73.9667, 40.78]},
				spherical:true,
				query:{category:"Parks"},
				distanceField:"calcDistance"
				}
		}
	]
)

//Result

{
        "_id" : ObjectId("5e378f318c3c78f313913916"),
        "name" : "Central Park",
        "location" : {
                "type" : "Point",
                "coordinates" : [
                        -73.97,
                        40.77
                ]
        },
        "category" : "Parks",
        "calcDistance" : 1147.4220523120937
}
{
        "_id" : ObjectId("5e378f528c3c78f313913917"),
        "name" : "Sara D. Roosevelt Park",
        "location" : {
                "type" : "Point",
                "coordinates" : [
                        -73.9928,
                        40.7193
                ]
        },
        "category" : "Parks",
        "calcDistance" : 7106.506152783147
}

 

 


해당 글은 코드프레소 DevOps Roasting 코스를 수강하면서 작성한 글입니다.

 

Comments