搜索多个字段 rails 5

Posted

技术标签:

【中文标题】搜索多个字段 rails 5【英文标题】:Search multiple fields rails 5 【发布时间】:2021-07-13 17:52:27 【问题描述】:

我有以下表格:

<div class='panel' id='panel-advanced-search'>
  <%= form_tag contacts_path, method: :get do %>
    <div>
      <div class='advanced_search'>
        <div>
          <%= label_tag "Contact Type" %>
          <%= select_tag(:contact_type, options_for_select(Contact::TYPES, selected: params[:contact_type]), prompt: 'Any', class: 'customSelect') %>
        </div>

        <div>
          <%= label_tag "Prospect Strength" %>
          <%= select_tag(:prospect_strength, options_for_select(Contact::PROSPECT_STRENGTHS, selected: params[:prospect_strength]), prompt: 'Any', class: 'customSelect') %>
        </div>

        <div>
          <%= label_tag "Customer" %>
          <%= collection_select(:customer_account_id, :customer_account_id, Customer.order(:name), :id, :name,  prompt: '' , class: 'customSelect select2') %>
        </div>

        <div>
          <%= label_tag "Supplier" %>
          <%= collection_select(:supplier_account_id, :supplier_account_id, Supplier.order(:name), :id, :name,  prompt: '' , class: 'customSelect select2') %>
        </div>

        <div>
          <%= label_tag "Company Name" %>
          <%= text_field_tag :company_name %>
        </div>

        <div>
          <%= label_tag "Name" %>
          <%= text_field_tag :name %>
        </div>

        <div>
          <%= label_tag "Job Title" %>
          <%= text_field_tag :title %>
        </div>

        <div>
          <%= label_tag "Postcode" %>
          <%= text_field_tag :postcode %>
        </div>

        <div>
          <%= label_tag :created_at, 'Created From' %>

          <div class="date-picker">
            <%= text_field_tag :created_at, nil, class: 'date-picker-select' %>

            <span class="date-picker-btn">
              <span class="icon-calendar" aria-hidden="true"></span>
            </span>
          </div>
        </div>

        <div>
          <%= label_tag :updated_at, 'Created To' %>

          <div class="date-picker">
            <%= text_field_tag :updated_at, nil, class: 'date-picker-select' %>

            <span class="date-picker-btn">
              <span class="icon-calendar" aria-hidden="true"></span>
            </span>
          </div>
        </div>

        <div>
          <%= label_tag "Tags" %>
          <%= collection_select(:tag_list, :tag_list, @tags.order(:name), :name, :name, ,  class: 'select2', multiple: true ) %>
        </div>

        <div>
          <%= label_tag "Assignee" %>
          <%= collection_select(:assigned_to, :assigned_to, User.all, :id, :name,  prompt: 'Any' , class: 'customSelect select2') %>
        </div>

        <div>
          <%= label_tag "Obsolete?" %>
          <%= select_tag(:obsolete, options_for_select(['Obsolete', 'All'], selected: params[:obsolete]), prompt: 'Not obsolete?', class: 'customSelect') %>
        </div>

        <div>
          <%= label_tag "Send Results To" %>
          <%= select_tag(:subsequent_action, options_for_select([
            ['Report', 'report'],
            ['CSV Export', 'csv_export'],
            ['New Event', 'new_event']
          ]), prompt: 'None', class: 'customSelect') %>
        </div>
      </div>

      <div class="advanced_search_btns">
        <%= submit_tag submit_text %>
        <%= link_to secondary_btn, contacts_path, class: 'btn-medium' %>
      </div>
    </div>
  <% end %>
</div>

以及模型中的以下方法

def self.advanced_search
  Contact.where('
    contact_type LIKE :search OR
    prospect_strength LIKE :search OR
    customer_account_id LIKE :search OR
    supplier_account_id LIKE :search OR
    company_name LIKE :search OR
    name LIKE :search OR
    title LIKE :search OR
    postcode LIKE :search OR
    created_at LIKE :search OR
    updated_at LIKE :search OR
    tag_list LIKE :search OR
    assigned_to LIKE :search OR
    obsolete LIKE :search
  ', search: "%#search%"
  )
end

我该如何使用这种方法,以便用户可以使用这个带有多个参数的搜索表单?我已经在 index 方法中有以下基本搜索,所以我需要有两个搜索表单

def index
  @per_page = params[:per_page] || 20
  @tags = ActsAsTaggableOn::Tag.all

  if params[:search].present?
    @contacts = Contact.search(params[:qs], params[:search]).order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: @per_page)
  else
    @contacts = Contact.all.order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: @per_page)
  end
end

编辑将上面的表单更新为完整的表单,理想情况下,我希望表单功能完全在模型中。

编辑#2

这是基本搜索:

型号

QUICK_SEARCH_FIELDS = 
  name: 
    column_names: 'contacts.name'
  ,
  customers: 
    joins_table: :customer,
    column_names: 'contacts.name',
    filters:  contact_type: 'customer' 
  ,
  suppliers: 
    joins_table: :supplier,
    column_names: 'contacts.name',
    filters:  contact_type: 'supplier' 
  ,
  tags: 
    tagged_with: true
  
.with_indifferent_access

