Динамически создавать запрос - Rails 5

Если я вручную напишу запрос, это будет как

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. Надеюсь, это понятно. Любая помощь приветствуется:)

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 (AKA 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')

Таким образом, вы соответствуете всем ключевым словам, а не любому из них. Это может или не может быть вашим намерением.

Другие вопросы по тегам