patternrubyrailsMinor
Searching over more models rails
Viewed 0 times
searchingmorerailsmodelsover
Problem
I'm trying to implement search over two models. In this example
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:
Here is the controller :
I use haml so here is my searchresult page :
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/
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}%")
endHere is the controller :
class HomeController < ApplicationController
def search
articles = Article.search(params[:q])
books = Book.search(params[:q])
@results = articles + books
end
endI 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/
- 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.
- Regarding
Solution
you want a separate model for search, i.e.
Finally in view you can render the actual results, i.e.
(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.
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
endFinally 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.