因此,我尝试改进应用的搜索功能
我的模型关系/关联是这样的(许多>一,一=一):
任务表只有一个外键来分配。
搜索参数看起来像这样:
params[:search]==User: 'user_handle', Client: 'client_name', Project: 'project_name', Activity: 'activity_name'
所以我需要可以搜索Clients.where()。tasks,Projects.where()。tasks等等。 然后我需要以某种方式连接这些查询并摆脱所有重复的结果。然而,在实践中如何做到这一点,我不知道。
我一直在用砖头挡住我的头,互联网搜索并没有真正帮助......所以任何帮助都是非常令人沮丧的。它也可能是一个简单的解决方案......
我在rails 4.2.5 sqlite for dev pg for production
答案 0 :(得分:1)
我会根据您自己的答案中的代码更改/推荐一些内容:
or
我建议的更改将使您能够执行以下操作:
search = search_params
tasks = Tasks.all
tasks = tasks.or.user_handle_matches(handle) if (handle = search[:user].presence)
tasks = tasks.or.client_name_matches(name) if (name = search[:client].presence)
tasks = tasks.or.project_name_matches(name) if (name = search[:project].presence)
tasks = tasks.or.activity_name_matches(name) if (name = search[:activity].presence)
@tasks = tasks.uniq
首先,将每个查询转换为模型上的范围。这使您可以在以后重用您的范围:
class User
scope :handle_matches, ->(handle) {
where(arel_table[:handle].matches("%#{handle}%"))
}
end
class Client
scope :name_matches, ->(name) {
where(arel_table[:name].matches("%#{name}%"))
}
end
class Project
scope :name_matches, ->(name) {
where(arel_table[:name].matches("%#{name}%"))
}
end
class Activity
scope :name_matches, ->(name) {
where(arel_table[:name].matches("%#{name}%"))
}
end
然后,您可以在Task
模型上使用这些范围,以获得更好的搜索功能。对于Task
上的每个范围,我们正在对关系进行连接(内连接)并使用范围来限制连接的结果:
class Task
belongs_to :assignment
has_one :user, :through => :assignment
has_one :activity, :through => :assignment
has_one :project, :through => :activity
scope :user_handle_matches, ->(handle) {
joins(:user).merge( User.handle_matches(handle) )
}
scope :client_name_matches, ->(name) {
joins(:client).merge( Client.name_matches(name) )
}
scope :activity_name_matches, ->(name) {
joins(:activity).merge( Activity.name_matches(name) )
}
scope :project_name_matches, ->(name) {
joins(:project).merge( Project.name_matches(name) )
}
end
要解决的最后一个问题是or
结果。 Rails 4及以下版本并不能真正实现开箱即用,但有一些宝石和代码可以实现这一功能。
我经常在this GitHub gist中将代码包含在初始值设定项中,以允许or
范围。该代码允许您执行Person.where(name: 'John').or.where(name: 'Jane')
。
本SO question中讨论了许多其他选项。
如果您不想包含随机代码和gem,另一种选择是将一组id传递给where子句。这会生成类似于SELECT * FROM tasks WHERE id IN (1, 4, 5, ...)
的查询:
tasks = []
tasks << Tasks.user_handle_matches(handle) if (handle = search[:user].presence)
tasks << tasks.or.client_name_matches(name) if (name = search[:client].presence)
tasks << tasks.or.project_name_matches(name) if (name = search[:project].presence)
tasks << tasks.or.activity_name_matches(name) if (name = search[:activity].presence)
# get the matching id's for each query defined above
# this is the catch, each call to `pluck` is another hit of the db
task_ids = tasks.collect {|query| query.pluck(:id) }
tasks_ids.uniq!
@tasks = Tasks.where(id: tasks_ids)
答案 1 :(得分:0)
所以我解决了它,但是它太晚了。
首先我写了一个方法
def add_res(ar_obj)
ar_obj.each do |o|
res += o.tasks
end
return res
end
然后我写了我的搜索逻辑
if !search_params[:user].empty?
query = add_res(User.where('handle LIKE ?', "%#{search_params[:user]}%"))
@tasks.nil? ? @tasks=query : @tasks=@tasks&query
end
if !search_params[:client].empty?
query = add_res(Client.where('name LIKE ?', "%#{search_params[:client]}%"))
@tasks.nil? ? @tasks=query : @tasks=@tasks&query
end
if !search_params[:project].empty?
query = add_res(Project.where('name LIKE ?', "%#{search_params[:project]}%"))
@tasks.nil? ? @tasks=query : @tasks=@tasks&query
end
if !search_params[:activity].empty?
query = add_res(Activity.where('name LIKE ?', "%#{search_params[:activity]}%"))
@tasks.nil? ? @tasks=query : @tasks=@tasks&query
end
if @tasks.nil?
@tasks=Task.all
end
@tasks=@tasks.uniq
如果有人可以提供更好的答案,我将永远是伟大的