1

I'm trying to figure out how to use the LIKE statement to achieve the following in Postgres:

Data: Test Project

The following search queries should return Test Project: test est est project pro

Currently my method in the my projects.rb model looks like this:

self.where('name LIKE ?', "%#{search}%")

(I'll be adding downcase of course) It currently fetches any variation of the first word of the :name attribute value but no the second, so searching pro would return nothing.

projects.rb model

  def self.search(search)
    if search
      scope :find_name, lambda { |search| where("name ILIKE :search", search: "%#{search.downcase}%") }
    else
      self.where(nil)
    end
  end


projects_controller.rb

def index                                
  @projects = Project.search(params[:search]
end  

index.html.erb
<p id="notice"><%= notice %></p>
<div class="projects-index">
  <div class="container-body text-center">
    <h1>Projects</h1>
    <%= form_tag projects_path, method: :get, id: "projects_search" do %>
      <p>
        <%= text_field_tag :search %>
      </p>
    <% end %>
  </div>
  <!-- CARDS -->
  <div class="row cards-section">
    <div id="projects">
      <%= render partial: 'projects' %>
    </div>
    <div class="col-3">
      <%= link_to new_project_path, class: "new-project text-center" do %>
        <div class="align vertical">
          <div class="align-flex center">
            <%= image_tag "add_project.png" %>
          </div>
          <p>New Project</p>
        </div>
      <% end %>
    </div>
  </div>
</div>

_projects.html.erb partial

<% @projects.each do |project| %>
  <div class="col-3">
    <div class="existing-card">
      <div class="top">
        <div class="ui icon left pointing dropdown button">
          <i class="ellipsis vertical icon"></i>
          <div class="menu">
            <div class="item">
              <%= link_to 'Edit', edit_project_path(project), class: "link" %>
            </div>
            <div class="item">
              <%= link_to 'Share', new_user_invitation_path, class: "link" %>
            </div>
            <div class="item">
              <%= link_to 'Delete', project_path(project), method: :delete, data: { confirm: 'Are you sure you want to delete this project?' }, class: "link" %>
            </div>
          </div>
        </div>
      </div>
      <div class="card-container align-flex center">
        <p><%= project.name %></p>
      </div>
    </div>
  </div>
<% end %>
4
  • 1
    According to guides.rubyonrails.org/active_record_querying.html this is an unsafe approach that can lead to SQL injection. @Sachin_R approach seems safer. Commented Aug 24, 2017 at 6:15
  • Thanks for bringing that up, which part exactly makes it vulnerable to SQL injections? (So I can avoid them in the future) Commented Aug 24, 2017 at 6:21
  • 1
    Putting the variable directly into the conditions string will pass the variable to the database as-is. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out they can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string Commented Aug 24, 2017 at 6:41
  • 1
    For more details you can also read guides.rubyonrails.org/security.html#sql-injection Commented Aug 24, 2017 at 6:43

2 Answers 2

2

with your query you just need to change LIKE to ILIKE

self.where('name ILIKE ?', "%#{search.downcase}%")

for more save I would like to suggest using scope below

scope :find_name, lambda { |search| where("name ILIKE :search", search: "%#{search.downcase}%") }

edited for your case

do not use scope inside method, for more about scope please see link and for your case you can change your index as follow

def index                                
  @projects = Project.find_name(params[:search]
end 

and change this

  def self.search(search)
    if search
      scope :find_name, lambda { |search| where("name ILIKE :search", search: "%#{search.downcase}%") }
    else
      self.where(nil)
    end
  end

to this

scope :find_name, lambda { |search| where("name ILIKE :search", search: "%#{search.downcase}%") }
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you, that worked beautifully, just to make sure now that @Stephane_Paquet brought it up, is this snippet safe from SQL injections?
Just noticed that threw an error: ActionView::Template::Error (undefined method 'each' for :find_name:Symbol): web_1 | 1: <% @projects.each do |project| %>
can you upload your controller and view, I'll try to help
Edited my answer, inside your project.rb please put your scope out from method see my edited answer (bottom section)
|
1

try

self.where('name ILIKE %?', "%#{search}%")

2 Comments

ILIKE will make the search case insensitive.
I tried this approach and I got this error: ActionView::Template::Error (PG::SyntaxError: ERROR: syntax error at or near "%" web_1 | LINE 1: SELECT "projects".* FROM "projects" WHERE (name ILIKE %''%) web_1 | ^ web_1 | : SELECT "projects".* FROM "projects" WHERE (name ILIKE %''%)):

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.