ElasticSearchで位置情報を検索する
現在位置の近くにあるコンビニを探すとか、そういうのを試したくなったのでやってみる。
ElasticSearchの導入
お手軽に試したいだけなのでdockerを使用する。
$ docker pull elasticsearch:8.3.2
dockerイメージのelasticsearch.yml
はTLSが有効になっていたが、お試しで使用するには煩わしい。-e "xpack.security.enabled=false"
を付けてセキュリティ機能を無効にしてコンテナを起動する。
$ docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "xpack.security.enabled=false"elasticsearch:8.3.2
curl
で死活確認。
$ curl -i http://localhost:9200/
HTTP/1.1 200 OK
X-elastic-product: Elasticsearch
content-type: application/json
content-length: 507
{
"name" : "cba32a558fa6",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "yjJCYYaoSTKkwO_6coZdDQ",
"version" : {
"number" : "8.3.2",
"build_type" : "docker",
"build_hash" : "8b0b1f23fbebecc3c88e4464319dea8989f374fd",
"build_date" : "2022-07-06T15:15:15.901688194Z",
"build_snapshot" : false,
"lucene_version" : "9.2.0",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
位置情報を登録する
登録するデータ
せっかくなので、ある程度まとまった件数の面白そうなデータが欲しい。なんとなく検索して見つかったのがこれ。国土交通省が提供している。
何やら説明文が難しいが、ダウンロードして中身を確認してみると至ってシンプルなxmlだった。これならjsonに変換してElasticSearchに登録するのも簡単そう。geo_pointの記述方法を確認、登録するjsonデータを以下の形にする。
{
"location": [140.714494, 41.756023],
"prefectureCode": "01",
"administrativeAreaCode": "01202",
"largeClassificationCode": "1",
"smallClassificationCode": "11",
"culturalPropertyName": "樽岸出土の石器",
"address": "函館市青柳町17-1",
"specifiedDate": 19571220,
"pointClassificationCode": "1"
}
geo_pointを使用するにはマッピングを明示する必要があるらしいので、インデックスを登録する際に指定する。他のフィールドはテキトーに。
$ curl -i -XPUT -H "content-type: application/json" http://localhost:9200/property-01/ -d '
{
"mappings": {
"properties": {
"location": {"type": "geo_point"},
"prefectureCode": {"type": "keyword"},
"administrativeAreaCode": {"type": "keyword"},
"largeClassificationCode": {"type": "keyword"},
"smallClassificationCode": {"type": "keyword"},
"culturalPropertyName": {"type": "text"},
"address": {"type": "text"},
"specifiedDate": {"type": "long"},
"pointClassificationCode": {"type": "keyword"}
}
}
}'
インデックスができたら、とりあえずドキュメントを1件登録。
$ curl -i -XPOST -H "content-type: application/json" http://localhost:9200/property-01/_doc/ -d '
{
"location": [140.714494, 41.756023],
"prefectureCode": "01",
"administrativeAreaCode": "01202",
"largeClassificationCode": "1",
"smallClassificationCode": "11",
"culturalPropertyName": "樽岸出土の石器",
"address": "函館市青柳町17-1",
"specifiedDate": 19571220,
"pointClassificationCode": "1"
}'
位置情報を検索する
さあ確認してみよう。件の場所をGoogle Mapで検索してみると、函館公園の一角にあると判明。そしてJR函館駅からの直線距離は、パッと見で1km以上3km未満といったところだ。この条件で検索してみる。
まずは函館駅から半径1km以内で検索。
$ curl -i -H "content-type: application/json" http://localhost:9200/*/_search?pretty -d '
{
"query": {
"geo_distance": {
"distance": "1km",
"location": {
"lat": 41.773989,
"lon": 140.726442
}
}
}
}'
ヒットしない。
HTTP/1.1 200 OK
X-elastic-product: Elasticsearch
content-type: application/json
content-length: 262
{
"took" : 11,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
条件を半径3kmに変更して検索。
$ curl -i -H "content-type: application/json" http://localhost:9200/*/_search?pretty -d '
{
"query": {
"geo_distance": {
"distance": "3km",
"location": {
"lat": 41.773989,
"lon": 140.726442
}
}
}
}'
ヒットした。とりあえず上手くいったようだ。
HTTP/1.1 200 OK
X-elastic-product: Elasticsearch
content-type: application/json
content-length: 850
{
"took" : 9,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "property-01",
"_id" : "9e1AKYIBvFRNCcdg2ydQ",
"_score" : 1.0,
"_source" : {
"location" : [
140.714494,
41.756023
],
"prefectureCode" : "01",
"administrativeAreaCode" : "01202",
"largeClassificationCode" : "1",
"smallClassificationCode" : "11",
"culturalPropertyName" : "樽岸出土の石器",
"address" : "函館市青柳町17-1",
"specifiedDate" : 19571220,
"pointClassificationCode" : "1"
}
}
]
}
}
今後の予定
ダウンロードしたデータを全部突っ込んで遊んでみよう。kibanaで可視化するのも良いかな。