0

I'm using tire and elasticsearch in my rails project, which is a retail site for car parts. ES is powering a faceted search page used for browsing the parts catalog. My question is: How can I make the term filters return only exact matches while searching the same fields with a query string, making use of analyzers only on the query string?

I hope that makes sense. I will try to provide an example:

The model/index in question is called Parts. Assume that a part has mappings called categories and sub_categories. If a user selects the Brake category and the Brake Caliper Carrier subcategory (creating term filters) I have to make sure that parts from the Brake Caliper subcategory are not also returned – it is a separate subcategory. I do, however, want the user to be able to simply enter something like "brakes" into the search field (creating a query_string) and get results from products within all of those categories.

Here is the relevant code from the Part model:

def to_indexed_json
    fits = fitments.try(:map) do |fit|
      {
        make: fit.try(:make).try(:name),
        make_id: fit.try(:make).try(:id),
        model: fit.try(:model).try(:name),
        model_id: fit.try(:model).try(:id),
        year: fit.year,
        sub_model: fit.sub_model
      }
    end
    {
      id: id,
      name: name,
      description: description,
      fitments: fits,
      categories: root_categories,
      sub_categories: sub_categories,
      price: price,
      condition_id: condition_id,
      country_of_origin: country_of_origin,
      brand: brand,
      oem: oem,
      thumb_url: part_images.first.try(:image).try(:thumb).try(:url),
      city: user.try(:city),
      inventory: inventory,
      part_number: part_number,
      user: user.try(:public_name)
    }.to_json
  end

  mapping do
    indexes :id, type: 'integer'
    indexes :name, analyzer: 'snowball', boost: 40
    indexes :description, analyzer: 'snowball', boost: 12

    indexes :price, type: "integer"
    indexes :country_of_origin, index: :not_analyzed
    indexes :condition_id, type: "integer"
    indexes :brand, index: :not_analyzed
    indexes :oem, type: "boolean"
    indexes :city, index: :not_analyzed
    indexes :inventory, type: "integer"
    indexes :part_number, index: :not_analyzed
    indexes :user, index: :not_analyzed

    indexes :thumb_url, index: :not_analyzed

    indexes :fitments do
      indexes :make
      indexes :make_id, type: "integer" #, index: 'not_analyzed'
      indexes :model
      indexes :model_id, type: "integer" #, index: 'not_analyzed'
      indexes :year, type: "integer"
      indexes :sub_model
    end

    indexes :categories do
      indexes :name, index: :not_analyzed
      indexes :id, type: "integer"
    end

    indexes :sub_categories do
      indexes :name, index: :not_analyzed
      indexes :id, type: "integer"
    end

  end

def search(params={})

  query_filters = []

  tire.search(:page => params[:page], :per_page => 20) do

    query_filters << { :term => { 'fitments.make_id' => params[:make] }} if params[:make].present?
    query_filters << { :term => { 'fitments.model_id' => params[:model] }} if params[:model].present?
    query_filters << { :term => { 'categories.name' => params[:category] }} if params[:category].present?
    query_filters << { :term => { 'sub_categories.name' => params[:sub_category] }} if params[:sub_category].present?
    query_filters << { :term => { 'city' => params[:city] }} if params[:city].present?
    query_filters << { :term => { 'condition_id' => params[:condition] }} if params[:condition].present?
    query_filters << { :term => { 'brand' => params[:brand] }} if params[:brand].present?
    query_filters << { :term => { 'oem' => params[:oem] }} if params[:oem].present?

    query do
      filtered do
        query {
          if params[:query].present?
            string params[:query]
          else
            all
          end
        }
        filter :and, query_filters unless query_filters.empty?
      end
    end

    facet("categories") { terms 'categories.name', size: 50 } unless params[:category].present?
    facet("cities") { terms 'city', size: 50 } unless params[:city].present?
    if params[:category].present? && !params[:sub_category].present?
      facet("sub_categories") { terms 'sub_categories.name', size: 50 }
    end
    facet("condition_id") { terms 'condition_id', size: 50 } unless params[:condition].present?
    facet("brand") { terms 'brand', size: 50 } unless params[:brand].present?
    facet("oem") { terms 'oem', size: 2 } unless params[:oem].present?

    size params[:size] if params[:size]

  end
end

1 Answer 1

1

You have to use the multi_field feature of Elasticsearch and filter on the non-analyzed fields; see eg. Why multi-field mapping is not working with tire gem for elasticsearch?

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks karmi. Its a huge help that you are here on SO supporting your gem!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.