elasticsearchで複雑なクエリーを組み立てる

背景

railsから全文検索エンジンelasticsearchを利用する"では、全文検索を手軽に実装することを目的としていたので、検索クエリーの組み立てにはあまり踏み込んでいませんでした。

今回は複数のOR, AND条件の組み合わせなどを設定したい場合の記述方法を説明します。

bool query

elasticsearchには複雑なクエリーを組み立てるためのbool queryという仕組みが用意されています。

bool queryではmust, should, must_notの組み合わせで表現を行います。
mustは必ず満たすべき条件を、shouldは少なくとも満たす条件を、must_notは満たしてはならない条件を指定します。

tireでの記述例

tireでの記述例を示します。

class Article < ActiveRecord::Base
  include Tire::Model::Search
  include Tire::Model::Callbacks
  # ~中略~
  def self.search(params)
    tire.search(load: true) do
      query {
        boolean do
          should   { string 'tags:ruby' }
          should   { string 'tags:java' }
          must_not { string 'tags:python' }
        end
      }
    end
  end
end

上記の例の場合、tags:ruby OR tags:java AND NOT tags:pythonと指定していることになります。

boolクエリーの拡張

複数のboolクエリーを組み合わせる場合、各条件をブロックでまとめ、見通しを良くすることも可能です。

tags_query = lambda do |boolean|
  boolean.should { string 'tags:ruby' }
  boolean.should { string 'tags:java' }
end
published_on_query = lambda do |boolean|
  boolean.must   { string 'published_on:[2011-01-01 TO 2011-01-02]' }
end
class Article < ActiveRecord::Base
  include Tire::Model::Search
  include Tire::Model::Callbacks
  # ~中略~
  def self.search(params)
    tire.search(load: true) do
      query {
        boolean &tags_query
        boolean &published_on_query
      }
    end
  end
end

boolクエリーにおけるshouldのオプション

boolクエリーではshouldの最小数一致数を指定することも可能です。
elasticsearchではminimum_should_matchパラメータで指定しますが、tireではminimum_number_should_matchでの指定なので若干注意が必要です。

class Article < ActiveRecord::Base
  include Tire::Model::Search
  include Tire::Model::Callbacks
  # ~中略~
  def self.search(params)
    tire.search(load: true, per_page: params[:per_page] ) do
      query {
        boolean(minimum_number_should_match: 1) do
          Array(params[:cc]).each {|c| should { string 'curriculums:'    + c}}
          Array(params[:jc]).each {|j| should { string 'job_categories:' + j}}
        end
        boolean do
          must {string 'school_code:' + params[:school_code] }
        end
      }
      sort {
        by :sort_key, :asc
      }
    end
  end
end