ブログに書くつもりじゃなかった

フリーのプログラマーが綴る、裏チラ系の備忘録や雑記帳。

ElasticSearchで位置情報を検索する

現在位置の近くにあるコンビニを探すとか、そういうのを試したくなったのでやってみる。

 

ElasticSearchの導入

お手軽に試したいだけなのでdockerを使用する。

$ docker pull elasticsearch:8.3.2

dockerイメージのelasticsearch.ymlTLSが有効になっていたが、お試しで使用するには煩わしい。-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に登録するのも簡単そう。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で可視化するのも良いかな。