좌표로 구성되는 2D Index 입니다.
하나의 Collection에는 하나의 2D Index만 구성할 수 있습니다.
요즘 SNS나 스마트폰용 App에서 자신의 위치에서 가까운 주유소 찾기 같은 기능을 편리하게 구현할 수 있습니다.
먼저 2D 값을 가진 Collection을 하나 생성해 보겠습니다.
for (var i = 0 ; i < 100 ; i ++) db.spatial.insert( { pos : [ i % 10 , Math.floor( i / 10 ) ] } ) WriteResult({ "nInserted" : 1 }) db.spatial.createIndex( { pos : "2d" } ) { "createdCollectionAutomatically" : true, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } db.spatial.getIndexes() [ ... { "v" : 1, "key" : { "pos" : "2d" }, "name" : "pos_2d", "ns" : "test.spatial" } ] db.spatial.find() { "_id" : ObjectId("55c3e2142b0ae13631c7f0a5"), "pos" : [ 0, 0 ] } { "_id" : ObjectId("55c3e2142b0ae13631c7f0a6"), "pos" : [ 1, 0 ] } ... { "_id" : ObjectId("55c3e2142b0ae13631c7f107"), "pos" : [ 8, 9 ] } { "_id" : ObjectId("55c3e2142b0ae13631c7f108"), "pos" : [ 9, 9 ] } |
1.1 $near 비교 연산자
주어진 좌표에서 가장 가까운 좌표를 검색합니다.
좀 더 정확히 설명하자면 가장 가까운 좌표부터 Sorting 하여 보여줍니다.
.limit(n) 와 함께 사용하면 가장 가까운 좌표 몇개를 보여주는 식으로 활용을 할 수 있습니다.
db.spatial.find( { pos : { $near : [ 5, 5] } } ).limit(5) { "_id" : ObjectId("55c3e2142b0ae13631c7f0dc"), "pos" : [ 5, 5 ] } { "_id" : ObjectId("55c3e2142b0ae13631c7f0db"), "pos" : [ 4, 5 ] } { "_id" : ObjectId("55c3e2142b0ae13631c7f0e6"), "pos" : [ 5, 6 ] } { "_id" : ObjectId("55c3e2142b0ae13631c7f0d2"), "pos" : [ 5, 4 ] } { "_id" : ObjectId("55c3e2142b0ae13631c7f0dd"), "pos" : [ 6, 5 ] } |
1.2 $center 비교 연산자
해당 좌표(x , y) 기준으로 해당 거리(r) 안에 있는 좌표들을 검색합니다.
(x,y) 에서 r의 반지름을 가진 원을 그려서 그 안에 있는 좌표들을 보여준다고 생각하시면 머리로 그림이 그려질 것입니다.
db.spatial.find( { pos : { $within : { $center : [ [ 3.7 , 7.6] , 2.5] } } }, { _id : 0 } ) { "pos" : [ 2, 6 ] } { "pos" : [ 2, 7 ] } ... { "pos" : [ 5, 9 ] } { "pos" : [ 6, 7 ] } { "pos" : [ 6, 8 ] } |
1.3 $box 비교 연산자
2개의 점을 받아서 그 2점을 대각선으로 한 Rectangle 내부의 점들을 검색합니다.
db.spatial.find( { pos : { $within : { $box : [ [ 2 , 2] , [ 3, 4] ] } } }, { _id : 0 } ) { "pos" : [ 2, 2 ] } { "pos" : [ 2, 3 ] } { "pos" : [ 2, 4 ] } { "pos" : [ 3, 2 ] } { "pos" : [ 3, 3 ] } { "pos" : [ 3, 4 ] } |
1.4 $polygon 비교 연산자
여러 개의 점으로 연결된 Polygon 내부의 점들을 검색합니다.
아래 예제는 아래위로 긴 모양의 사각형에서 대놓고 완전 티나게 (2,2)를 빼고자 해당 좌표 근처에서 살짝 안으로 들어간 모양입니다.
db.spatial.find( { pos : { $within : { $polygon : [ [ 1,1] , [ 1,3] , [2,3] , [1.999,2] , [2,1] ] } } }, { _id : 0 } ) { "pos" : [ 1, 1 ] } { "pos" : [ 1, 2 ] } { "pos" : [ 2, 1 ] } { "pos" : [ 1, 3 ] } { "pos" : [ 2, 3 ] } |
1.5 $centerSphere 비교 연산자
$center 연산자와 사용법이 같습다만, GeoJSON Object에 대해서도 적용이 가능합니다.
물론 $center에서 사용한 legacy coordinate pair에도 사용이 가능합니다.
geoJSON에 대한 자세한 설명은 ( http://geojson.org/geojson-spec.html ) 를 참조하시기 바랍니다.
간단하게는 조금 뒤에 다루겠습니다.
한가지 예를 들어보겠습니다.
금요일 상암동 누리꿈스퀘어에 있는 회사 (126.8897, 37.5794)에서 퇴근 후
상암 월드컵경기장에 있는 홈플러스 (126.8982, 37.5672)에서 장을 본 후
바로 옆 CGV (126.8978, 37.5689)에서 영화를 보고
집 (126.9032, 37.5655)에 가서 발닦고 잤습니다.
그 4곳의 장소에서 여려 명의 사람들과 통화를 하였을 경우 통화목록이 다음과 같이 저장이 되었습니다.
|
db.tel_pos.save( { pno : "010-1234-5678" , pos : [ [ 126.8897 , 37.5794 ] , [ 126.8978 , 37.5689 ] ] } ) db.tel_pos.save( { pno : "010-9876-5432" , pos : [ [ 126.8897 , 37.5794 ] , [ 126.8982 , 37.5672 ] , [ 126.9032 , 37.5655 ] ] } ) db.tel_pos.save( { pno : "010-9999-8888" , pos : [ [ 126.8897 , 37.5794 ] ] } ) db.tel_pos.createIndex({ pos : "2d" } ) db.tel_pos.find() { "_id" : ObjectId("55c3f3a32b0ae13631c7f109"), "pno" : "010-1234-5678", "pos" : [ [ 126.8897, 37.5794 ], [ 126.8978, 37.5689 ] ] } { "_id" : ObjectId("55c3f4062b0ae13631c7f10a"), "pno" : "010-9876-5432", "pos" : [ [ 126.8897, 37.5794 ], [ 126.8982, 37.5672 ], [ 126.9032, 37.5655 ] ] } { "_id" : ObjectId("55c3f4332b0ae13631c7f10b"), "pno" : "010-9999-8888", "pos" : [ [ 126.8897, 37.5794 ] ] } |
db.tel_pos.find( { pos : { $within : { $centerSphere : [ [ 126.9032 , 37.5655] , 1 / 3963 ] } } }, { _id : 0 , pno : 1, pos : 1 } ) { "pno" : "010-9876-5432", "pos" : [ [ 126.8897, 37.5794 ], [ 126.8982, 37.5672 ], [ 126.9032, 37.5655 ] ] } { "pno" : "010-1234-5678", "pos" : [ [ 126.8897, 37.5794 ], [ 126.8978, 37.5689 ] ] } |
1.6 $nearSphere 비교 연산자
$near 연산자와 사용법이 같습니다만, GeoJSON Object에 대해서도 적용이 가능합니다.
그럼 마포구청 (126.9015, 37.5660)에서 가장 가까운 곳에서 통화한 사람을 찾는 방법은 다음과 같습니다.
db.tel_pos.find( { pos : { $nearSphere : [ 126.9015 , 37.5660] } }, { _id : 0 , pno : 1, pos : 1 } ).limit(1) { "pno" : "010-9876-5432", "pos" : [ [ 126.8897, 37.5794 ], [ 126.8982, 37.5672 ], [ 126.9032, 37.5655 ] ] } |
2. GeoMetry Index
geoJSON에 의해 표현되는 여러가지 형태 ( 선, 직선, 다각형 ) 로 생성한 Index 입니다.
MongoDB에서는 3가지 타입의 geoJSON에 대한 GeoMetry Index를 제공합니다.
- Point 타입
{ type : "Point" , coordinates : [ 127.1058, 37.5164] } // 잠실역 좌표 |
- LineString 타입
{ type : "LineString" , coordinates : [ [ 127.1058, 37.5164] , [127.0847, 37.5105] } // 잠실역 <-> 삼성역 |
- Polygon 타입
{ type : "Polygon" , coordinates : [ [ [127.1261, 37.5191] , [127.1220, 37.5221] , [127.1225, 37.5240] , [127.1270, 37.5231] , [127.1290, 37.5180] , [127.1240, 37.5117] , [127.1261, 37.5191] ] ] } // 올림픽공원을 둘러싼 7각 Polygon |
2.1 Point Type
먼저 예제로 사용할 collection을 입력하겠습니다.
db.pos.insert( { _id : "m01" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.1058, 37.5164] } , name : [ "name=잠실역 2호선" , "trans_type=지하철" ] } ) db.pos.insert( { _id : "m02" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.0980, 37.5301] } , name : [ "name=동서울 터미널" , "trans_type=버스" ] } ) ) db.pos.insert( { _id : "m03" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.0952, 37.5398] } , name : [ "name=강변역 2호선" , "trans_type=지하철" ] } ) db.pos.insert( { _id : "m04" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.0742, 37.5419] } , name : [ "name=건대역 2호선" , "trans_type=지하철" ] } ) db.pos.createIndex( { loc : "2dsphere" } ) db.pos.find() { "_id" : "m01", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.1058, 37.5164 ] }, "name" : [ "name=잠실역 2호선", "trans_type=지하철" ] } { "_id" : "m02", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.098, 37.5301 ] }, "name" : [ "name=동서울 터미널", "trans_type=버스" ] } { "_id" : "m03", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0952, 37.5398 ] }, "name" : [ "name=강변역 2호선", "trans_type=지하철" ] } { "_id" : "m04", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0742, 37.5419 ] }, "name" : [ "name=건대역 2호선", "trans_type=지하철" ] } |
잠실역을 기준으로 2000m (2 Km) 이내의 Point를 검색하는 방법은 다음과 같습니다.
db.pos.find( { loc : { $near : { $geometry : { type : "Point" , coordinates : [ 127.1058, 37.5164] } , $maxDistance: 2000 } } } ).pretty() { "_id" : "m01", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.1058, 37.5164 ] }, "name" : [ "name=잠실역 2호선", "trans_type=지하철" ] } { "_id" : "m02", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.098, 37.5301 ] }, "name" : [ "name=동서울 터미널", "trans_type=버스" ] } |
2.2 LineString Type
앞서 사용한 pos collection에 3개의 Point를 더 추가 하겠습니다.
db.pos.insert( { _id : "m05" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.0847, 37.5120] } , name : [ "name=신천역 2호선" , "trans_type=지하철" ] } ) db.pos.insert( { _id : "m06" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.0740, 37.5133] } , name : [ "name=종합운동장역 2호선" , "trans_type=지하철" ] } ) db.pos.insert( { _id : "m07" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.0848, 37.5105] } , name : [ "name=삼성역 2호선" , "trans_type=지하철" ] } ) db.pos.find() { "_id" : "m01", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.1058, 37.5164 ] }, "name " : [ "name=잠실역 2호선", "trans_type=지하철" ] } { "_id" : "m02", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.098, 37.5301 ] }, "name" : [ "name=동서울 터미널", "trans_type=버스" ] } { "_id" : "m03", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0952, 37.5398 ] }, "name " : [ "name=강변역 2호선", "trans_type=지하철" ] } { "_id" : "m04", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0742, 37.5419 ] }, "name " : [ "name=건대역 2호선", "trans_type=지하철" ] } { "_id" : "m05", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0847, 37.512 ] }, "name" : [ "name=신천역 2호선", "trans_type=지하철" ] } { "_id" : "m06", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.074, 37.5133 ] }, "name" : [ "name=종합운동장역 2호선", "trans_type=지하철" ] } { "_id" : "m07", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0848, 37.5105 ] }, "name " : [ "name=삼성역 2호선", "trans_type=지하철" ] } |
잠실역 - 신천역 - 종합운동장역 - 삼성역을 지나는 선분에 인접(Intersect) 한 geoJSON을 찾는 방법은 다음과 같습니다.
db.pos.find( { loc : { $geoIntersects : { $geometry : { type : "LineString" , coordinates : [ [127.1058, 37.5164], [127.0847, 37.5120], [127.0740, 37.5133], [127.0848, 37.5105] ] } } } } ).pretty() { "_id" : "m06", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.074, 37.5133 ] }, "name" : [ "name=종합운동장역 2호선", "trans_type=지하철" ] } { "_id" : "m01", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.1058, 37.5164 ] }, "name" : [ "name=잠실역 2호선", "trans_type=지하철" ] } { "_id" : "m07", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0848, 37.5105 ] }, "name" : [ "name=삼성역 2호선", "trans_type=지하철" ] } |
2.3 Polygon Type
2개의 Point를 더 추가하겠습니다.
db.pos.insert( { _id : "m08" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.1261, 37.5141] } , name : [ "name=올림픽 수영장" , "trans_type=Public Sport" ] } ) db.pos.insert( { _id : "m09" , data_type : NumberLong(1), loc : { type : "Point" , coordinates : [127.1225, 37.5240] } , name : [ "name=카페" , "trans_type=Resturant" ] } ) db.pos.find() { "_id" : "m01", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.1058, 37.5164 ] }, "name " : [ "name=잠실역 2호선", "trans_type=지하철" ] } { "_id" : "m02", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.098, 37.5301 ] }, "name" : [ "name=동서울 터미널", "trans_type=버스" ] } { "_id" : "m03", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0952, 37.5398 ] }, "name " : [ "name=강변역 2호선", "trans_type=지하철" ] } { "_id" : "m04", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0742, 37.5419 ] }, "name " : [ "name=건대역 2호선", "trans_type=지하철" ] } { "_id" : "m05", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0847, 37.512 ] }, "name" { "_id" : "m06", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.074, 37.5133 ] }, "name" : [ "name=종합운동장역 2호선", "trans_type=지하철" ] } { "_id" : "m07", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.0848, 37.5105 ] }, "name " : [ "name=삼성역 2호선", "trans_type=지하철" ] } : [ "name=신천역 2호선", "trans_type=지하철" ] } { "_id" : "m08", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.1261, 37.5141 ] }, "name " : [ "name=올림픽 수영장", "trans_type=Public Sport" ] } { "_id" : "m09", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.1225, 37.524 ] }, "name" : [ "name=카페", "trans_type=Resturant" ] } |
올림픽 공원 내에 위치한 geoJSON을 찾는 방법은 다음과 같습니다.
db.pos.find( { loc : { $geoWithin : { $geometry : { type : "Polygon" , coordinates : [[ [ 127.1261, 37.5191] , [127.1220, 37.5221] , [127.1225, 37.5240] , [127.1270, 37.5231] , [127.1290, 37.5180] , [127.1240, 37.5117] , [127.1261, 37.5191]] ]} } } } ).pretty() { "_id" : "m09", "data_type" : NumberLong(1), "loc" : { "type" : "Point", "coordinates" : [ 127.1225, 37.524 ] }, "name" : [ "name=카페", "trans_type=Resturant" ] } |
댓글 없음:
댓글 쓰기