1. ホーム
  2. ruby-on-rails

[解決済み] 姓と名で検索してみる

2022-02-17 15:40:59

質問

railsアプリでユーザーを姓と名の両方で検索しようとしているのですが、現在試している各方法で結果がまちまちです。これらのメソッドのいずれかを書き換えて、希望する結果を得る方法はありますか?

user_controller.rb

メソッド #1

def self.search(query)
  where("first_name LIKE ? OR last_name LIKE ?", "%#{query}%", "%#{query}%") 
end

これは姓か名のどちらかで動作しますが、両方は動作しません。

方法その2

def self.search(keywords)
  if keywords
    where(:all, :conditions => ["concat(first_name," ",last_name) like?", "%#{keywords}%"])
  end
end

これは結果を返しません

メソッド#3

def self.search(search)
  if search
    select('(first_name || " " || last_name) as \'ful_name\', *')
    where ['first_name LIKE :s OR last_name LIKE :s OR ful_name LIKE :s', :s => "%#{search}"]
  else
  scoped
 end
end

これはエラーを返します

SQLite3::SQLException: no such column: ful_name: SELECT "users".* FROM "users" WHERE (first_name LIKE '%Spider Man' OR last_name LIKE '%Spider Man' OR ful_name LIKE '%Spider Man') ORDER BY created_at DESC

app/views/users/index.html.erb:5:in `_app_views_users_index_html_erb__848623016_40254132'

index.html.erb

<% provide(:title, 'Search') %>
<h1>Search</h1>

<ul class="span4 users">
  <%= render @users %>
</ul>

_user.html.erb

<li>
  <%= image_tag user.avatar(:medium) %>
  <h4><%= link_to user.full_name, feed_user_path(user), :class => "follow-color" %></h4>
  <% if current_user.admin? && !current_user?(user) %>
    | <%= link_to "delete", user, method: :delete,
                                  data: { confirm: "You sure?" } %>
  <% end %>
</li>

_header.html.erb

<%= form_tag users_path, method: "get", class: "search-bar" do %>
  <%= text_field_tag :search, params[:search], placeholder: "Search" %>
<% end %>

解決方法は?

これです。

:conditions => ["concat(first_name," ",last_name) like?", "%#{keywords}%"]

が動作しないのは、(陰湿な)引用符の問題があるからです。Rubyでは、こうです。

"a" "b"

と同じです。

"ab"

だからあなたの :conditions は本当はこうです。

:conditions => ["concat(first_name,,last_name) like?", "%#{keywords}%"]

ということですね。

:conditions => ["concat(first_name, ' ', last_name) like ?", "%#{keywords}%"]

SQLの文字列リテラルは、二重引用符ではなく一重引用符を使用します。また、標準SQLをサポートすると謳っているデータベースを使用している場合、SQLの文字列リテラルを記述する際に || 演算子で文字列の連結を行います。

:conditions => ["first_name || ' ' || last_name like ?", "%#{keywords}%"]

SELECT句で定義されたエイリアスは一般にWHERE句では使用できないため、3番目のものは動作しません。したがって、 "unknown column" というエラーが発生します。の結果も捨ててしまうことになります。 select の呼び出しが足りないので . ここでも

select('(first_name || " " || last_name) as \'ful_name\', *')
where ['first_name LIKE :s OR last_name LIKE :s OR ful_name LIKE :s', :s => "%#{search}"]

SQLでは文字列リテラルはシングルクォートを使い、ダブルクォートは識別子のために使われます。とだけ言いたいのでしょう。

where("first_name like :s or last_name like :s or first_name || ' ' || last_name like :s", :s => "%#{search}")

または単に

where("first_name || ' ' || last_name like :s", :s => "%#{search}")

2つほど注意点があります。

  1. 文字列の連結演算子は、データベース固有のものです。標準的なSQLでは || を使用したいが、設定によってはMySQLが concat 関数を使用します。AFAIKは、SQLiteが多くのMySQLイズムをサポートしていますが、それらを使用する際には注意が必要で、できるだけ標準に忠実であるべきです。
  2. クウォートは、これもまたデータベース固有のものです。標準SQLでは、文字列リテラルには一重引用符を、識別子(テーブル名やカラム名など)には二重引用符を使用します。MySQLは識別子にバックチックを使用し、SQLiteは(AFAIK)識別子にダブルクォートまたはバックチック、文字列にシングルクォートまたはダブルクォートを使用することができます。繰り返しになりますが、良い習慣を身につけるために、できるだけ標準に忠実でありたいものです。