検索ボックスなどから渡される文字列をもとに、Elasticsearch用の適当な検索クエリをつくる方法について説明します。クエリ文字列ここで言っている文字列というのは、先述した通り検索ボックスなどから渡される文字列のことです。QiitaでElasticsearchに関する記事を探したければ「Elasticsearch」と入力すると思いますが、そのような文字列のことを指しています。表現しているので、ここではそのような文字列のことをクエリ文字列と呼ぶことにします。検索クエリElasticsearchを利用して検索を行うときは、GETリクエストで検索クエリを渡し、レスポンスボディ経由でJSON形式の検索結果を受け取ると思いますが、今回クエリ文字列から生成したいのはそのGETリクエストで渡す検索クエリのことです。ようにURLクエリパラメータ経由で渡したり、リクエストボディにJSONを含めて渡したりできますが、この記事では後者のJSONで表現できる形式の検索クエリを文字列から生成します。パターン例えばQiitaの検索ボックスでは、いくつかの特殊なパターンの検索クエリが利用できます。Railsに完全一致する記事のみが一致します。ように、いくつかのパターンが組み合わせられることもあり得ます。渡されたクエリ文字列を解析し、これらのパターンの定義に従ってElasticsearch用の適切な検索クエリを生成するプログラムをつくる、というのがこの記事の目的です。...Tokenizerクエリ文字列を検索クエリに変換するために、まず受け取ったクエリ文字列をトークンという単位に分割し、トークンの列を種類ごとに幾つかのグループに分け、最終的にそのグループ達を木構造に並び替えて検索クエリに変換する、ということをやります。-d」というクエリ文字列を受け取ったときは、以下のように5つのトークンに分割します。正規表現を使って文字列からトークンの配列に変換しています。単純化すると以下のようなコードになります。```使うときはこんな感じで使います。-d")OrSeparatableNodeTokenizerを利用してトークンの配列を生成したあと、これらをグループに分け検索クエリに変換します。ORトークンに着目して、ORトークンの前後でトークンを分けていきます。clauseを利用し、それぞれのグループのいずれかが満たされるものが一致するようなクエリをつくります。Queryではなく単純な1つのクエリを出力するようにします。OR」のようなクエリ文字列が与えられた場合にORの部分を無視するために、ORトークンで分割してトークンが1つも存在しないグループは無視します。Queryになるかもしれないし、ならないかもしれない、という意味を込めてOrSeparatableという名前にしました。この部分の処理を担当しています。FilterableNodeOrSeparatableNodeでトークンをグループに分割したあと、今度はそれぞれのグループを適切なクエリに変換する必要があります。条件を表すトークンに分けられます。利用してこれらの条件を表現することにします。必須の条件というのは指定されているときには必ず満たしてほしい種類のもので、「-a」や「tag:a」のようなトークンがこれに該当します。b」のようなクエリ文字列で検索したときは、aとbの両方に一致すればより嬉しいが、片方に一致するだけでもまあ良しという扱いにし、必須ではない条件として扱うことにします。Queryではなく、より単純なクエリを生成します。QueryBuilder後は個々のトークンを適切な形に変換していく処理が続くので省略しますが、上記のような流れでトークン列をクエリに変換していくと、最終的に検索クエリが得られます。組み合わせて検索クエリを表現しています。qiita-elasticsearchでは、クエリ文字列をトークンに分割する処理と検索クエリを組み立てる処理をまとめてQueryBuilderというクラスで担当しています。単純な例では、以下のような使い方になります。この部分の処理を担当しています。題材にしながら、クエリ文字列をElasticsearch用の検索クエリに変換する方法について簡単に説明しました。qiita-elasticsearchはまだまだ発展途上ではありますが、単純な仕組みで実現されているので、Elasticsearchの学習に役立てていければ幸いです。