Dynamically create query - Rails 5

送分小仙女□ 提交于 2019-12-19 12:01:52

问题


If I manually write a query, it will be like

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

However, I need to dynamically generate that query. I am getting data like

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

I am using Rails 5. Hope it is clear. Any help appreciated :)


回答1:


Something like this:

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

is equivalent to:

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

So you could write:

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

But any time you see that sort of feedback pattern (i.e. apply an operation to the operation's result or f(f( ... f(x)))) you should start thinking about Enumerable#inject (AKA Enumerable#reduce):

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

That said, your query has two placeholders but keywords is just a flat array so you won't have enough values in:

users.where(query, *k)

to replace the placeholders. I think you'd be better off using a named placeholder here:

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

You'd probably also want to include some pattern matching for your LIKE so:

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

where || is the standard SQL string concatenation operator (which AFAIK not all databases understand) and % in a LIKE pattern matches any sequence of characters. Or you could add the pattern matching in Ruby and avoid having to worry about the different ways that databases handle string concatenation:

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

Furthermore, this:

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

produces a WHERE clause like:

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

so you're matching all the keywords, not any of them. This may or may not be your intent.



来源:https://stackoverflow.com/questions/49340190/dynamically-create-query-rails-5

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!