How do i construct a dynamic scope search given a variable-length array of elements to exclude, as in:
class Participant < ApplicationRecord
scope exclude_names, -> (['%name1%', '%name2%', '%name3%', ...]) {
where.not(Participant.arel_table[:name_search].matches('%name1%').or(
Participant.arel_table[:name_search].matches('%name2%').or(
Participant.arel_table[:name_search].matches('%name3%').or(
...
}
but done dynamically as the name_list is of variable length.
I suggest to use does_not_match
method and accumulate AND
conditions iterating through excluded names. You also don't need call explicitly class name inside the scope, because it is class method
class Participant < ApplicationRecord
scope :exclude_names, ->(*names_to_exclude) {
query = names_to_exclude.reduce(nil) do |q, name|
condition = arel_table[:name_search].does_not_match("%#{name}%")
q&.and(condition) || condition
end
where(query)
}
end
After that you can call this scope
Participant.exclude_names('name1')
# SELECT * FROM participants
# WHERE name_search NOT LIKE '%name1%'
Participant.exclude_names('name1', 'name2')
# SELECT * FROM participants
# WHERE name_search NOT LIKE '%name1%'
# AND name_search NOT LIKE '%name2%'
Participant.exclude_names(%w[name1 name2])
# SELECT * FROM participants
# WHERE name_search NOT LIKE '%name1%'
# AND name_search NOT LIKE '%name2%'
Of course you can use OR
like in your question, in this case it will be like this
class Participant < ApplicationRecord
scope :exclude_names, ->(*names_to_exclude) {
query = names_to_exclude.reduce(nil) do |q, name|
condition = arel_table[:name_search].matches("%#{name}%")
q&.or(condition) || condition
end
where.not(query)
}
end
After that you can call this scope, compare with previous queries
Participant.exclude_names('name1')
# SELECT * FROM participants
# WHERE NOT (name_search LIKE '%name1%')
Participant.exclude_names('name1', 'name2')
# SELECT * FROM participants
# WHERE NOT (name_search LIKE '%name1%' OR name_search LIKE '%name2%')
Participant.exclude_names(%w[name1 name2])
# SELECT * FROM participants
# WHERE NOT (name_search LIKE '%name1%' OR name_search LIKE '%name2%')