ローカル環境にCloudSearch互換の検索サービスを立てる

September 25, 2016

TL;DR

ローカルで動くCloudSearch互換の検索サービスをforkしてaws-sdk-rubyから使えるようにしました。

https://github.com/hokuma/nozama-cloudsearch

=================

CloudSearchはAWS提供のフルマネージドな検索システムです。 https://aws.amazon.com/jp/cloudsearch/

ぽちぽちっとブラウザからクリックするだけで検索サービスが構築できるので、要件がハマれば便利なサービスだと思います。 実際私もとあるプロジェクトで使っています。

便利に使わせてもらっている一方、CloudSearchを使っていて一つ困る点はローカルに環境を用意できないことです。 CloudSearchはAWS独自の検索サービスであるため、ローカル環境での開発用途であってもAWS上に検索ドメインを起動しておく必要があります。 一人で開発であれば開発用に一ドメイン用意すれば良いかもしれませんが、複数人での開発となると各開発者ごとにドメインを立ち上げるのか? CloudSearchは使えば使うだけお金がかかります。人数分用意する、というのも現実的ではなさそうです。

そこで、CloudSearchと互換するAPIを持つローカル版のCloudSearchはないものかと探したところ以下のプロジェクトを見つけました。

https://github.com/oisinmulvihill/nozama-cloudsearch

MongoDBとElasticsearchで構築されたCloudSearch互換のAPIを持つ検索サービスです。 Readmeにあるように開発環境でのテスト向けに作られています。

これで問題解決か、、、と安堵したのもつかの間、実際にrubyのaws-sdkから使ってみると動きません。 ということで、動くまでにやったことをメモがてら記録します。

とりあえず早く使わせてくれ、という場合は本家からforkしたこちらをどうぞ。

https://github.com/hokuma/nozama-cloudsearch

本家に向けてこれからPRを出していこうと思っています(一部はすでに取り込み済み)が、とりあえず使ってみたい場合はこちらで。

以下、一つ一つ動くまでにやったことです。(実際のcommit順は前後してます)

version属性を必須とする制約をなくす

https://github.com/hokuma/nozama-cloudsearch/commit/ee826e42c24abbceeaabc1b8334b8f7b3f7ca051

なぜかドキュメント保存時にversion属性が必須となります。 CloudSearchのドキュメントを見る限り、version属性が必須という記載が見つけられませんでした。 少なくとも本物のCloudSearchを使っていてそれが原因でエラーになったことはありません。

以下にあるドキュメント保存時のサンプルを見てもversionなどという属性値はありません。 https://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/preparing-data.html

ドキュメントの保存をupsertにする

https://github.com/hokuma/nozama-cloudsearch/commit/5c203622c34c1bf66ff0b83dd61ab933c1eece26

検索対象のドキュメントそのものはMongoDBで管理し、検索インデックスをElasticsearchに作成する構成になっています。

MongoDBにドキュメントを保存する際にinsert処理をしているため、同一IDを持つドキュメントを保存するとID重複エラーとなります。 CloudSearchの更新処理は常にreplace処理です。なければ作成、すでにあれば置換します。 insert処理はCloudSearchの仕様にあっていないのでupsert処理に変えました。 厳密にいうとupsertも正しくはないのですが、開発環境でとりあえず使う分には問題ないかと。

ドキュメントの中身を返す

https://github.com/hokuma/nozama-cloudsearch/commit/796b9fa881ea859e168a11ad180ca384bdc963db

ID重複の問題を解決してドキュメントは保存できました。 しかし、いざ検索してみるとドキュメントのIDしか取得できません。 CloudSearch的には、returnパラメータで指定されたフィールドを返すか、 指定がなければスキーマ上で戻り値として利用可能と定義されたフィールドすべてを返します。 https://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/retrieving-data.html

しかし、戻り値として利用可能かどうかの定義とか、returnパラメータの実装とか正直面倒です。 常にヒットしたドキュメント情報を返すようにしました。

SDKフォーマットに対応する

https://github.com/hokuma/nozama-cloudsearch/commit/c66bda560fbbfb124e5a3cb49d70b67a7d5bee55

aws-sdk-rubyから叩くとjsonのパースで失敗しました。 どうやらaws-sdk-rubyから叩くとドキュメントのフィールド値が必ず配列になるようです。 証拠となるドキュメントが見つけられなかったのですが、aws-sdk-rubyの中にこんなjsonがあります。

https://github.com/aws/aws-sdk-ruby/blob/ef9f2d3655c4bacb3adff580a6b295df510f78fe/aws-sdk-core/apis/cloudsearchdomain/2013-01-01/api-2.json

formatというパラメータがドキュメントのフィールド値のフォーマットを制御しているようなので、foramtsdkが指定されていたらすべてを配列で返すようにしました。

日本語に対応する

https://github.com/hokuma/nozama-cloudsearch/commit/5e93e43262d09a5bd371082055235878eb9238ed

日本語を含むドキュメントの保存はできるのですが、検索すると文字コード関連のエラーが起きました。 pythonの日本語の扱いよくわかってないです :bow:。 とりあえずこれで動きました。

ここまでで、一通りドキュメントの保存、検索はできました。

comments powered by Disqus