【问题标题】:Dynamically create query - Rails 5动态创建查询 - Rails 5
【发布时间】:2018-03-17 18:04:01
【问题描述】:

如果我手动编写查询,它会像

User.where("name LIKE(?) OR desc LIKE(?)",'abc','abc')
    .where("name LIKE(?) OR desc LIKE(?)",'123','123')

但是,我需要动态生成该查询。 我正在获取类似的数据

def generate_query(the_query)

   query,keywords = the_query

   # Here 
   # query = "name LIKE(?) OR desc LIKE(?)"
   # keywords = [['abc','abc'],['123','123']]

   keywords.each do |keyword|
       users = User.where(query,*keyword) <-- not sure how to dynamically add more 'where' conditions.    
    end

end

我正在使用 Rails 5。希望很清楚。任何帮助表示赞赏:)

【问题讨论】:

    标签: ruby-on-rails activerecord rails-activerecord


    【解决方案1】:

    类似这样的:

    q = User.where(a)
            .where(b)
            .where(c)
    

    相当于:

    q = User
    q = q.where(a)
    q = q.where(b)
    q = q.where(c)
    

    所以你可以写:

    users = User
    keywords.each do |keyword|
      users = users.where(query, *keyword)
    end
    

    但任何时候你看到这种反馈模式(即对操作的结果或f(f( ... f(x)))应用一个操作)你应该开始考虑Enumerable#inject(又名Enumerable#reduce):

    users = keywords.inject(User) { |users, k| users.where(query, *k) }
    

    也就是说,您的 query 有两个占位符,但 keywords 只是一个平面数组,因此您将没有足够的值:

    users.where(query, *k)
    

    替换占位符。我认为您最好在此处使用命名占位符:

    query    = 'name like :k or desc like :k'
    keywords = %w[abc 123]
    users    = keywords.inject(User) { |users, k| users.where(query, k: k) }
    

    您可能还想为您的LIKE 添加一些模式匹配,所以:

    query = "name like '%' || :k || '%' or desc like '%' || :k || '%'"
    users = keywords.inject(User) { |users, k| users.where(query, k: k) 
    

    其中|| 是标准SQL 字符串连接运算符(AFAIK 并非所有数据库都能理解),而LIKE 模式中的% 匹配任何字符序列。或者您可以在 Ruby 中添加模式匹配,而不必担心数据库处理字符串连接的不同方式:

    query = 'name like :k or desc like :k'
    users = keywords.inject(User) { |users, k| users.where(query, k: "%#{k}%")
    

    此外,这个:

    User.where("name LIKE(?) OR desc LIKE(?)",'abc','abc')
        .where("name LIKE(?) OR desc LIKE(?)",'123','123')
    

    产生一个WHERE 子句,如:

    where (name like 'abc' or desc like 'abc')
      and (name like '123' or desc like '123')
    

    所以您匹配的是所有关键字,而不是任何关键字。这可能是您的意图,也可能不是。

    【讨论】:

    • 感谢您的回复。我的数组示例是错误的。我修好了。
    • 好的,如果keywords 是一个数组,那么where(query, *k) 就可以了。我仍然倾向于关键字占位符以避免重复。您还想使用*k.map { |k| "%#{k}%" } 添加模式匹配字符。
    猜你喜欢
    • 1970-01-01
    • 2017-04-20
    • 1970-01-01
    • 2012-12-12
    • 2015-10-01
    • 2015-04-09
    • 2011-12-02
    • 2011-02-18
    • 2020-03-15
    相关资源
    最近更新 更多