mroongaで位置情報検索するには

> もっとgroongaを知ってもらおう!ということで週刊groongaをはじめました。毎週木曜にgroongaやmroonga、rroongaのトピックを投稿予定です。

いよいよ、[gihyo.jp](http://gihyo.jp/)さんでgroongaの隔週更新連載が始まりました!! 
第2回の記事も公開されたので、一読をおすすめします。

* [「第1回 全文検索エンジンgroongaを紹介します！」](http://gihyo.jp/dev/clip/01/groonga/0001)
* [「第2回 groongaをRuby On Railsでも使ってみた ～chikamap.comの事例から」](http://gihyo.jp/dev/clip/01/groonga/0002)

連載は始まりましたが、利用事例をどんどん紹介していきたいです。groongaやmroonga、rroongaを実際に使っていて、利用事例記事を書いてもいいよ、という人をまだまだ募集しています。
詳細は[groonga普及のための協力のお願い](http://sourceforge.jp/projects/groonga/lists/archive/dev/2013-February/001186.html)を参照してください。

# はじめに

MySQLで高速に全文検索するためのオープンソースのストレージエンジンとして[mroonga](http://mroonga.org/ja/)を公開しています。
最新のバージョンは2013年3月29日にリリースした[3.02](http://mroonga.org/ja/docs/news.html#release-3-02-2013-3-29)です。

今回は、mroongaで位置情報検索をする方法について紹介します。

# mroongaにおける位置情報検索

mroongaで位置情報検索を行うにはいくつか方法があります。

* MBRContainsを使った位置情報検索
* mroonga_commandを使った位置情報検索

それぞれのやりかたを具体例で紹介します。

サンプルとして使うスキーマは以下の通りです。

```
CREATE TABLE shops (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name TEXT,
  location GEOMETRY NOT NULL,
  SPATIAL KEY location_index (location)
) ENGINE = mroonga DEFAULT CHARSET utf8;
```

サンプルとして使うデータは以下の通りです。

```
INSERT INTO shops (name, location)
           VALUES ('根津のたいやき',
                   GeomFromText('POINT(139.762573 35.720253)'));
INSERT INTO shops (name, location)
           VALUES ('たい焼 カタオカ',
                   GeomFromText('POINT(139.715591 35.712521)'));
INSERT INTO shops (name, location)
           VALUES ('そばたいやき空',
                   GeomFromText('POINT(139.659088 35.683712)'));
INSERT INTO shops (name, location)
           VALUES ('車',
                   GeomFromText('POINT(139.706207 35.721516)'));
INSERT INTO shops (name, location)
           VALUES ('広瀬屋',
                   GeomFromText('POINT(139.685608 35.714844)'));
INSERT INTO shops (name, location)
           VALUES ('さざれ',
                   GeomFromText('POINT(139.685043 35.714653)'));
INSERT INTO shops (name, location)
           VALUES ('おめで鯛焼き本舗錦糸町東急店',
                   GeomFromText('POINT(139.817154 35.700516)'));
INSERT INTO shops (name, location)
           VALUES ('長屋 錦糸町店',
                   GeomFromText('POINT(139.81105 35.698254)'));
INSERT INTO shops (name, location)
           VALUES ('たいやき工房白家 阿佐ヶ谷店',
                   GeomFromText('POINT(139.638611 35.705517)'));
INSERT INTO shops (name, location)
           VALUES ('たいやき本舗 藤家 阿佐ヶ谷店',
                   GeomFromText('POINT(139.637115 35.703938)'));
INSERT INTO shops (name, location)
           VALUES ('みよし',
                   GeomFromText('POINT(139.537323 35.644539)'));
INSERT INTO shops (name, location)
           VALUES ('寿々屋 菓子',
                   GeomFromText('POINT(139.695755 35.628922)'));
INSERT INTO shops (name, location)
           VALUES ('たい焼き / たつみや',
                   GeomFromText('POINT(139.638657 35.665501)'));
INSERT INTO shops (name, location)
           VALUES ('たい焼き鉄次 大丸東京店',
                   GeomFromText('POINT(139.76857 35.680912)'));
INSERT INTO shops (name, location)
           VALUES ('吾妻屋',
                   GeomFromText('POINT(139.647598 35.700817)'));
INSERT INTO shops (name, location)
           VALUES ('ほんま門',
                   GeomFromText('POINT(139.652573 35.722736)'));
INSERT INTO shops (name, location)
           VALUES ('浪花家',
                   GeomFromText('POINT(139.796234 35.730061)'));
INSERT INTO shops (name, location)
           VALUES ('代官山たい焼き黒鯛',
                   GeomFromText('POINT(139.704834 35.650345)'));
INSERT INTO shops (name, location)
           VALUES ('たいやき神田達磨 八重洲店',
                   GeomFromText('POINT(139.770599 35.681461)'));
INSERT INTO shops (name, location)
           VALUES ('柳屋 たい焼き',
                   GeomFromText('POINT(139.783981 35.685341)'));
INSERT INTO shops (name, location)
           VALUES ('たい焼き写楽',
                   GeomFromText('POINT(139.794846 35.716969)'));
INSERT INTO shops (name, location)
           VALUES ('たかね 和菓子',
                   GeomFromText('POINT(139.560913 35.698601)'));
INSERT INTO shops (name, location)
           VALUES ('たい焼き ちよだ',
                   GeomFromText('POINT(139.652817 35.642601)'));
INSERT INTO shops (name, location)
           VALUES ('ダ・カーポ',
                   GeomFromText('POINT(139.727356 35.627346)'));
INSERT INTO shops (name, location)
           VALUES ('松島屋',
                   GeomFromText('POINT(139.737381 35.640556)'));
INSERT INTO shops (name, location)
           VALUES ('銀座 かずや',
                   GeomFromText('POINT(139.760895 35.673508)'));
INSERT INTO shops (name, location)
           VALUES ('ふるや古賀音庵 和菓子',
                   GeomFromText('POINT(139.676071 35.680603)'));
INSERT INTO shops (name, location)
           VALUES ('蜂の家 自由が丘本店',
                   GeomFromText('POINT(139.668106 35.608021)'));
INSERT INTO shops (name, location)
           VALUES ('薄皮たい焼き あづきちゃん',
                   GeomFromText('POINT(139.673203 35.64151)'));
INSERT INTO shops (name, location)
           VALUES ('横浜 くりこ庵 浅草店',
                   GeomFromText('POINT(139.796829 35.712013)'));
INSERT INTO shops (name, location)
           VALUES ('夢ある街のたいやき屋さん戸越銀座店',
                   GeomFromText('POINT(139.712524 35.616199)'));
INSERT INTO shops (name, location)
           VALUES ('何故屋',
                   GeomFromText('POINT(139.665833 35.609039)'));
INSERT INTO shops (name, location)
           VALUES ('築地 さのきや',
                   GeomFromText('POINT(139.770721 35.66592)'));
INSERT INTO shops (name, location)
           VALUES ('しげ田',
                   GeomFromText('POINT(139.780273 35.672626)'));
INSERT INTO shops (name, location)
           VALUES ('にしみや 甘味処',
                   GeomFromText('POINT(139.774628 35.671825)'));
INSERT INTO shops (name, location)
           VALUES ('たいやきひいらぎ',
                   GeomFromText('POINT(139.711517 35.647701)'));
```


## MBRContains関数を使った位置情報検索

それでは、MBRContains関数を使って位置情報検索をやってみましょう。

サンプルデータとして登録されているたいやき屋を四角の範囲から探してみます。

![浅草駅周辺のたいやき屋](http://www.clear-code.com/blog/images/20110913_5.png)

MBRContains関数の構文は以下の通りです。

```
MBRContains(GeomFromText('LineString(北西の点の経度 北西の点の緯度, 南東の点の経度 南東の点の緯度)'), カラム名)
```

これに従うとクエリは以下のようになります。

```
SELECT id, name, AsText(location) AS location_text FROM shops
  WHERE MBRContains(GeomFromText('LineString(139.7912 35.7185, 139.8069 35.7065)'), location)
  ORDER BY id;
```

結果は以下の通りでした。

```
id	name	location_text
21	たい焼き写楽	POINT(139.79484611111113 35.71696888888889)
30	横浜 くりこ庵 浅草店	POINT(139.79682888888888 35.71201305555556)
```

たいやき屋が2件該当することがわかります。

## mroonga_commandを使った位置情報検索

今度は、サンプルデータとして登録されているたいやき屋を指定した円の範囲から探してみます。

![浅草駅周辺のたいやき屋](http://www.clear-code.com/blog/images/20110913_6.png)

指定した円の範囲にあるたいやき屋を探すには、SQLの範囲でも条件を組み合わせることでできます。

ただ、mroonga_commandを使う方法もあるのでそちらを紹介します。
(まだドキュメントがありませんが、近日リリースするmroonga 3.03でmroonga_commandのドキュメントを追加します。)

mroonga_commandはデータストアとして使っているgroongaの機能を呼び出すことのできるUDFです。

このUDFを使うとクエリは以下のようになります。

```
SELECT mroonga_command("select shops --filter 'geo_in_circle(location, \"35.7119,139.7983\", 500)'");
```

[geo_in_circle](http://groonga.org/ja/docs/reference/functions/geo_in_circle.html)は座標が円の範囲内に存在するかどうかを調べるための関数です。

上記クエリで浅草駅の周辺500メートルの範囲にあるたいやき屋を探せます。

```
[[[1],[["_id","UInt32"],["_key","Int32"],["id","Int32"],["location","WGS84GeoPoint"],["name","LongText"]],[30,30,30,"128563247x503268584","横浜 くりこ庵 浅草店"]]]
```

たいやき屋が1件該当することがわかります。

mroonga_commandを使う場合、いわゆる普通のSQLの範囲から逸脱していますが、こういうやりかたもあります。

# まとめ

今回はmroongaで位置情報検索を行う方法を紹介しました。

今回紹介に使ったサンプルは[groongaで高速な位置情報検索](http://www.clear-code.com/blog/2011/9/13.html)と同じものです。

mroongaはカラムストア機能付き全文検索エンジンである[groonga](http://groonga.org/ja)をデータストアとして使っています。

mroongaに興味をもったら、まずは[インストール](http://mroonga.org/ja/docs/install.html)してみてください。

動作を知りたいというのであれば、[ユーザーガイド](http://mroonga.org/ja/docs/userguide.html)もあります。