具体搜索实施

时间:2016-04-15 00:06:36

标签: ruby-on-rails ruby ruby-on-rails-4

因此,我尝试改进应用的搜索功能

我的模型关系/关联是这样的(许多>一,一=一):

  • 客户<项目< Activities = Assignments = Users
  • 作业<任务

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

搜索参数看起来像这样:

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

2 个答案:

答案 0 :(得分:1)

我会根据您自己的答案中的代码更改/推荐一些内容:

  1. 将搜索查询移至每个模型类的范围
  2. 在撰写查询(here's a quick guide
  3. 时,首选AREL over raw SQL
  4. 在查询模型时增强rails以使用某种or
  5. 我建议的更改将使您能够执行以下操作:

    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

如果有人可以提供更好的答案,我将永远是伟大的