【问题标题】:Rails: Search & Filter Books by CategoryRails:按类别搜索和过滤书籍
【发布时间】:2019-09-03 01:58:39
【问题描述】:

我需要一些帮助。我正在为我的数字图书馆构建一个高级搜索功能,我想按图书名称或描述搜索图书,然后按图书所属的类别过滤搜索结果。

数据库的结构是这样的:书籍属于子类别和类别,而子类别仅属于类别。但我想按类别过滤。

假设我有一本名为 Great AchieversGreat Developers 的书,它们属于 AchieversDevelopers 类别强>分别。我希望以这种方式进行搜索:当我使用关键字 Great 进行搜索时,我会看到 Great AchieversGreat Developers 这本书,而当我使用我按成就者类别过滤,我只会看到伟大成就者

目前我的高级搜索功能可以按书名和描述搜索图书,但无法在搜索结果中按类别过滤图书。

这是书本模型的代码

class Book < ApplicationRecord
  has_one_attached :upload
  belongs_to :category, required: false
  belongs_to :sub_category, required: false

  def self.search(keywords)
      if keywords
        where("name LIKE ? OR description LIKE ? OR author LIKE ?", "%#{keywords}%", "%#{keywords}%", "%#{keywords}%").order('id DESC')
      else
        order('id DESC') 
      end
    end
end

这是类别模型的代码

class Category < ApplicationRecord
  has_many :sub_categories
  has_many :books
end

这是子类别模型的代码

class SubCategory < ApplicationRecord
  has_many :books
  belongs_to :category, required: false
 end

这是图书控制器的截断代码

class BooksController < ApplicationController
  def index
    @books = Book.search(params[:keywords]).paginate(:page => params[:page], :per_page => 9).order('created_at DESC')
  end

  private
    def set_book
      @book = Book.find(params[:id])
    end

    def book_params
      params.require(:book).permit(:name, :author, :description, :category_id, :sub_category_id, :new_sub_category_name, :upload, :keywords, :deep_keywords)
    end
end

这是图书索引视图中搜索功能的截断代码

<%= form_tag(books_path, method: :get) do %>
    <%= text_field_tag :keywords, params[:keywords] %>
    <%= collection_select :category, :id, Category.all.order('name ASC'), :id, :name,{include_blank: 'Select Category'} %>
    <%= submit_tag 'Search', name: nil %>
<% end %>

我附上了搜索操作完成后控制台日志的屏幕截图

我们将不胜感激任何形式的帮助。谢谢。

【问题讨论】:

    标签: ruby-on-rails search


    【解决方案1】:

    你需要一个连接(未经测试,但你明白了):

    def self.search(params)
      books = where(nil)
      keywords = params.dig(:keywords)
      if keywords
        books = books.where("name LIKE ? OR description LIKE ? OR author LIKE ?",
                             "%#{keywords}%", "%#{keywords}%", "%#{keywords}%")   
      end
    
      cat_id = params.dig(:category, :id)
      if cat_id
        books = books.joins(:category).where(category: {id: cat_id})
      end
    
      books.order('id DESC')
    end
    

    编辑:

      def index
        @books = Book.search(params).
                 paginate(page: params[:page], per_page: 9).
                 order('created_at DESC') # You already set order on search
      end
    

    【讨论】:

    • 非常感谢您的回答。我是否必须对我的 books 控制器 和我的 book index 视图搜索功能进行任何更改才能正常工作?
    • 嗨@CAmador,我刚试过,这是我遇到的错误:ActiveRecord::StatementInvalid in Books#index SQLite3::SQLException: ambiguous column name: name : SELECT "books".* FROM "books" INNER JOIN "categories" ON "categories"."id" = "books"."category_id" WHERE (name LIKE '%nursing%' OR description LIKE '%nursing%' OR作者 LIKE '%nursing%') AND "category"."id" = ? ORDER BY id DESC, created_at DESC LIMIT ?抵消 ?。我该如何解决?
    【解决方案2】:

    最好的办法是找到过滤类别下的书籍。然后根据搜索词从该图书收藏中过滤。

    您可以修改books控制器代码如下。

    def index 
    if params['category'].blank? or params['category']['id'].blank? 
    @books = Book.all 
    else 
    category = Category.find(params['category']['id']) 
    @books = category.books 
    end 
    @books = @books.search(params[:keywords]).paginate(:page => params[:page], :per_page => 9).order('created_at DESC') 
    end
    

    图书索引视图中搜索功能的截断代码可以保持不变

    <%= form_tag(books_path, method: :get) do %>
        <%= text_field_tag :keywords, params[:keywords] %>
        <%= collection_select :category, :id, Category.all.order('name ASC'), :id, :name,{include_blank: 'Select Category'} %>
        <%= submit_tag 'Search', name: nil %>
    <% end %>
    

    书籍模型的代码也可以保持不变

    class Book < ApplicationRecord
      has_one_attached :upload
      belongs_to :category, required: false
      belongs_to :sub_category, required: false
    
      def self.search(keywords)
          if keywords
            where("name LIKE ? OR description LIKE ? OR author LIKE ?", "%#{keywords}%", "%#{keywords}%", "%#{keywords}%").order('id DESC')
          else
            order('id DESC') 
          end
        end
    end
    

    我希望这会有所帮助。

    如果有帮助,请将此答案赞为有用,或在答案下方评论以获得更多说明。

    【讨论】:

    • 非常感谢您的回答。我是否必须对我的图书模型和我的图书索引视图搜索功能进行任何更改才能正常工作?
    • @PromisePreston,无需更改图书模型文件中的任何内容。只需更新我在书籍控制器中回答的索引方法。这应该可以正常工作。请验证。
    • 嗨@dharmesh 和@CAmador,我刚刚尝试了这个答案,但我收到了这个错误。 BooksController#index 中的 NoMethodError。 'undefined method `search' for #<:activerecord_relation:0x0000559a0d3ae628> 你的意思是?每个'。我该怎么办?
    • @PromisePreston,刚刚在我的示例应用程序中测试了您的示例。无需更改书籍模型中的任何内容。问题中的搜索功能应保持不变。只需更新 Book 控制器并尝试此答案,如果它工作正常,请接受答案。
    • 嗨@dharmesh,我刚刚试了一下,它工作了,但搜索结果还没有按类别过滤。当我在搜索过程中选择特定类别时,它只是根据书籍的名称、描述和作者显示书籍搜索,而不过滤书籍结果。你能帮忙吗?谢谢。
    【解决方案3】:

    如果您使用ransack,您可以避免自己进行搜索。你甚至可以用它在你的控制器中做一个单行来实现你想要的:

    def index
        @books = Book.all.ransack(params[:q]).results.paginate(:page => params[:page], :per_page => 9)
    end
    

    Then in your requests you have to pass theparams[:q],为书籍资源做任何你需要做的搜索。即:

    #params[:q] for books with category_id == 10
    { "category_id_eq": 10 }
    #params[:q] for books with name or description or author matching "search"
    { "name_or_description_or_author_matches": "%search%" }
    #params[:q] for books with name or description or author matching "search" and category_id == 10
    { "name_or_description_or_author_matches": "%search%", "category_id_eq": 10 }
    #params[:q] for books with name or description or author matching "search" and category_id == 10, ordered by created_at
    { "name_or_description_or_author_matches": "%search%", "category_id_eq": 10, "s": "created_at desc" }
    

    检查available matchers,以便您可以根据需要构建过滤器。

    【讨论】: