Search code examples
ruby-on-railsruby-on-rails-4.2searchkick

Rails 4 searchkick with many-to-many relationsip


How to implement a Category filter for searchkick. Basically I have an input field that takes the query term, but I also want a dropdown that users can pick a category to search from or search in ALL categories. There is a many-to-many relationship between posts and categories

My models are:

-- post.rb
class Post < ActiveRecord::Base
 has_many :post_categories
 has_many :categories, through: :post_categories
 searchkick text_start: [:title]
end

-- category.rb
class Category < ActiveRecord::Base
    has_many :post_categories
    has_many :posts, through: :post_categories
end

--post_category.rb
class PostCategory < ActiveRecord::Base
    belongs_to :post
    belongs_to :category
end

Now in my posts_controller index action, I have the following, which works so far by returning all posts that match the query parameter, or returns all posts if no query parameter is provided in the search input.

class PostsController < ApplicationController

    def index
        query = params[:q].presence || "*"
        @posts = Post.search (query)
    end
end

This works well so far. But I also want to add a category filter in the view, so that the user can choose to search for the query string within a particular category, or search in all categories if no category is selected. Thanks in advance.


Solution

  • As per searchkick documentation, you can add parameters to .search query - see here section Queries, specifically where. Sample from docs:

    Product.search "apples", where: {in_stock: true}, limit: 10, offset: 50
    

    Should be smt like

    Post.search query, where: {category: [some_array]}
    

    Note - searchkick gem converts conditions from where statement to ElasticSearch filters (see here)

    Update - for searching by attributes of related objects (not model itself), you should include its fields into search_index - see sample here

    Add the category titles to the search_data method.

    class Project < ActiveRecord::Base
      def search_data
        attributes.merge(
          categories_title: categories.map(&:title)
        )
      end
    end
    

    Also this question on related topic

    By default search_data of searchkick is return of serializable_hash model call -see sources for reference.

    def search_data
      respond_to?(:to_hash) ? to_hash : serializable_hash
    end unless method_defined?(:search_data)
    

    Which does not include anything of associations by default, unless passed with :include parameter - source

     def serializable_hash(options = nil)
        ...
        serializable_add_includes(options) do ..
     end
    
     def serializable_add_includes(options = {}) #:nodoc:
       return unless includes = options[:include]
     ...
     end