🐙

【MongoDB】aggregateのクエリで「$geoNear」を使用して指定した地点から近いドキュメントを取得

に公開

概要

MongoDBではMongoDBの地理空間インデックスの記事で紹介されている通り、緯度・経度をデータに登録することで、位置情報を利用した検索クエリが可能となります。今回はaggregateのクエリで地理情報を使用した集計$geoNearの機能を使ってみたので、メモ書きです。

前提

  • 使用したMongoDBのバージョンはv8.0.1です。
  • Pythonのmongoengineを使用してクエリを発行しています。

$geoNearについて

$geoNear(集計)のドキュメントに記載の通り、使用にあたりいくつか留意点があります。個人的に注意が必要かなと感じた点を以下の通り抜粋します。

  • $geoNearは、パイプラインの最初のステージとしてのみ使用できます。
  • 地理空間インデックスが 1 つしかない場合は、$geoNear は暗黙的にインデックス付きフィールドを計算に使用します。

実装サンプル

以下のような形式のドキュメントを用意します。 2dsphereのインデックスをlon_latの項目に適用しておきます。

{
  "_id": "453a76e1-3c03-4983-b616-b80e1a0103d7"
  "name": "高尾山",
  "create_user_account_id": "sample_user_id_1",
  "address": "東京都八王子市高尾町",
  "lon_lat": [
    139.261831,
    35.633417
  ],
  "prefecture_code": "13",
  "detail": "",
  "url": "https://www.jalan.net/kankou/spt_guide000000187656/"
}

これをmongoengineを使用して、aggregateのクエリを発行するサンプル実装は以下です。

def find_my_posts_by_lat_lon(
    user_account_id: str,
    lon: float,
    lat: float,
    radius_meter: float,
    limit: int,
) -> list:
    pipeline = [
        {
            "$geoNear": {
                "near": {
                    "type": "Point",
                    "coordinates": [lon, lat],
                },
                "distanceField": "distance",
                "maxDistance": radius_meter,
                "spherical": True,
            }
        },
        {"$match": {"create_user_account_id": user_account_id}},
        {"$limit": limit},
        {
            "$project": {
                "_id": 1,
                "name": 1,
                "address": 1,
                "lon_lat": 1,
                "prefecture_code": 1,
                "detail": 1,
                "url": 1,
            }
        },
    ]
    // 事前にmongoengineでドキュメントのclassを定義しておく
    return list(PostPlace.objects().aggregate(pipeline))

Discussion