はじめに
今回OpenSearchを利用する機会があったので、OpenSearchの使い方や検索APIとしての利用法を紹介します。
構成としては、API Gateway + Lambda というおなじみの構成ですが以下になります。
OpenSearchとは
公式からの引用になりますが、以下となります。
>Amazon OpenSearch Service は、AWS クラウドにおける OpenSearch クラスターのデプロイ、オペレーション、スケーリングを容易にするマネージドサービスです。Amazon OpenSearch Service は Amazon Elasticsearch Service の後継であり、OpenSearch およびレガシーの Elasticsearch OSS (ソフトウェアのファイナルオープンソースバージョンである 7.10 まで) をサポートしています。
2021年9月にAmazon Elasticsearch Service の名称が Amazon OpenSearch Serviceに変更されておりますが、基本的にはElasticsearchと大きく違いはない為、調査の際に一部 Elasticsearch の記事などを参考にしても支障はなかったです。
今回利用するデータ
沖縄といえばやはり海!なので、海の情報を使い今回は位置情報の利用と重み付けを行いたいと思います。
おすすめ海ランキングのようなコンテンツは既にありますが、現在地から近い海やアクティビティでの検索ができるようなものはありませんでしたので、海にちなんだデータを扱いたいと思います。
データは以下の情報を持っています。
・ビーチ名
・住所
・駐車場有無
・アクティビティ数
・アクティビティ内容
・設備数
・設備内容
※アクティビティや設備については全ての海に確認しにいくのも難しかった為、私の想像となります。
OpenSearchクラスタの作成などはここで記載せずとも沢山の記事がある為、割愛します。
VPC内につくる際はこちらの記事が参考になります。
VPC 内で Amazon OpenSearch Service ドメインを起動する
Index・Mappingの作成、データ登録
今回はデータ量も少ないのでダッシュボードへアクセスし、DevToolsから操作します。
DevToolsにて以下を実行します。
■index・Mappingの作成
今回は全文検索は行わない為、文字列は全てKeywordにしています。
PUT /sea_data
{
"mappings" : {
"dynamic" : "false",
"properties" : {
"name": {
"type" : "keyword"
},
"municipalities": {
"type" : "keyword"
},
"address": {
"type" : "keyword"
},
"location" : {
"type" : "geo_point"
},
"parking" : {
"type" : "integer"
},
"activity_cnt" : {
"type" : "integer"
},
"activity" : {
"type" : "nested",
"properties" : {
"name" : {
"type" : "keyword"
}
}
},
"facility_cnt" : {
"type" : "integer"
},
"facility" : {
"type" : "nested",
"properties" : {
"name" : {
"type" : "keyword"
}
}
}
}
}
}
■Bulk APIを使用し一括でデータを登録
POST sea_data/_doc/_bulk
{ "create" : { "_id" : "1" } }
{"activity":[{"name":"シュノーケル"}],"activity_cnt":1,"address":"知念久高","facility":[{"name":"トイレ"},{"name":"シャワー"}],"facility_cnt":2,"location":{"lat":26.1566466,"lon":127.8836075},"municipalities":"南城市","name":"メーギ浜","parking":1}
{ "create" : { "_id" : "2" } }
{"activity":[],"activity_cnt":0,"address":"瀬長","facility":[{"name":"トイレ"},{"name":"シャワー"},{"name":"足洗い場"},{"name":"更衣室"}],"facility_cnt":4,"location":{"lat":26.1770625,"lon":127.6436875},"municipalities":"豊見城市","name":"瀬長ビーチ","parking":0}
{ "create" : { "_id" : "3" } }
{"activity":[{"name":"シーカヤック"},{"name":"バナナボート"},{"name":"ビッグマーブル"},{"name":"体験シュノーケリング"}],"activity_cnt":4,"address":"西崎町","facility":[{"name":"トイレ"},{"name":"ロッカー"},{"name":"更衣室"},{"name":"シャワー"},{"name":"売店"},{"name":"自販機"},{"name":"クラゲ防止ネット"}],"facility_cnt":7,"location":{"lat":26.1323147,"lon":127.6512737},"municipalities":"糸満市","name":"美々ビーチいとまん","parking":1}
〜〜〜 省略 〜〜
■function_score を利用しスコアに重み付け
色々なアクティビティがあったほうが楽しいので、メインとしてはアクティビティの重みを上げたいと思います。
なので、今回は以下のような重み付けをしたいと思います。
1.アクティビティが4つ以上ある場合、5点 加点
2.設備 が2つ以上ある場合、2点 加点
3.なるべく近いほうがいいので、近い方を減衰関数で加点
会社の位置情報を元に検索すると、以下のようなクエリになります。
GET sea_data/_search
{
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"match_all": {}
}
],
"filter": [
{
"geo_distance": {
"distance": "20km",
"location": {
"lat": 26.284346,
"lon": 127.745369
}
}
}
]
}
},
"score_mode": "sum",
"functions": [
{
"gauss": {
"location": {
"origin": "26.284346,127.745369",
"offset": "0km",
"scale": "10km",
"decay": 0.9
}
}
},
{
"filter": {
"range": {
"facility_cnt": {
"gte": 2
}
}
},
"weight": 2
},
{
"filter": {
"range": {
"activity_cnt": {
"gte": 4
}
}
},
"weight": 5
}
]
}
}
"_source":[
"name","municipalities","address","activity_cnt","facility_cnt"
]
}
検索APIとしてリクエストする
残りはユーザーの位置情報をAPIで受け取り、受け取った位置情報をLambdaからOpenSearchへリクエストする際に使用します。
curl -H "Content-Type: application/json" \
-d '{"location":{"latitude":26.284346, "longitude":127.745369}}' \
-X POST https://*************/dev/seasearch | jq | grep -e score -e name
返却内容
"_score": 7.9278913,
"name": "クリード西原マリンパーク"
"_score": 7.802432,
"name": "あざまサンサンビーチ"
"_score": 7.7300305,
"name": "みーばるビーチ"
"_score": 7.6743894,
"name": "美々ビーチいとまん"
"_score": 2.9979672,
"name": "トロピカルビーチ"
"_score": 2.993073,
"name": "アラハビーチ"
"_score": 2.9879184,
"name": "サンセットビーチ"
"_score": 2.8974116,
"name": "波の上ビーチ"
"_score": 2.7723508,
"name": "瀬長ビーチ"
"_score": 2.7305648,
"name": "豊崎海浜公園オリオンECO美らSUNビーチ"
"_score": 2.6618044,
"name": "メーギ浜"
まとめ
このように、OpenSearchを利用することでお手軽に位置情報と連動した検索APIを作ることができます。
更に、夏であればBBQ対応しているビーチに重み付けを行ったり、透明度のようなデータも追加することで透明度にも重み付けを行うなど、より使いやすい検索APIが提供できます。
今回はサンプルだった為、簡易的なAPIとなっていますが、条件を組み替える場合もクエリを書き換えるだけですぐに確認が行えるので、スコア調整も比較的簡単に確認できます。