지리공간 쿼리
지리공간 쿼리 (Geospatial Queries)
'내 주변 카페 찾기' 같은 기능을 만든다면, 위치 데이터를 어떻게 저장하고 검색할지 고민하게 되죠. MongoDB는 지리공간 데이터를 위한 쿼리 연산을 지원해요. 이 페이지에서는 MongoDB의 지리공간 기능 전반을 소개할게요.
본문
지리공간 데이터 저장하기
MongoDB에서는 지리공간 데이터를 GeoJSON 객체로 저장하거나, 레거시 좌표 쌍으로 저장할 수 있어요.
GeoJSON 객체
지구 같은 구면(sphere) 위의 기하 연산을 하려면 위치 데이터를 GeoJSON 객체로 저장해요. GeoJSON 데이터는 type(GeoJSON 객체 타입)과 coordinates(객체 좌표) 필드를 가진 내장 문서로 지정해요.
<field>: { type : <GeoJSON type>, coordinates: <coordinates> }
위도·경도를 지정할 때는 경도(longitude)를 먼저, 그다음 **위도(latitude)**를 적어요. 경도는 -180180(양 끝 포함), 위도는 -9090(양 끝 포함)이 유효해요. GeoJSON Point 예시예요.
location: { type: "Point", coordinates: [-73.856077, 40.848447] }
MongoDB는 GeoJSON 객체에 대한 지리공간 쿼리를 구면 위에서 계산하며, WGS84 참조계를 사용해요.
레거시 좌표 쌍
평면(유클리드) 계산에는 2d 인덱스와 함께 레거시 좌표 쌍을 사용해요. 구면 계산을 하려면 레거시 쌍을 GeoJSON Point로 변환하고 2dsphere 인덱스를 써요.
레거시 좌표 쌍은 배열(권장)이나 내장 문서로 지정할 수 있어요. 위도·경도를 쓸 때 역시 경도를 먼저, 위도를 나중에 적어요.
<field>: [ <longitude>, <latitude> ]
언어에 따라 객체 필드 순서가 보존되지 않을 수 있으니 배열을 권장해요.
지리공간 인덱스
지리공간 인덱스는 GeoJSON 객체나 레거시 좌표 쌍으로 저장된 데이터를 대상으로 한 쿼리를 지원해요. MongoDB는 두 종류를 제공해요.
- 2d 인덱스 — 평면 위에 기하를 해석하는 쿼리를 지원해요.
- 2dsphere 인덱스 — 구면 위에 기하를 해석하는 쿼리를 지원해요.
지리공간 쿼리 연산자
$geoIntersects— 주어진 GeoJSON 기하와 교차하는 기하를 선택해요. 2dsphere 인덱스가 필요해요.$geoWithin— 주어진 경계 GeoJSON 기하 안에 들어오는 기하를 선택해요. 2dsphere와 2d 인덱스가 모두 지원해요.$near— 특정 지점의 근처에 있는 지리공간 객체를 반환해요. 지리공간 인덱스가 필요하며, 2dsphere와 2d가 지원해요.$nearSphere— 구면 위의 특정 지점 근처에 있는 지리공간 객체를 반환해요. 지리공간 인덱스가 필요하며, 2dsphere와 2d가 지원해요.
참고로 2d 인덱스를 구면 데이터 쿼리에 쓰면 잘못된 결과나 오류가 날 수 있어요. 예를 들어 2d 인덱스는 극을 가로지르는 구면 쿼리를 지원하지 않아요.
같은 location 서브문서에 레거시 좌표 필드와 GeoJSON 객체가 함께 있다면 인덱싱 방식이 서버 버전에 따라 달라져요. MongoDB 8.2 이전은 필드 순서상 먼저 나오는 지원 표현을 인덱싱하고, 8.2부터는 필드 순서와 무관하게 서브문서에 GeoJSON 객체가 있으면 항상 GeoJSON 표현을 인덱싱해요.
지리공간 집계 스테이지
$geoNear 집계 스테이지는 지리공간 지점에 대한 근접도에 따라 문서의 정렬된 스트림을 반환해요. $match, $sort, $limit의 기능을 지리공간 데이터에 통합한 것이라 생각하면 돼요. 출력 문서에는 추가 거리 필드와 위치 식별자 필드가 포함될 수 있어요. $geoNear는 지리공간 인덱스를 요구해요.
지리공간 모델
MongoDB 지리공간 쿼리는 평면 위 또는 구면 위의 기하를 해석할 수 있어요. 2dsphere 인덱스는 구면 쿼리만 지원하고, 2d 인덱스는 평면 쿼리와 일부 구면 쿼리를 지원해요. 가능하면 구면 쿼리에는 2dsphere 인덱스를 쓰는 게 좋아요.
예제
예제는 sample_mflix 샘플 데이터셋의 sample_mflix.theaters 컬렉션을 사용해요. 이 컬렉션에는 2dsphere 인덱스가 있어요. 다음 쿼리는 $near 연산자로 지정한 GeoJSON 지점에서 가까운 순서대로 문서를 반환하는데, 최소 1000미터에서 최대 100000미터 사이의 문서를 돌려줘요.
db.theaters.find({
"location.geo": {
$near: {
$geometry: { type: "Point", coordinates: [-73.9667, 40.78] },
$minDistance: 1000,
$maxDistance: 100000
}
}
}).limit(3)
다음 연산은 $geoNear 집계 연산으로 쿼리 필터 { "location.address.state": "NY" }에 맞는 문서를 지정한 GeoJSON 지점에서 가까운 순으로 정렬해 반환해요.
db.theaters.aggregate([
{ $geoNear: {
near: { type: "Point", coordinates: [-73.9667, 40.78] },
spherical: true,
query: { "location.address.state": "NY" },
distanceField: "calcDistance"
}},
{ $limit: 3 }
])
더 알아보기
- Geospatial Indexes — 인덱스 상세
- GeoJSON Objects — 지원 GeoJSON 객체 목록
- 2d Indexes — 평면 2d 인덱스
- 2dsphere Indexes — 구면 2dsphere 인덱스