Amazon CloudSearch: 簡単な地理空間検索をやってみる

# Amazon CloudSearch
CloudSearchは、クラウド上ですべて管理されたWebサイト向けの検索ソリューションで、セットアップ、管理、拡張がとても簡単に行えます。が、SDKなどがまだ整備されていないため、検索エンジンと比べるとWebApplicationに組み込むのがめんどくさいかも。地理空間検索にも対応しているということなんで球面上距離（haversin）で、指定した地点に一番近い山の手線の駅を検索するというのをやってみたいと思います。

####参考
System Properties Comparison CloudSearch vs. Elasticsearch vs. Solr
http://db-engines.com/en/system/CloudSearch%3BElasticsearch%3BSolr

AWS Black Belt Techシリーズ Amazon CloudSearch
http://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-tech-amazon-cloudsearch

# やってみる
### 地理情報の準備

山の手線の駅名と位置情報のCSVデータを用意しました。
緯度経度の情報は、','カンマでつなげて一つのカラムにします。
1行目はヘッダー行にします。

```sample-data.csv

"name","latlon"
"東京駅（東京）","35.681500, 139.766693"
"神田駅（東京）","35.691686, 139.770843"
"秋葉原駅（東京）","35.698656, 139.774169"
"御徒町駅（東京）","35.707430, 139.774620"
"上野駅（東京）","35.713798, 139.777243"
"鶯谷駅（東京）","35.720477, 139.778836"
"日暮里駅（東京）","35.727773, 139.770977"
"西日暮里駅（東京）","35.732154, 139.766782"
"田端駅（東京）","35.738042, 139.760838"
"駒込駅（東京）","35.736449, 139.746894"
"巣鴨駅（東京）","35.733517, 139.739342"
"大塚駅（東京）","35.731822, 139.728183"
"池袋駅（東京）","35.728943, 139.710396"
"目白駅（東京）","35.721209, 139.706577"
"高田馬場駅（東京）","35.712310, 139.703725"
"新大久保駅（東京）","35.701315, 139.700056"
"新宿駅（東京）","35.690835, 139.700236"
"代々木駅（東京）","35.683051, 139.702053"
"原宿駅（東京）","35.670187, 139.702697"
"渋谷駅（東京）","35.658790, 139.701551"
"恵比寿駅（東京）","35.646681, 139.710074"
"目黒駅（東京）","35.633998, 139.715781"
"五反田駅（東京）","35.626490, 139.723430"
"大崎駅（東京）","35.619694, 139.728542"
"品川駅（東京）","35.630177, 139.740441"
"田町駅（東京）","35.645724, 139.747565"
"浜松町駅（東京）","35.655654, 139.756695"
"新橋駅（東京）","35.665491, 139.759603"
"有楽町駅（東京）","35.675101, 139.763337"

```


### Domainの作成
検索ドメインを作成します。
![cloudSearch-create-NewDomain.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/e46dde5e-5acf-025c-d987-6505b35b66f5.jpeg)

インスタンスタイプとレプリケーション数を指定します。
![cloudSearch-入力.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/da1955c7-cca9-affa-92fb-61b360f336eb.jpeg)

先ほどのsample-data.csvを指定します。
![cloudsearch-sampledata-import.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/8a24e4cf-0ec7-4ad6-7389-0273c2ca673c.jpeg)

カラムとデータの内容から自動生成されます。latlonを Type'latlon'に変更。nameもjapaneseに変更します。
![cloudsearch-searchdomain.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/115d70bf-9c76-5b29-4cca-a259bc1a6d2d.jpeg)

Access-policyの設定です。今回は、全許可にしています。
![cloudsearch-access-policy.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/6d379503-656c-7d51-b0c6-51c59041f7ac.jpeg)

確認画面です。
![cloudsearch-comfirm.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/888d9182-2939-3206-d9e9-e75e67d7aae3.jpeg)

問題なければConguraturationsという文字がでます。
![cloudsearch-congraturations.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/4df0ffe1-6b5c-f14a-3ea2-9c074c5f67c4.jpeg)

DashBoard画面のLOADINGの文字がACTIVEになるまで待ちます。（10分ほど）
![cloudera-roading.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/ed66eb6e-27ac-6fdb-ba83-bac9bb32ac75.jpeg)




### データのアップロード
Domainが起動したら、再度、同じファイルをアップロードします。
![cloudsearch-upload.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/10b6ef55-d776-68ee-5c69-81b3931079ee.jpeg)


![cloudsearch-uploadDocuments.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/9c3c8518-20ae-2497-9099-afffdbd86703.jpeg)

`Download the generated document batch(optional)`をクリックすると投入するデータのプレビューがみれます。

