4

I have two models, Posts and Channels that I'm searching using Tire and ElasticSearch.

Currently, everything works just fine. However, I'm performing two searches and returning the results of both. I'd like to consolidate this into one – and search both models at the same time.

Below is the controller action I'm using to do just this:

def browse
    @user = current_user
    @channels = Channel.search(params)
    @posts = Post.search(params)
end

Below is the Post model:

class Post < ActiveRecord::Base
  attr_accessible :description, :name, :tag_list, :thumbnail_image, :status, :post_type, :subtitle, :summary, :visibility, :user
  acts_as_taggable

  has_one :publication
  has_one :user, :through => :publication
  has_many :subscriptions, dependent: :destroy


  has_many :channel_post_connections, dependent: :destroy
  has_many :channels, :through => :channel_post_connections

  accepts_nested_attributes_for :channel_post_connections
  accepts_nested_attributes_for :channels
  accepts_nested_attributes_for :publication
  accepts_nested_attributes_for :user


  has_many :post_pages, dependent: :destroy

  validates_presence_of :post_type, :name, :subtitle, :tag_list, :summary, :thumbnail_image, :if => :post_is_published?


  include Tire::Model::Search
  include Tire::Model::Callbacks
  index_name 'posts_index'

  def self.search(params)
    tire.search(load: true) do
      query { string params[:query], default_operator: "AND" } if params[:query].present? 
      sort { by :created_at, "desc" } if params[:query].blank?
    end
  end

  def to_indexed_json
    to_json(methods: [:user_first_name, :user_display_name])
  end

  def user_first_name
    self.user.first_name if self.user.present?
  end

  def user_display_name
    self.user.display_name if self.user.present?
  end

end

Below is the Channel model:

class Channel < ActiveRecord::Base
    attr_accessible :title, :description, :cover_image, :status, :writable, :visibility, :thumbnail_image

    has_one :channel_publication
    has_one :user, :through => :channel_publication

    has_many :channel_subscriptions

    has_many :channel_post_connections

    accepts_nested_attributes_for :channel_publication
    accepts_nested_attributes_for :user

    mount_uploader :thumbnail_image, ChannelThumbnailImageUploader

    include Tire::Model::Search
    include Tire::Model::Callbacks
    index_name 'channels_index'

    def self.search(params)
        tire.search(load: true) do
            query { string params[:query], default_operator: "AND" } if params[:query].present? 
          sort { by :created_at, "desc" } if params[:query].blank?
        end
    end

    def to_indexed_json
      to_json(methods: [:user_first_name, :user_display_name])
    end

    def user_first_name
      self.user.first_name if self.user.present?
    end

    def user_display_name
      self.user.display_name if self.user.present?
    end

end

Then, in my view, I have two loops:

- @posts.each do |post|
  %h3
    = post.name

- @channels.each do |channel|
  %h3 
    = channel.title

Again – my goal is to execute one search, and display the results of posts and channels jumbled together using one loop.

Any help would be greatly appreciated... I'm pulling my hair out with this one!


UPDATE

Adding app trace...

app/controllers/posts_controller.rb:111:in `[]'
app/controllers/posts_controller.rb:111:in `block in browse'
app/controllers/posts_controller.rb:110:in `browse'

ANSWER

I replaced my browse action in the controller with:

def browse
    @user = current_user
    @search_items = Tire.search(['posts_index', 'channels_index'],{load: true}) do |search|
      if params[:query].present? 
        search.query do |q|
          q.string params[:query], default_operator: "AND"
        end
      end
      search.sort { by :created_at, "desc" }
    end
    @results = @search_items.results
end

And then looped through @results in the view – and it worked!

cmw
  • 946
  • 2
  • 11
  • 26

1 Answers1

3

You can use,

      def browse
        @search_items = Tire.search(['posts_index', 'channels_index'],{load: true}) do |search|
          search.query do |q|
            q.string params[:query], default_operator: "AND" if params[:query].present? 
          end
          search.sort { by :created_at, "desc" } if params[:query].blank?
        end
        @results = @search_items.results
      end
Vamsi Krishna
  • 3,742
  • 4
  • 20
  • 45
  • I've tried placing this within the "def self.search(params)" block – with no luck: undefined method `each' for #. How do you recommend I implement this given the code above? I've tried repurposing this a number of ways, and haven't had any success. This definitely appears to be what I'm looking for, however. – cmw Sep 23 '13 at 17:51
  • You need to place it in the browse method not the self.search method. Check my edited answer. – Vamsi Krishna Sep 24 '13 at 07:48
  • Ah, OK, thanks – I think I'm pretty close now... however, after implementing your edited answer, I'm getting a TypeError: "can't convert Symbol into Integer" – cmw Sep 24 '13 at 13:40
  • You bet – thanks for taking the time to help me. I've added the application trace to my original post. Line 111 is query {... – cmw Sep 24 '13 at 13:50
  • Try to get the params hash before the Tire.search call or you can use the block method alternatively. Please see my edit for block method approach. Check these links https://github.com/karmi/tire/issues/197 and https://github.com/karmi/tire/issues/840 – Vamsi Krishna Sep 24 '13 at 14:04
  • That worked! I had to move the "sort" method out of "search.query" – and everything worked. Thanks a ton! – cmw Sep 24 '13 at 15:48
  • Yes, sorry for that. Glad that you figured that out. Edited my answer. – Vamsi Krishna Sep 25 '13 at 06:13