MongoDB에서 위치 기반 쿼리는 geoJson을 활용하여 처리합니다.
<field>: { type: <GeoJSON type> , coordinates: <coordinates> }
geoJson는 위 형태처럼 type, coordinates로 이루어져 있습니다. type은 Point, LineString, Polygon, MultiPolygon, MultiPoint 등 다양한 종류가 있습니다. coordinates는 위치의 위도와 경도를 다루는 영역으로 아래와 같습니다.
{ type: "Point", coordinates: [ 40, 5 ] }
coordinates의 배열에서 첫번째 요소가 경도에 해당하고, 두번째 요소가 위도에 해당합니다.
유효한 경도의 범위는 [ -180 ~ 180 ]
유효한 위도의 범위는 [ -90 ~ 90 ]
MongoDB에서 위치 쿼리를 활용하기 위해서는 인덱스 설정이 필요합니다. MongoDB에서 제공하는 위치기반 인덱스는 2가지 종류로 나누어집니다.
평면 좌표를 인덱싱하는데 사용합니다.
db.collection.createIndex({ location: "2d" })
지구 표면상의 데이터를 인덱싱하는데 사용합니다.
db.collection.createIndex({ location: "2dsphere" })
$geoIntersects 연산자는 지정된 지리적 객체(예: 포인트, 라인스트링, 폴리곤 등)가 데이터베이스에 저장된 지리적 객체와 겹치는(교차하는)지 여부를 확인합니다.
$geoWithin 연산자는 지정된 지리적 객체가 주어진 경계 내에 완전히 포함되는지 여부를 확인합니다.
$near 연산자는 특정 지점에서 가까운 문서를 찾는 데 사용됩니다. 이 연산자는 2D 지리적 인덱스를 사용하여 유클리드 평면(플랫 플레인) 거리로 계산합니다.
$nearSphere 연산자는 특정 지점에서 가까운 문서를 찾는 데 사용되며, 구면 거리(대지구 거리)를 계산합니다. 이는 지구의 곡률을 고려하여 거리를 측정합니다.
$geoNear는 어그리게이션 파이프라인의 단계로 주어진 지점에서 가까운 순서대로 문서를 반환합니다.
$geoNear 예시
db.places.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [ -73.9667, 40.78 ] },
distanceField: "dist.calculated",
maxDistance: 1000,
query: { category: "restaurant" },
includeLocs: "dist.location",
spherical: true
}
}
])
아래 코드는 특정 경도, 위도를 입력받아 근처의 장소를 찾는 코드입니다.
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
// Place represents a place with geospatial data
type Place struct {
Name string `json:"name" bson:"name"`
Tel string `json:"tel" bson:"tel"`
Location GeoJson `bson:"location" json:"location"`
}
// GeoJson represents a GeoJSON object
type GeoJson struct {
Type string `json:"type"`
Coordinates []float64 `json:"coordinates"`
}
// FindNearPoint finds places near a given point
func FindNearPoint(lat float64, lng float64) ([]*Place, error) {
// MongoDB connection settings
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
return nil, fmt.Errorf("client 생성 에러: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil {
return nil, fmt.Errorf("MongoDB 연결 에러: %v", err)
}
defer client.Disconnect(ctx)
// Ping the database to ensure connection
err = client.Ping(ctx, readpref.Primary())
if err != nil {
return nil, fmt.Errorf("DB 연결 에러: %v", err)
}
collection := client.Database("your_database").Collection("places")
// $geoNear aggregation stage
geoNearStage := bson.D{
{"$geoNear", bson.D{
{"near", bson.D{
{"type", "Point"},
{"coordinates", []float64{lng, lat}},
}},
{"distanceField", "dist.calculated"},
{"maxDistance", 1000},
{"spherical", true},
}},
}
cursor, err := collection.Aggregate(ctx, mongo.Pipeline{geoNearStage})
if err != nil {
return nil, fmt.Errorf("pipeline aggregation 에러 발생: %v", err)
}
var results []*Place
if err = cursor.All(ctx, &results); err != nil {
return nil, fmt.Errorf("결과값 디코딩 에러: %v", err)
}
return results, nil
}
func main() {
places, err := FindNearPoint(40.78, -73.9667)
if err != nil {
log.Fatalf("가까운 지점 찾기 실패: %v", err)
}
for _, place := range places {
fmt.Printf("Name: %s, Tel: %s, Location: %+v\n", place.Name, place.Tel, place.Location)
}
}