```sample-data.json
[ {
  "type" : "add",
  "id" : "sample-data.csv_1",
  "fields" : {
    "latlon" : "35.6858577,139.7644613",
    "name" : "東京駅（東京）"
  }
}, {
  "type" : "add",
  "id" : "sample-data.csv_2",
  "fields" : {
    "latlon" : "35.6858577,139.7644613",
    "name" : "神田駅（東京）"
  }
}, {
  "type" : "add",
  "id" : "sample-data.csv_3",
  "fields" : {
    "latlon" : "35.6858577,139.7644613",
    "name" : "秋葉原駅（東京）"
  }
}, {
  "type" : "add",
  "id" : "sample-data.csv_4",
  "fields" : {
    "latlon" : "35.7038419,139.7672079",
    "name" : "上野駅（東京）"
  }
}
 ]

```

### 検索してみる

テスト検索をしてみます。

*Run a Test Search*を選択し、Searchのテキストボックスに"東京"と入力してみます。

![cloudsearch-run-test-search-tokyo.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/97c038d9-87c8-dd89-7414-e3f08d1c3bc2.jpeg)

ドキュメントに東京が含まれているレコード（つまり全レコード）が結果として出力されました。



### 地理空間検索を実行する

適当なポイントをgoogle mapから選択します。
ポイントを選択し、場所の情報を出すと緯度・経度が。
`新御茶ノ水駅（東京）/@35.699789,139.764230`

![ocyanomizu-point.JPG](https://qiita-image-store.s3.amazonaws.com/0/36221/cfcfe907-11cb-75f5-a67b-d1ae82831928.jpeg)

それでは、以下のような書式でブラウザからCloudSearchのAPIをたたいてみます。

**{Search Endpoint}/{API-Version}/search?q={検索条件}&expr.distance=haversin({緯度},{経度},latlon.latitude,latlon.longitude)&return=distance,name&sort=distance asc**

* return : 検索結果で表示させるfieldsや、関数の結果などを指定する


```http

http://search-sample-data-XXXXX.ap-northeast-1.cloudsearch.amazonaws.com/2013-01-01/search?q=東京&expr.distance=haversin(35.699789,139.764230,latlon.latitude,latlon.longitude)&return=distance,name&sort=distance asc
```


検索結果を距離(distance)の昇順で上位10件までが返ってきています。

```response.json
{
    "status": {
        "rid": "oIaItJcpQgqcxnI=",
        "time-ms": 9
    },
    "hits": {
        "found": 29,
        "start": 0,
        "hit": [
            {
                "id": "sample-data.csv_3",
                "fields": {
                    "name": "秋葉原駅（東京）"
                },
                "exprs": {
                    "distance": "0.9062821751404536"
                }
            },
            {
                "id": "sample-data.csv_2",
                "fields": {
                    "name": "神田駅（東京）"
                },
                "exprs": {
                    "distance": "1.0809333857050134"
                }
            },
            {
                "id": "sample-data.csv_4",
                "fields": {
                    "name": "御徒町駅（東京）"
                },
                "exprs": {
                    "distance": "1.2657034643074225"
                }
            },
            {
                "id": "sample-data.csv_5",
                "fields": {
                    "name": "上野駅（東京）"
                },
                "exprs": {
                    "distance": "1.9511435692477412"
                }
            },
            {
                "id": "sample-data.csv_1",
                "fields": {
                    "name": "東京駅（東京）"
                },
                "exprs": {
                    "distance": "2.0457426517396846"
                }
            },
            {
                "id": "sample-data.csv_6",
                "fields": {
                    "name": "鶯谷駅（東京）"
                },
                "exprs": {
                    "distance": "2.6515531913099477"
                }
            },
            {
                "id": "sample-data.csv_29",
                "fields": {
                    "name": "有楽町駅（東京）"
                },
                "exprs": {
                    "distance": "2.746324825972988"
                }
            },
            {
                "id": "sample-data.csv_7",
                "fields": {
                    "name": "日暮里駅（東京）"
                },
                "exprs": {
                    "distance": "3.170695687401283"
                }
            },
            {
                "id": "sample-data.csv_8",
                "fields": {
                    "name": "西日暮里駅（東京）"
                },
                "exprs": {
                    "distance": "3.6061388745906506"
                }
            },
            {
                "id": "sample-data.csv_28",
                "fields": {
                    "name": "新橋駅（東京）"
                },
                "exprs": {
                    "distance": "3.836536273040852"
                }
            }
        ]
    }
}
```

## その他データなど
サンプルデータ　市役所、駅などの位置情報データがダウンロードできる
http://download.geonames.org/export/dump/


## Amazon CloudSearchを使ってみて
* データの取り込み、特にスキーマの設定などがMongoDBにくらべて簡単だった。
* 検索範囲の絞り込みのためのカテゴリーなどのカラムを追加すれば、距離の検索のパターンのバリエーションをふやすことが出来るとおもう。
* 次はDynamoDBを使って取得してみたい