【问题标题】:How to do a LIKE query in Arel and Rails?如何在 Arel 和 Rails 中进行 LIKE 查询?
【发布时间】:2011-05-24 18:05:56
【问题描述】:

我想做这样的事情:

SELECT * FROM USER WHERE NAME LIKE '%Smith%';

我在 Arel 的尝试:

# params[:query] = 'Smith'
User.where("name like '%?%'", params[:query]).to_sql

然而,这变成了:

SELECT * FROM USER WHERE NAME LIKE '%'Smith'%';

Arel 正确地包装了查询字符串 'Smith',但是因为这是一个 LIKE 语句,所以它不起作用。

如何在 Arel 中进行 LIKE 查询?

附:奖励——我实际上是在尝试扫描表上的两个字段,包括名称和描述,以查看查询是否有任何匹配项。这将如何运作?

【问题讨论】:

  • 我更新了奖励的 arel 答案。

标签: ruby-on-rails activerecord arel


【解决方案1】:

试试

User.where("name like ?", "%#{params[:query]}%").to_sql

PS。

q = "%#{params[:query]}%"
User.where("name like ? or description like ?", q, q).to_sql

已经很久了,但是@cgg5207 添加了一个修改(如果您要搜索长名称或多个长名称参数,或者您懒得输入,这将非常有用)

q = "%#{params[:query]}%"
User.where("name like :q or description like :q", :q => q).to_sql

或

User.where("name like :q or description like :q", :q => "%#{params[:query]}%").to_sql

【讨论】:

  • Rails 怎么知道不要在替换字符串中转义 %?似乎如果您只想要一个单面通配符,则没有什么可以阻止用户提交一个在两端包含 % 的查询值(我知道在实践中,Rails 会阻止 % 出现在查询字符串中,但似乎应该在 ActiveRecord 级别对此进行保护)。
  • 这不是容易受到 SQL 注入攻击吗?
  • @Behrang no 8) User.where("name like %#{params[:query]}% or description like%#{params[:query]}%").to_sql 将易受攻击,但是,在我展示的格式中,Rails 转义了 params[:query]
  • 抱歉跑题了。我有方法to_sql或arel manager的sql git,如何在db上执行sql?
  • Model.where(to_sql_result)
【解决方案2】:

这是您在 arel 中执行类似查询的方式:

users = User.arel_table
User.where(users[:name].matches("%#{user_name}%"))

PS:

users = User.arel_table
query_string = "%#{params[query]}%"
param_matches_string =  ->(param){ 
  users[param].matches(query_string) 
} 
User.where(param_matches_string.(:name)\
                       .or(param_matches_string.(:description)))

【讨论】:

  • 与使用where("name like ?", ...) 不同,这种方法在不同数据库之间更便携。例如,这将导致在针对 Postgres 数据库的查询中使用 ILIKE。
  • 这是否可以防止 SQL 注入?
  • 这并不能完全防止 SQL 注入。尝试将 user_name 设置为“%”。查询将返回匹配项
  • 我尝试直接使用参数进行 sql 注入,User.where(users[:name].matches("%#{params[:user_name]}%")),我尝试了TRUNCATE users; 和其他此类查询,但 sql 端没有任何反应。对我来说看起来很安全。
  • 使用.gsub(/[%_]/, '\\\\\0')转义MySql通配符。
【解决方案3】:

Reuben Mallaby 的回答可以进一步缩短以使用参数绑定:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql

【讨论】:

    【解决方案4】:

    不要忘记转义用户输入。 你可以使用ActiveRecord::Base.sanitize_sql_like(w)

    query = "%#{ActiveRecord::Base.sanitize_sql_like(params[:query])}%"
    matcher = User.arel_table[:name].matches(query)
    User.where(matcher)
    

    您可以在models/user.rb中简化

    def self.name_like(word)
      where(arel_table[:name].matches("%#{sanitize_sql_like(word)}%"))
    end
    

    【讨论】:

      猜你喜欢
      • 2011-11-23
      • 2015-09-29
      • 2011-07-01
      • 1970-01-01
      • 2011-07-28
      • 1970-01-01
      • 1970-01-01
      • 2012-08-11
      相关资源
      最近更新 更多