Question

I am trying to match a search query against two fields, as well as filter by facets if selected from dropdowns on the page.

When the user enters keywords it should match if found in two database fields: Title and Description. The dropdowns filter by a status, and a type.

Here is my Tire search configuration:

  def self.search(params)
      tire.search(load: true, page: params[:page], per_page: 25) do
        query do
          boolean do
            should { string "title:#{params[:query]}", default_operator: "OR" } if params[:query].present?
            should { string "description:#{params[:query]}", default_operator: "OR" } if params[:query].present?
            must { term :status_id, params[:status_id] } if params[:status_id].present?
            must { term :type_id, params[:type_id] } if params[:type_id].present?
          end
        end
        sort { by :updated_at, "desc" } if params[:query].blank?
        facet "status" do
          terms :status_id
        end
        facet "type" do
          terms :type_id
        end
      end
  end

Indexing settings:

  settings :analysis => {
      :filter => {
          :my_ngram => {
              "type" => "nGram",
              "max_gram" => 10,
              "min_gram" => 3}
      },
      :analyzer => {
          :my_analyzer => {
              "type" => "custom",
              "tokenizer" => "lowercase",
              "filter" => ["my_ngram"]
          }
      }
  } do
    mapping do
      indexes :title, boost: 10, analyzer: 'my_analyzer'
      indexes :description, boost: 5, analyzer: 'my_analyzer'
      indexes :status_id, :type => 'integer'
      indexes :type_id, :type => 'integer'
    end
  end

I originally only had the title and description fields, which was working fine. I am now trying to add the ability to filter by status and type.

What is the proper way to configure this? If status is selected, it should only return records with that status. The same follows for type, and if both are selected.

Any help is appreciated.

It's not that errors occur, but the results no longer filter at all by either keywords or facets:

curl -X GET 'http://localhost:9200/projects/project/_search?load=true&size=25&pretty' -d '{"query":{"bool":{"should":[{"query_string":{"query":"title:test","default_operator":"OR"}},{"query_string":{"query":"description:test","default_operator":"OR"}}],"must":[{"term":{"status_id":{"term":"1"}}},{"term":{"type_id":{"term":"1"}}}]}},"facets":{"status":{"terms":{"field":"status_id","size":10,"all_terms":false}},"type":{"terms":{"field":"type_id","size":10,"all_terms":false}}},"size":25}'

# 2013-08-16 12:08:34:791 [200] (31 msec)
#
# {"took":31,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]},"facets":{"status":{"_type":"terms","missing":0,"total":0,"other":0,"terms":[]},"type":{"_type":"terms","missing":0,"total":0,"other":0,"terms":[]}}}
Was it helpful?

Solution

If you could create the equivalent of this I think you would get the desired results. (Please excuse the lack of quotes on the JSON keys!)

{
    query: {
      multi_match: {
        query: "test",
        fields: ["title", "description"]
      }
    },
    filter: {
      and: [
        {
          term: { status_id: 123 }
        },
        {
          term: { type_id: 456 }
        }
      ]
    },
    facets: {
      type: {
        terms: {
          field: "type_id",
          size: 10
        }
      },
      status: {
        terms: {
          field: "status_id",
          size: 10
        }
      }
    }
}

Update

I don't know tire but will try to write something!

  def self.search(params)
    tire.search(load: true, page: params[:page], per_page: 25) do
      query do
        boolean do
          should { match :title params[:query] } if params[:query].present?
          should { match :description  params[:query] } if params[:query].present?
        end
      end

      sort { by :updated_at, "desc" } if params[:query].blank?

      filter :and, { :term => { :status_id => params[:status_id] } } if params[:status_id].present?
                   { :term => { :type_id => params[:type_id] } } if params[:type_id].present?
    end
  end

You will probably have to fix the ruby, but a few things to note. Match queries are the recommended default string search, they are faster than query_string ones (though you have slightly less control). Also

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top