MongoDBでゆるふわDB体験

第9回 MongoDBの地理空間インデックス

この記事を読むのに必要な時間:およそ 4.5 分

地理空間インデックスの概要

MongoDBでは,地理情報をデータとして登録し,地理空間インデックスを作成することにより,位置情報を利用した検索クエリが可能になります。たとえば,山手線の駅の地理情報をMongoDBに登録して,地理情報インデックスを張っておけば,自分のいる場所から近い順に山手線の駅を検索するということが可能となります。

MongoDBの地理空間インデックスは,バージョン1.4という非常に早い時期からサポートされており,人気サイトであるfoursquareで使用されているという実績を持っています。

こちらに,地理空間インデックスに関する開発の歴史をまとめました。

バージョンリリースされた機能
1.4地理空間インデックスをサポート
1.8球面空間でのクエリをサポート
2.4GeoJSONオブジェクトをサポート※1

それではさっそく地理空間インデックスを使ってみましょう。

※1)
 2.4で追加されたGeoJSONについては,この連載の前回の記事第8回「リリース間近! MongoDB 2.4の新機能」をお読みください。

シンプルなデータで地理空間インデックスを使ってみる

2dインデックスの作成

地理空間インデックスを使うためには,2dインデックスという特別なインデックスの作成が必要となります。以下のクエリで,yamanotesenコレクションのlocフィールドに2dインデックスを作成します。

> db.yamanotesen.ensureIndex( { loc : "2d" } );

次にデータのinsertを行います。

データの準備

地理空間インデックスを使うため,経度・緯度の情報を含めたデータを挿入します。今回は山手線の7つの駅を使用しました。

経度緯度
五反田139.72382235.625974
恵比寿139.71007035.646685
新宿139.70046435.689729
新大久保139.70026135.700875
池袋139.71108635.730256
上野139.77704335.713790
品川139.73899935.628760

経度・緯度は配列またはハッシュとして保存します。今回はlocというフィールドに配列として保存します。yamanotesenというコレクションにinsertするクエリは,以下のようになります。

> db.yamanotesen.insert({ name:"五反田",loc: [ 139.723822, 35.625974 ] });
> db.yamanotesen.insert({ name:"恵比寿",loc: [ 139.710070, 35.646685 ] });
> db.yamanotesen.insert({ name:"新宿",loc: [ 139.700464, 35.689729 ] });
> db.yamanotesen.insert({ name:"新大久保",loc: [ 139.700261, 35.700875 ] });
> db.yamanotesen.insert({ name:"池袋",loc: [ 139.711086, 35.730256 ] });
> db.yamanotesen.insert({ name:"上野",loc: [ 139.777043, 35.713790 ] });
> db.yamanotesen.insert({ name:"品川",loc: [ 139.738999, 35.628760 ] });
※2)
後ほど紹介する球面空間での検索において,経度と緯度の順番が重要になります。データには,['経度', '緯度']の書式でinsertしましょう。

準備は整いましたので,いよいよ検索してみましょう。

近傍を検索するクエリ

近傍の検索には,$nearオペレータを使用します。今回は渋谷駅[ 139.701238, 35.658871 ]から近い順に3つの駅を取得してみましょう。クエリは,このようになります。

> db.yamanotesen.find({ loc : { $near : [ 139.701238, 35.658871 ] }}).limit(3)

今回準備した7つの駅で,渋谷駅から近い駅上位3件の結果はこのようになりました(_idの表示は省略しています)⁠

> db.yamanotesen.find({ loc : { $near : [ 139.701238, 35.658871 ] }},{"_id":0}).limit(3)
{ "name" : "恵比寿", "loc" : [ 139.71007, 35.646685 ] }
{ "name" : "新宿", "loc" : [ 139.700464, 35.689729 ] }
{ "name" : "五反田", "loc" : [ 139.723822, 35.625974 ] }

検索結果の詳細情報取得

geoNearコマンドを使用することで,オブジェクトまでの距離と,いくつかの統計情報を取得することができます。

> db.runCommand({'geoNear':'yamanotesen', near : [ 139.701238, 35.658871 ], num: 1})
{
        "ns" : "test.yamanotesen",
        "near" : "1110100101001011001100110110111110110111011101111101",
        "results" : [
                {
                        "dis" : 0.015050010631232593,
                        "obj" : {
                                "_id" : ObjectId("514a653517ca6b06a3266e26"),
                                "name" : "恵比寿",
                                "loc" : [
                                        139.71007,
                                        35.646685
                                ]
                        }
                }
        ],
        "stats" : {
                "time" : 0,
                "btreelocs" : 0,
                "nscanned" : 5,
                "objectsLoaded" : 3,
                "avgDistance" : 0.015050010631232593,
                "maxDistance" : 0.015066109444883333
        },
        "ok" : 1
}
resultsフィールドで返される配列内のdisフィールドが距離になります。今回は渋谷駅から恵比寿駅までの距離が0.015050010631232593となっています。これは,平面空間上における,座標[ 139.701238, 35.658871 ]と 座標[ 139.710070, 35.646685 ]の2点間の距離です。

経度・緯度の1度の距離を乗することで,実際の距離を計算することが可能です。正確な値ではありません※3が,今回は経度・緯度の1度を全て111.262283kmとして計算してみますと,

0.015050010631232593×111.262283=約1.7km

となります。

地球は球面なので平面空間で計算すると誤差が出てしまいます。MongoDBは球面空間でのクエリもサポートしていますので,次は球面空間で計算してみましょう。

※3)
正確には経度1度の距離は緯度によって変わります。

著者プロフィール

藤崎祥見(ふじさきしょうけん)

野村総合研究所 OpenStandia所属。オープンソースのR&Dとセミナー講師を担当。

Debian,Ubuntu,Liferayのコミュニティで活動した後,MongoDBの翻訳に関わり,丸の内MongoDB勉強会を始める。

実家がお寺で,住職の資格を所持する坊主系エンジニア。

Twitter:@syokenz


渡部徹太郎(わたなべてつたろう)

野村総合研究所 OpenStandia所属。オープンソースを使ったSIやサポートの業務に従事。

藤崎と共同で丸の内MongoDB勉強会を始める。

趣味は自宅サーバ。好きなものはLinuxとRuby。

Twitter:@fetarodc


林田敦(はやしだあつし)

野村総合研究所 OpenStandia所属。オープンソースを使ったSIや製品開発業務に従事。

丸の内MongoDB勉強会では広報兼雑用係を務める。

趣味はレザークラフト,ダイビング,スキー,キャンプ,ジェットスキー,カメラ等々。作って滑って撮って潜れるエンジニア。

Facebook:Atsushi Hayashida

コメント

コメントの記入