How To Create A Search Function - Ruby On Rails

2019-09-11 02:28发布

Im trying to make a search function for an app. I have a service object called searches and all search logic is in this object.

When the search form is submitted with an about query @results in Search Controller is the following array.

=> [
    [0] [],
    [1] [
        [0] About {
                          :id => 2,
                       :title => "About",
                        :slug => "about",
            :meta_description => "",
               :meta_keywords => "",
                     :content => "<p>Lorem ipsum about</p>",
                    :position => 1,
                   :published => true,
                     :on_menu => true,
                  :created_at => Wed, 13 Jan 2016 00:44:08 UTC +00:00,
                  :updated_at => Fri, 15 Jan 2016 04:05:52 UTC +00:00
        }
    ],
    [2] [],
    [3] []
]

Here is my Search object. See how the User attributes are different then the Pages and NewsArticle? Good. Now go look at the view.

class SearchService
  attr_accessor :query

  def initialize(query)
    @query = query
  end

  # searchable_fields_by_model
  CONDITIONS_BY_MODEL = {
    Page => [:title, :content],
    NewsArticle => [:title, :content]
    User => [:first_name, :last_name]
  }

  def raw_results
    ActiveRecord::Base.transaction do
      CONDITIONS_BY_MODEL.flat_map do |model, fields|
        Array(fields).map do |field|
          model.where(["#{field} LIKE ?", "%#{query}%"])
        end
      end
    end
  end
end

Below is my view. Currently the view will only display content and title. But my User has first_name and last_name therefore it won't display. I need a way to display the results of all Models and it needs to be clean so if i add another model to search with entirely different attributes it will still display. How would you do this?

.page-header
  %h1 Search#index

  - @results.each do |results|
    - results.each do |r|
      %p= r.title
      %p= r.content

Search controller

class SearchController < ApplicationController
  def index
    @results = SearchService.new(params[:q]).results
  end
end

1条回答
爷的心禁止访问
2楼-- · 2019-09-11 02:59

How to fix this.

class SearchController < ApplicationController
  def index
    @results = SearchService.new(params[:q]).results

    @page_results = []
    @news_article_results = []
    @category_results = []

    @results.each do |resource|
      if resource.class == Page
        @page_results << resource
      elsif resource.class == NewsArticle
        @news_article_results << resource
      elsif resource.class == Category
        @category_results << resource
      else
      end
    end
  end
end

class SearchService
  MIN_QUERY_LENGTH = 3
  attr_accessor :query

  def initialize(query)
    raise ArgumentError if query.length < MIN_QUERY_LENGTH
    @query = query
  end

  # searchable_fields_by_model
  CONDITIONS_BY_MODEL = {
    Page => [:title, :content],
    NewsArticle => [:title, :content],
    Category => :name,
  }

  def results
    @results ||= ActiveRecord::Base.transaction do
      CONDITIONS_BY_MODEL.flat_map do |model, fields|
        Array(fields).flat_map do |field|
          model.where(["#{field} LIKE ?", "%#{query}%"])
        end
      end
    end
  end
end

Here is my view

.page-header
  %h1 Search Results
  %h2= "For #{params[:q]}"
  %br
  %h2 Pages
  - @page_results.each do |resource|
    %p= link_to "#{resource.title}", page_path(resource.slug)
    = resource.content.html_safe

  %h2 News Article
  - @news_article_results.each do |resource|
    %p= link_to "#{resource.title}", news_article_path(resource.slug)
    = resource.content.html_safe

  %h2 Category
  - @category_results.each do |resource|
    %p= link_to "#{resource.name}", category_path(resource.slug)
查看更多
登录 后发表回答