问题
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