简体   繁体   中英

Rails 4 returning records from multiple tables matching a search term

I'm attempting to return records from two database tables (pages and articles) that match each of the words in a search term. Here's my query:

if params.has_key?('search_term')
  words = params['search_term'].split(' ')
  query = "
    select
      p.id
      , p.title
      , p.slug
      , p.created_at
      , p.content
      , 'page' as type
    from pages p
    where 1 = 1
  "
  words.each do |word|
    query << " and p.title like '%" << word << "%' or p.content like '%" << word << "%'"
  end
  query << "
    union all
    select
      a.id
      , a.title
      , a.slug
      , a.created_at
      , a.content
      , 'article' as type
    from articles a
    where 1 = 1
  "
  words.each do |word|
    query << " and a.title like '%" << word << "%' or a.content like '%" << word << "%'"
  end
  @search_results = ActiveRecord::Base.connection.execute(query)
end

Firstly, how can I sanitise the value of word inside the loops to prevent a SQL injection attack? I'm wondering if it's necessary to sanitise word further because I've already split the search term string.

Secondly, would there be a better way to approach what I'm trying to do? I'm relatively new to Ruby/Rails so would welcome any pointers.

Thanks in advance for your help.

You should the built in querying tools as much as possible. So assuming you have a Page class and and Article class you can use select and where

matched_pages = Page.select(:title, :slug, :created_at, :content)
matched_articles = Articles.select(:title, :slug, :created_at, :content)
words.each do |word|
  matched_pages = matched_pages.where(["title LIKE :word OR content LIKE :word", {word: "%#{word}%"}])
  matched_articles = matched_articles.where(["title LIKE :word OR content LIKE :word", {word: "%#{word}%"}])
end

If you need them merged together you could concatenate the two results something like

@search_results = matched_pages + matched_articles

Then if you want it in a view:

<% @search_results.each do |item| %>
  <tr>
    <td><%= item.title %></td>
    <td><%= item.slug %></td>
    <td><%= item.created_at %></td>
    <td><%= item.content %></td>
    <td><%= item.class.name.downcase %></td>
  </tr>
<% end %>

Of course there are many other things you may want to do with it and there are other (usually better) ways to display the data in a view than the example, but that should help you get some idea what the data would look like.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM