はじめに
Elasticsearchは緯度経度を用いた位置情報検索ができます。
今回は山手線の緯度経度データをElasticsearchに投入し、位置情報を用いた範囲検索を実装してみます。
インデックスの作成
- 今回は山手線の駅名と緯度経度情報を持つデータを投入します。
- geo_pointタイプはdynamic mappingされず、事前に定義する必要があるので注意です。
from elasticsearch import Elasticsearch
# Elasticsearchインスタンスの作成
es = Elasticsearch(
"elasticsearchのURL",
http_auth=("user_id", "password")
)
# マッピング定義
mapping = {
"mappings": {
"properties": {
"name": {
"type": "text"
},
"location": {
"type": "geo_point"
},
}
}
}
# インデックスの作成
es.indices.create(index="yamanote_station", body=mapping)
テストデータのバルクインサート
- 作成したインデックスにバルクインサートを用いて山手線のデータを投入します。
from elasticsearch import helpers
def bulk_insert(index, geo_location_dict):
yamanote_info_list = [{"name": station, "location": geo_location} for station, geo_location in geo_location_dict.items()]
# bulkで扱えるデータ構造に変換します
for yamanote_info in yamanote_info_list:
yield {
"_op_type": "create",
"_index": index,
"_source": yamanote_info
}
yamanote_station_dict = {
'上野駅': {'lat': 35.7141672, 'lon': 139.7774091},
'五反田駅': {'lat': 35.6261591, 'lon': 139.7236022},
'代々木駅': {'lat': 35.683033, 'lon': 139.7020555},
'原宿駅': {'lat': 35.6702285, 'lon': 139.7026976},
'品川駅': {'lat': 35.6284713, 'lon': 139.7387597},
'大塚駅': {'lat': 35.7318309, 'lon': 139.7281112},
'大崎駅': {'lat': 35.6198513, 'lon': 139.7281892},
'巣鴨駅': {'lat': 35.7334192, 'lon': 139.7392848},
'御徒町駅': {'lat': 35.7075185, 'lon': 139.7748564},
'恵比寿駅': {'lat': 35.6467139, 'lon': 139.7100777},
'新大久保駅': {'lat': 35.7012459, 'lon': 139.7002258},
'新宿駅': {'lat': 35.6896067, 'lon': 139.7005713},
'新橋駅': {'lat': 35.666379, 'lon': 139.7583398},
'日暮里駅': {'lat': 35.7281578, 'lon': 139.7706414},
'有楽町駅': {'lat': 35.6749187, 'lon': 139.7628199},
'東京駅': {'lat': 35.68123620000001, 'lon': 139.7671248},
'池袋駅': {'lat': 35.7295028, 'lon': 139.7109001},
'浜松町駅': {'lat': 35.6553809, 'lon': 139.7571289},
'渋谷駅': {'lat': 35.6580339, 'lon': 139.7016358},
'田町駅': {'lat': 35.6457361, 'lon': 139.7475624},
'田端駅': {'lat': 35.7381581, 'lon': 139.7608154},
'目白駅': {'lat': 35.7212199, 'lon': 139.7066115},
'目黒駅': {'lat': 35.6340929, 'lon': 139.7158331},
'神田駅': {'lat': 35.6918216, 'lon': 139.7709318},
'秋葉原駅': {'lat': 35.698383, 'lon': 139.7730717},
'西日暮里駅': {'lat': 35.73200569999999, 'lon': 139.7668856},
'駒込駅': {'lat': 35.7365665, 'lon': 139.7470098},
'高田馬場駅': {'lat': 35.7125654, 'lon': 139.7038615},
'高輪ゲートウェイ駅': {'lat': 35.6355207, 'lon': 139.7406809},
'鶯谷駅': {'lat': 35.7214573, 'lon': 139.7780133}
}
helpers.bulk(es, bulk_insert("yamanote_station", yamanote_station_dict))
位置情報検索
- 今回は東京駅から半径3km以内に存在する山手線の駅を検索してみます
- 円形範囲検索はgeo_distanceクエリを使用します。(その他の位置情報検索は公式を参照してみてください)
query = {
"query": {
"geo_distance": {
"distance": "3km",
"location": {
"lat": yamanote_station_dict['東京駅']['lat'],
"lon": yamanote_station_dict['東京駅']['lon']
}
}
}
}
res = es.search(index="yamanote_station", body=query, size=10)
- 検索結果
- 新橋・有楽町・神田・秋葉原と東京駅から3km以内の駅がしっかり検索できていますね。
{'hits': [{'_id': 'JeizK38BB0c_3d13lkrt',
'_index': 'yamanote_station',
'_score': 1.0,
'_source': {'location': {'lat': 35.666379, 'lon': 139.7583398},
'name': '新橋駅'},
'_type': '_doc'},
{'_id': 'J-izK38BB0c_3d13lkrt',
'_index': 'yamanote_station',
'_score': 1.0,
'_source': {'location': {'lat': 35.6749187, 'lon': 139.7628199},
'name': '有楽町駅'},
'_type': '_doc'},
{'_id': 'KOizK38BB0c_3d13lkrt',
'_index': 'yamanote_station',
'_score': 1.0,
'_source': {'location': {'lat': 35.68123620000001, 'lon': 139.7671248},
'name': '東京駅'},
'_type': '_doc'},
{'_id': 'MOizK38BB0c_3d13lkrt',
'_index': 'yamanote_station',
'_score': 1.0,
'_source': {'location': {'lat': 35.6918216, 'lon': 139.7709318},
'name': '神田駅'},
'_type': '_doc'},
{'_id': 'MeizK38BB0c_3d13lkrt',
'_index': 'yamanote_station',
'_score': 1.0,
'_source': {'location': {'lat': 35.698383, 'lon': 139.7730717},
'name': '秋葉原駅'},
'_type': '_doc'}],
'max_score': 1.0,
'total': {'relation': 'eq', 'value': 5}}
参考記事
- Geo queries | Elasticsearch Guide [8.0] | Elastic https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-queries.html#geo-queries
- Elasticsearch の位置検索(Geolocation)を学ぶ | DevelopersIO https://dev.classmethod.jp/articles/study-elasticsearch-geolocation/