Elasticsearch でスキーマを定義してデータを投入する方法を試してみます。
Elasticsearch のインストールについてはコチラ、
kuromoji のインストーラについてはコチラを参考にしてください。
Elasticsearch のスキーマについて
Elasticsearch ではスキーマを マッピング、型を タイプ という概念で扱います。
マッピングを定義していない場合は、投入されたデータの値からタイプを自動で判定して、マッピングされます。
これはとても便利な機能である反面、意図しない・不必要なインデクシングが発生してしまう可能性もあります。
安定した運用が求められるケースでは、しっかりとスキーマを定義したほうが良いですね。
マッピングの指定
今回はランドマークの「名前、緯度軽度、説明」を投入してみます。
項目 | プロパティ名 | 説明 |
---|---|---|
名前 | name | ランドマークの名前。全文検索対象外。 |
緯度軽度 | coord | ランドマークの緯度軽度。空間検索の対象。 |
説明 | description | ランドマークの説明。全文検索の対象 |
これをマッピング指定に用いる JSON で表現すると以下のようになります。
{
"mappings": {
"sample": {
"properties": {
"name": {
"type": "string",
"index": "not_analyzed"
},
"coord": {
"type": "geo_point"
},
"description": {
"type": "string",
"analyzer": "kuromoji"
}
}
}
}
}
この JSON を使って landmark
というインデックスを作成します。
$ curl -X PUT http://localhost:9200/landmark --data-binary @mapping.json
意図通り定義されたかを確認してみます。
$ curl -X GET http://localhost:9200/landmark
{
"landmark": {
"aliases": {},
"mappings": {
"sample": {
"properties": {
"coord": {
"type": "geo_point"
},
"description": {
"type": "string",
"analyzer": "kuromoji"
},
"name": {
"type": "string",
"index": "not_analyzed"
}
}
}
},
...
}
}
いい感じですね。
データの投入
前回同様、Bulk API を使ってデータを投入します。
以下の JSON を使います。
{ "index" : {} }
{ "name": "スカイツリー", "coord": { "lat": "35.710063", "lon": "139.8107"}, "description": "東京スカイツリー(とうきょうスカイツリー、英: TOKYO SKYTREE)は東京都墨田区押上一丁目にある電波塔(送信所)である。"}
{ "index" : {} }
{ "name": "BLUE NOTE TOKYO", "coord": { "lat": "35.661198", "lon": "139.716207"}, "description": "N.Y.の「Blue Note」を本店に持つジャズクラブとして、南青山にオープンしたブルーノート東京。ジャズをはじめとする多様な音楽ジャンルのトップアーティストたちが、連夜渾身のプレイを繰り広げている。"}
{ "index" : {} }
{ "name": "東京タワー", "coord": { "lat": "35.65858", "lon": "139.745433"}, "description": "東京タワー(とうきょうタワー、英: Tokyo Tower)は、東京都港区芝公園にある総合電波塔とその愛称である。正式名称は日本電波塔(にっぽんでんぱとう)。"}
API にデータを食わせます。
$ curl -X POST http://localhost:9200/landmark/sample/_bulk --data-binary @landmark.json
データの検索
「ジャズ」で検索してみます。
$ curl -X GET http://localhost:9200/landmark/sample/_search -d '{"query":{"match":{"description":"ジャズ"}}}'
{
...
"hits": {
"total": 1,
"max_score": 0.081366636,
"hits": [
{
"_index": "landmark",
"_type": "sample",
"_id": "AU071ik4RTPkdK_mzpPE",
"_score": 0.081366636,
"_source": {
"name": "BLUE NOTE TOKYO",
"coord": {
"lat": "35.661198",
"lon": "139.716207"
},
"description": "N.Y.の「Blue Note」を本店に持つジャズクラブとして、南青山にオープンしたブルーノート東京。ジャズをはじめとする多様な音楽ジャンルのトップアーティストたちが、連夜渾身のプレイを繰り広げている。"
}
}
]
}
}
ちゃんと検索されました。
次に空間検索をしてみます。
「渋谷駅から近い順」に検索してみましょう。
(緯度経度は GoogleMap などから取得すると良いです)
{
"sort": [
{
"_geo_distance": {
"coord": {
"lat": 35.658517,
"lon": 139.701334
},
"order": "asc"
}
}
],
"query": {
"filtered": {
"query": {
"match_all": {}
}
}
}
}
$ curl -X GET http://localhost:9200/landmark/sample/_search --data-binary @query.json
レスポンスはこんな感じです。
{
...
"hits": {
"total": 3,
"max_score": null,
"hits": [
{
...
"_source": {
"name": "BLUE NOTE TOKYO",
"coord": {
"lat": "35.661198",
"lon": "139.716207"
},
...
},
"sort": [
1376.353778271321
]
},
{
...
"_source": {
"name": "東京タワー",
"coord": {
"lat": "35.65858",
"lon": "139.745433"
},
...
},
"sort": [
3984.1379076620046
]
},
{
...
"_source": {
"name": "スカイツリー",
"coord": {
"lat": "35.710063",
"lon": "139.8107"
},
...
"sort": [
11419.980751959736
]
}
]
}
}
地点間の直線距離が計算され、それの照準で返却されています。
もちろんワード指定の検索と組み合わせることも可能ですので、色々と試してみてください。