如何在 Arel 和 Rails 中进行 LIKE 查询?

Posted

技术标签:

【中文标题】如何在 Arel 和 Rails 中进行 LIKE 查询?【英文标题】:How to do a LIKE query in Arel and Rails? 【发布时间】: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 答案。 【参考方案1】:

不要忘记转义用户输入。 你可以使用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

【讨论】:

【参考方案2】:

试试

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)【参考方案3】:

这是您在 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通配符。【参考方案4】:

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

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

【讨论】:

以上是关于如何在 Arel 和 Rails 中进行 LIKE 查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用ARel对子查询进行连接?

Arel、联接和 Rails 查询

如何解决 Arel 与 Hartl Rails 教程(第 2 章)不兼容的问题?

Rails/Arel:选择所有记录作为 ActiveRecord::Relation

如何使用 Arel(大概)创建一个不影响 Rails 3 中的查询的 ActiveRecord 范围?

Rails:将复杂的 SQL 查询转换为 Arel 或 ActiveRecord