def self.search(field, query)
  field = QUICK_SEARCH_FIELDS[field]

  contact = all
  contact = contact.joins(field[:joins_table]) if field[:joins_table]
  contact = contact.where(field[:filters]) if field[:filters]
  contact = contact.where("#field[:column_names] LIKE ?", "%#query%") if field[:column_names]
  contact = contact.tagged_with(query) if field[:tagged_with]
  contact
end

表格

<div class='panel' id='panel-search'>
  <%= form_tag contacts_path, method: :get do %>
    <div class='l-inline-row-block'>
      <div class='l-inline-col width_120px'>
        <%= select_tag(:qs, options_for_select(Contact::QUICK_SEARCH_FIELDS.keys(), selected: params[:qs]), class: 'customSelect') %>
      </div>

      <div class='l-inline-col'>
        <%= search_field_tag :search, params[:search] %>
      </div>

      <div class='l-inline-col' style='width: 100px;'>
        <%= submit_tag submit_text %>
      </div>
    </div>
  <% end %>
</div>

【问题讨论】:

在高级搜索中:用户可以从多个字段中选择一个值进行搜索,但他也可以保持该字段为空例如:用户想要搜索contact_type 中的内容但不是在prospect_strength?字段之间的搜索词也可以不同吗? 是的,是的,是的,字段之间的搜索词可能不同,但如果用户想要搜索 contact_typeprospect_strength,他们应该能够以及他们的任何其他字段想要添加到搜索中。 我知道您需要简单的表单和高级搜索:但不能同时使用,对吗?还是他们?哪一个先例?如果有高级搜索,是否也使用普通搜索? 不是同时不,这取决于用户他们想在那里使用哪一个完全分开。明智的选项卡首先是基本搜索选项卡,因此这是用户首先看到的内容,但两者之间没有字段共享或任何内容。两个搜索同时在页面上,但完全不同的形式。 【参考方案1】:

您可能需要进行视图调整,并且不能只对所有字段使用 search 术语/查询:

1.确保视图中表单内的字段与联系人模型中需要搜索的列的名称相同 2.在每个表单中添加一个隐藏字段,以便您确定这是哪个搜索(还有另一种选择,但这个工作较少) 3.将用户只能选择一个字段的collection_select更改为select_tag,以免弄乱查询构建

每个填充的字段都会传递一个值 - 您可以构建一个哈希值,包括输入的列/表单字段及其值,例如 contact_type: "value1", prospect_strength: "value2"

之后,您需要使用该哈希构建查询并查询数据库

看起来像这样:

在你看来:

将此添加到高级搜索表单中

&lt;%= hidden_field_tag :search_type, :advanced %&gt;

然后在您的正常搜索中添加这个

&lt;%= hidden_field_tag :search_type, :normal %&gt;

在您的模型中:

def self.advanced_search(values_by_column_name) # values_by_column_name is a hash passed from the controller
    tag_list = values_by_column_name.delete("tag_list")
    sql_query = values_by_column_name.keys.map  |column| "#column LIKE :#column" 
    sql_query = sql_query.join(" OR ")
    values_by_column_name = values_by_column_name.transform_values  |value| "%#value%" .symbolize_keys

    relation = Contact.where(sql_query, values_by_column_name)
    relation = relation.tagged_with(tag_list) if tag_list
    relation
end

在您的控制器中:

ADVANCED_SEARCH_FIELDS = [
    :contact_type,
    :prospect_strength,
    :customer_account_id,
    :supplier_account_id,
    :company_name,
    :name,
    :title,
    :postcode,
    :created_at,
    :updated_at,
    tag_list: [],
    :assigned_to,
    :obsolete,
]

def index
    @per_page = params[:per_page] || 20
    @tags = ActsAsTaggableOn::Tag.all

    if advanced_search?
        @contacts = Contact.advanced_search(advanced_search_params)
    elsif normal_search?
        @contacts = Contact.search(params[:qs], params[:search])
    else
        @contacts = Contact.all
    end

    @contacts = @contacts.order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: @per_page)
end

def advanced_search?
    params[:search_type] == "advanced" && advanced_search_params.any?
end

def normal_search?
    params[:search_type] == "normal" && params[:search].present?
end

def advanced_search_params
    @_advanced_search_params ||= params.permit(ADVANCED_SEARCH_FIELDS).select  |_, v| v.present? .to_h
end

【讨论】:

是否可以将高级搜索字段移动到模型中,因为这是一个理想情况下应该属于模型的数据库查询?还有不同的字段类型不仅仅是选择字段,所以还有文本和选择字段 什么意思?现在高级搜索字段在哪里?联系人数据库中不是所有这些字段吗? 让我用整个表格更新问题,所有这些字段都在联系人表中是的。高级搜索不应该有任何默认值,它应该是空白的,所以我们不应该默认为contact_type 更新问题 我知道你能在这里评论一下:高级搜索的参数和普通搜索的参数?我想看看区别,是否有任何特殊键可以帮助确定区别

以上是关于搜索多个字段 rails 5的主要内容,如果未能解决你的问题,请参考以下文章

Rails Ransack gem:使用一个条件搜索多个值

Rails 5搜索构面过滤器

Ransack,搜索多列,一个字段,rails 3

搜索多个列 - Rails

Rails 跨多个模型搜索

Rails ActiveRecord - 搜索多个属性