MongoDB 지리 공간적 쿼리 활용 (Go 예시)

beluga000·2024년 8월 5일
0
post-thumbnail

MongoDB 지리 공간적 쿼리

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가지 종류로 나누어집니다.

  1. 2d

평면 좌표를 인덱싱하는데 사용합니다.

db.collection.createIndex({ location: "2d" })
  1. 2dsphere

지구 표면상의 데이터를 인덱싱하는데 사용합니다.

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

지리 공간 쿼리 연산자

  1. $geoIntersects

$geoIntersects 연산자는 지정된 지리적 객체(예: 포인트, 라인스트링, 폴리곤 등)가 데이터베이스에 저장된 지리적 객체와 겹치는(교차하는)지 여부를 확인합니다.

  1. $geoWithin

$geoWithin 연산자는 지정된 지리적 객체가 주어진 경계 내에 완전히 포함되는지 여부를 확인합니다.

  1. $near

$near 연산자는 특정 지점에서 가까운 문서를 찾는 데 사용됩니다. 이 연산자는 2D 지리적 인덱스를 사용하여 유클리드 평면(플랫 플레인) 거리로 계산합니다.

  1. $nearSphere

$nearSphere 연산자는 특정 지점에서 가까운 문서를 찾는 데 사용되며, 구면 거리(대지구 거리)를 계산합니다. 이는 지구의 곡률을 고려하여 거리를 측정합니다.

  1. $geoNear

$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
     }
   }
])
  • near : 검색할 기준 지점을 지정
  • distanceField : 각 문서에 계산된 거리를 포함할 필드명을 지정
  • maxDistance : 검색할 최대 거리(미터 단위)를 지정
  • query : 추가적인 필터링 조건을 지정
  • includeLocs : 결과에 원래 위치 데이터를 포함할 필드명을 지정
  • spherical : 거리를 구면 거리(대지구 거리)로 계산할지를 지정

Go에서 MongoDB 지리공간적 쿼리 활용 예시

아래 코드는 특정 경도, 위도를 입력받아 근처의 장소를 찾는 코드입니다.

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)
    }
}
  1. MongoDB 연결 설정:
    • MongoDB 클라이언트를 생성하고 데이터베이스에 연결합니다.
    • defer를 사용하여 함수가 종료될 때 MongoDB 연결을 해제합니다.
  2. $geoNear 어그리게이션 단계 설정:
    • $geoNear 단계에서 near 필드는 검색할 기준 지점을 지정합니다.
    • distanceField는 계산된 거리가 저장될 필드입니다.
    • maxDistance는 검색할 최대 거리(미터 단위)입니다.
    • spherical은 거리를 구면 거리(대지구 거리)로 계산할지 여부를 지정합니다.
  3. 결과 처리:
    • Aggregate 메서드를 사용하여 $geoNear 쿼리를 실행합니다.
    • cursor.All을 사용하여 결과를 Place 구조체 배열로 디코딩합니다.
  4. 메인 함수:
    • FindNearPoint 함수를 호출하여 특정 지점 근처의 장소를 검색하고, 결과를 출력합니다.
profile
Developer

0개의 댓글