2016-04-15 66 views
1

所以我想提高搜索功能,我的应用程序具体的搜索实现

我的模型关系/协会是像这样(很多>一,一等于一):

  • 客户<项目<活动=分配=用户
  • 分配<任务

任务表只有一个外键分配。

搜索PARAMS是这个样子:

params[:search]==User: 'user_handle', Client: 'client_name', Project: 'project_name', Activity: 'activity_name' 

所以我需要porbably搜索Clients.where()任务,Projects.where()的任务等等。 然后我需要以某种方式连接这些查询并摆脱所有重复的结果。如何在实践中做到这一点,我不知道。

我一直在用这种方式撞击砖墙,而互联网搜索并没有真正帮助...所以任何帮助都大大降低了。它可能是一个简单的解决方案太...

我在轨道上4.2.5源码dev的PG生产

+0

嘿,我不明白你是否指'继承'或'协会'由'<'操作符..请。我认为重写会很好 – illusionist

+0

编辑,我的意思是关联(认为它们被称为rails中的关系),例如: 项目具有外键client_id 项目模型具有belongs_to关联 客户端模型具有has_many关联 –

回答

1

有几件事情我会根据您自己的回答中的代码更改/推荐:

  1. 移动搜索查询到示波器上的每个模型类
  2. 身高AREL通过原始SQL编写查询(here's a quick guide
  3. 加强轨查询模型

的变化我时使用某种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中讨论了许多其他选项。

如果你不想包含随机代码和宝石,另一个选择是将一个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) 
+0

感谢man,这真的有帮助! –

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 : @[email protected]&query 
end 
if !search_params[:client].empty? 
    query = add_res(Client.where('name LIKE ?', "%#{search_params[:client]}%")) 
    @tasks.nil? ? @tasks=query : @[email protected]&query 
end 
if !search_params[:project].empty? 
    query = add_res(Project.where('name LIKE ?', "%#{search_params[:project]}%")) 
    @tasks.nil? ? @tasks=query : @[email protected]&query 
end 
if !search_params[:activity].empty? 
    query = add_res(Activity.where('name LIKE ?', "%#{search_params[:activity]}%")) 
    @tasks.nil? ? @tasks=query : @[email protected]&query 
end 
if @tasks.nil? 
    @tasks=Task.all 
end 
@[email protected] 

如果有人能提供一个更好的答案,我将永远感激