HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyrailsMinor

Searching over more models rails

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
searchingmorerailsmodelsover

Problem

I'm trying to implement search over two models. In this example Book and Article. Here are the attributes from both of these :

Article(id: integer, magazine: string, title: string, body: text, created_at: datetime, updated_at: datetime)
Book(id: integer, publisher: string, author: string, title: string, foreword: text, body: text, created_at: datetime, updated_at: datetime)


I'm trying to search over multiple attributes on both of these Models using the same search string and combining results. Because my next goal is to have a permlink to results.

Here is how I implemented search on these Models:

def self.search(q)
  Article.where('magazine like :value OR title like :value OR body like :value', value: "%#{q}%")
 end

def self.search(q)
  Book.where('publisher like :value OR author like :value OR title like :value OR foreword like :value OR body like :value', value: "%#{q}%")
end


Here is the controller :

class HomeController < ApplicationController 
    def search
      articles = Article.search(params[:q])
      books = Book.search(params[:q])

      @results = articles + books       
    end
end


I use haml so here is my searchresult page :

%div.main
  %div.pull.left
    =link_to 'Back to search', '/',:class => 'btn btn-primary btn-lg'
  %div.clearfix
  %table.table.table-condensed
    %thead
      %tr
        %th Id
        %th Title
        %th Type
    %tbody
      - @results.each do |item|
        %tr
          %td #{item.id}
          %td #{item.title}
          %td #{item.class.name}


Is there a more elegant way to actually do the search? I don't have that many experience with rails but I was reading trough this article :

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

  1. And I was thinking I could do this as 2. Extract Service Objects, but as two search methods search over different attributes I wasn't able to do this? I'd be glad to hear your take on this.



  1. Regarding

Solution

you want a separate model for search, i.e.

class SearchResult < AR::Base
  #columns: content, title, user
  belongs_to :searchable, polymorphic: true
end

class Book < AR::Base
  has_one :searchable, polymorphic: true, class_name: 'SearchResult'
  belongs_to :user

  after_create :autobuild_searchable
  after_commit :update_search

  private

  def autocreate_searchable
    self.create_searchable
  end

  def update_search_content
    searchable.update_attributes(content: "#{title} #{author}", title: title, user: user.username)
  end
end

class SearchController
  def search
    @result = SearchResult.where("content like ?", "%#{params[:q]}%")
    if params[:user]
      @result = @result.where(user: params[:user])
    end
  end
end


Finally in view you can render the actual results, i.e.

= render @result.map(&:searchable)


(rails will render correct partial for each element, whatever it's book, magazine or anything, it will look for views/magazines/_magazine, views/books/_book)

You can extend the search with extra columns to use them as additional filters (here i've added user) and you don't pollute your models with extra search logic.
How will you handle the searchable content is up to you (here is just a primitive example). You can split it into separate columns (ie. search_field1, search_field2, search_field3), you can tokenize the input or build a fulltext index.

you can store a permalink in SearchResult or you can generate in on the fly. You may have some issues with rails path helpers on polymorphic associations, but those are easy to overcome.

If you have postgres as database i advise you to take a look on pg_search, it's simple and brilliant solution: https://github.com/Casecommons/pg_search and should give you better understanding on this topic.

The above code may not work, I didn't check it against Ruby/Rails, just trying to show an idea.

Code Snippets

class SearchResult < AR::Base
  #columns: content, title, user
  belongs_to :searchable, polymorphic: true
end

class Book < AR::Base
  has_one :searchable, polymorphic: true, class_name: 'SearchResult'
  belongs_to :user

  after_create :autobuild_searchable
  after_commit :update_search


  private

  def autocreate_searchable
    self.create_searchable
  end

  def update_search_content
    searchable.update_attributes(content: "#{title} #{author}", title: title, user: user.username)
  end
end

class SearchController
  def search
    @result = SearchResult.where("content like ?", "%#{params[:q]}%")
    if params[:user]
      @result = @result.where(user: params[:user])
    end
  end
end
= render @result.map(&:searchable)

Context

StackExchange Code Review Q#35878, answer score: 2

Revisions (0)

No revisions yet.