Elasticsearch percolatorと私
再びとはなんぞやといいますところ、このブログの中での登場が2回目ですという意味です(笑)。
最初の登場は、こちら。
上記の説明では、percolatorについては、公式リファレンスの直訳・意訳で検索条件をインデックスに入れて...のようにまとめました。
が、
Elasticsearch percolatorの便利さは上記では説明できていない
今思えば、percolatorが何に向いているか・何ができそうかを中心に説明しても良い気分です。
ググったところ、percolatorの仕組みを先に説明されている例が多いため、ここでは多少外れていても、仕組みは後回しにどちらかと言えばpercolatorの良さをアピールするような説明をすると、だれかの役に立つのではと思ってこちらに書き綴って見ます。
とまあ、もったいつけていいますが、
Elasticsearchのpercolatorは、
Elasticsearchの構成を活かした、後述するある仕掛けにしたがうことで、
他の手法より簡単に
あるドキュメント(JSON形式で表せるあるドメインのオブジェクト)が、 特定の条件に収まっているか/外れているかを
判定できる仕組みです。
なんか普通ですか?
ちょっとアピール不足かな。
すみません> Elasticsearchちゃん。
ちなみに、ググった範囲ですが、下記を見る限り、同じluceneファミリーのsolrには、percolatorに相当するものは無いようですね。
percolatorは判定エンジン(仮称)
さて、ドキュメントが...判定できるというのは、つまるところ、次のようなことができます。
- カテゴリの分類
- なんらかの条件の範囲内であること
- なんらかの条件の範囲外であること
- ビジネスルールのバリデーション
などなどです。
上記の例全般はもちろん、その中でもエッジケースぽくてかつアプリでロジックを組みたく無いなー的なものにも相性が良いです。
例えば、ドキュメント中にNGワード(チェック対象のワードはそれなりのワード数)が入っていないかのチェック(あるいは入っているのチェック)とか、判定条件自体が形は似ているけど都度になるので判定条件自体をあらかじめ別の場所で生成しておきそれとぶつけたい*1というようなものがあげられます。
これは後述のとおり、通常はプログラムのメインのロジックで、ある条件1に該当するか、条件2に該当するか... というような対象の条件全てを適用してチェックするのと異なり、percolatorでは、あらかじめ定義した条件のうち、マッチするものがあるかという依存関係の逆転(dependency inversion principle) ぽい方式になることも貢献していると思います。
percolatorの判定ロジックはElasticsearchの検索クエリDSLで設定できる
Elasticsearchの構成を活かした、後述するある仕掛け...はさらに後述するとして、これらの判定条件は、ElasticsearchのクエリDSLが利用できます。
よって、(JOINや相関問い合わせを伴わない)SQLのWHERE句でいけるようなことはひととおりできますし、Elasticsearchの全文検索エンジンならではの次のような条件を使うこともできます。
つまるところ、プログラミング言語でif文やelse文を組み合わせてでごちゃごちゃしたり、ごちゃごちゃを回避しようとして潔癖に無理に継承などで集約してかえってわかりにくくなってしまうようなところを、判定用の式を組み合わせて宣言的に(贅肉を落として)構成できる&あえて外出しできるというところが、他の手法に対して一つのアドバンテージになっています。
- 全文検索のmatch句を使った条件判定
- Geo系のクエリの利用
- Range系のクエリの利用
- 検索対象フィールドのワイルドカード指定などのシンタックスシュガー
- (ちょっと飛躍するかもしれませんが)前項のシンタックスシュガーが用意されていることと全体的に「式」からなる宣言的な記法のため、コード値などをハードコーディングしても可読性やメンテナンス性が落ちにくい。(ここでは、原理主義的な外部定義よりは、適度なハードコーディングはビジネスロジックを直接表現しているのでわかりやすい場合もあるという立場で述べています。)
- Geo系のポリゴン当て、Rage系のINTERSECTS、(試していないですが)Span queriesのような、JSONならではの構造化データに対する複数次元の範囲でヒットさせるような方法*2
2019/2/12 23:00 追記 上記で、いろいろできるとぶっ放していますが、実は使えるクエリDSLの組み合わせに制限があるような気もしてきました。このような記事を書いておいて調査が甘くて申し訳ありませんが、実際の使用はmapping設定に留意の上、お手元の環境で実際にお試しください。
参考: www.elastic.co
判定ロジックをストックできる
もう一つの便利なところは、Elasticsearchの構成を活かして、この判定条件をインデックスに文字通りストックしていくようなことができます。
図で示すとこんな感じです。ここまで引っ張った「Esの構成を活かした...後述」も盛り込んだつもり。
頑張ってまとめてみましたが伝わるかな〜?
余計わかりにくくなっているかもしれないと不安になりつつ、実際にpercolatorを試してみれば、腑に落ちる点もあると思いますが...
判定エンジンらしくAPIとして使うことができる(と思う)
さて、検索の世界から少し逸脱しますが、このような判定の仕組み&判定条件をストックしていけるというのは、アプリケーションの世界での更新系やデータ管理の基礎機能での使い勝手が良いです。
私見ですが*3、この判定条件をアプリの本体ロジックとは少し切り離して一見無関係なElasticsearchにストックしていける(HTTPのAPIとしても使える)ので、これを緊急回避的に設定可能なライブラリとして追い出す選択肢があると、設計のアソビをもうけやすくなります。
というのも、例えば、DDDなどにおいては、ビジネスルールだビジネスロジックだ、あるいは安定していると言われている「データ」に関するバリデーションのロジックについては、各所で知見がまとめられているように、ドメイン知識として濃淡や変化するものしにくいものの仕分けして適当な責務のクラス設計などに振り分けてことになるというのが定石だと思いますが、実のところ特定分野ではこのドメイン知識の密度の見極めが難しく、プログラムの設計としてはベストというものが実現できたとしても、それでもドメイン知識の重要なものとそうでないものを見誤って、それほど重視しなくて良いところを手厚く、またその逆になってしまうこともしばしばあります。
まあ、そんなことができるだけ少なくなるように、モデリングの工夫やうまい手法をみなさんが研究されているのだと思いますが、やはり当初はそれが「安定した論理=コアのドメイン知識」として見えやすい(そしてその当初の時点ではその見立ては間違っていない)のがやっかいで、コアだと思ってがっつり実装したらそうでもなかったということもよくある話だと思います。
ということで、そんな設計に迷うような場合に、PoCや初期時点では、ビジネルルールレベルのバリデーションなどをElasticsearchのpercolatorに追い出しておくというのは、悪くない案だと考えています。
まとめ
Elasticsearchのpercolatorについて、データ更新の際に便利だぞということで、自分の感じたことを書いてみました。
最後にもういちど検索の話に戻ると、メインの検索の前に、percolatorでなんらかの一次判定をしてやるという用途はもちろん使えます。
また、複雑な検索条件の前借りにも使えます。
何かというと、メインのインデックスにドキュメントをPOSTする前に、percolatorでカテゴリ判定したり、以前の下記の記事で主張させていただいた「ビジネスロジックの圧縮((percolatorは、複数フィールドを持つあるドキュメントに対してその複数フィールドの独特のビジネスロジックをpercolatorの検索で判定してやり、検索サービスに必要な最小限の知識(1フィルードのBoolean)に集約する」という用途でも力を発揮すると思います。
是非みなさんもうまい使い方を見つけて情報公開いただけると私もマネマネさせていただいたいと思っています。
具体的なデータを使ったプッシュ記事を書きたいけど、今回はひとまずここまで。
ここまで読んでいただきありがとうございました。