簡體   English   中英

如何基於篩選器和列對ActiveAdmin索引進行排序

[英]How to sort the ActiveAdmin index based on a filter and a column

我有一個帶有Movie模型的Rails應用程序。 Movie模型將'name'和'release_date'作為常規屬性,以及用於使用elasticsearch搜索電影名稱的作用域。

class Movie < ActiveRecord::Base

   scope :movie_name_search, -> (term) {
     movie_ids = elasticSearch(term, :name).map(&id)
     Movie.where(id: movie_ids).reorder('').order_by_ids(movie_ids) unless movie_ids.nil?     
   }

end

然后我設置我的活動管理員來顯示這些數據

ActiveAdmin.register Promotion do
  filter :movie_name_search, as: :string, label: "Movie Name"

  index do
    actions
    column :name
    column :release date, sortable: :release_date
  end

end

將電影名稱放入搜索欄可以很好地工作,並且對release_date進行排序可以很好地完成,但我不能同時進行這兩項操作。 一旦我使用過濾器的電影名稱,按日期排序不起作用。 它僅在我刪除重新排序和新訂單時有效。

scope :movie_name_search, -> (term) {
   movie_ids = elasticSearch(term, :name).map(&id)
   Movie.where(id: movie_ids) unless movie_ids.nil?     
}

看來我在范圍內強制執行的排序優先於列的排序,但我不知道為什么。 有任何想法嗎?

當您在movie_search_name調用Movie.where時,您正在重置范圍鏈。 你想送whereself ,而不是(即只刪除Movie.部分),使先決條件得以保留。

scope :movie_name_search, -> (term) {
  movie_ids = elasticSearch(term, :name).map(&id)
  where(id: movie_ids) unless movie_ids.nil?     
}

編輯:我現在明白這個問題

像你說的,彈性的搜索將返回數組id在有序S,但where不尊重這個順序。 它只是在找到它們時從數據庫中提取記錄,只要它們的id在數組中即可。 我們之后將記錄排序為數組對我們沒有好處,因為ActiveRecord不能將其他子句應用於查詢。 它必須作為查詢的一部分完成。

SQL確實提供了一種強制執行任意順序的方法: ORDER BY CASE ,但它不是內置於Rails中的。 幸運的是,添加並不難。

假設您的搜索結果是[2, 1, 3] 在SQL中,我們可以按照以下順序檢索它們:

SELECT * FROM movies
  WHERE id IN (2, 1, 3)
  ORDER BY CASE id
    WHEN 2 THEN 0
    WHEN 1 THEN 1
    WHEN 3 THEN 2
    ELSE 3 END;

為了使它與ActiveRecord兼容,我們可以為Movie添加一個類方法:

應用程序/模型/ movie.rb

class Movie < ActiveRecord::Base

  # ...

  def self.order_by_ids_array(ids)
    order_clause = "CASE id "
    ids.each_with_index do |id, index|
      order_clause << sanitize_sql_array(["WHEN ? THEN ? ", id, index])
    end
    order_clause << sanitize_sql_array(["ELSE ? END", ids.length])
    order(order_clause)
  end
end

現在,您的ActiveAdmin范圍同時使用whereorder_by_ids_array

scope :movie_name_search, -> (term) {
  movie_ids = elasticSearch(term, :name).map(&id)
  where(id: movie_ids).order_by_ids_array(movie_ids) unless movie_ids.nil?     
}

參考:

http://www.justinweiss.com/articles/how-to-select-database-records-in-an-arbitrary-order/

編輯II:一個真正的黑客

注意:這需要使用Ransack的最新版本的ActiveAdmin。

我們遇到的問題是過濾器在排序方面效果不佳。 所以這是新計划:讓我們在索引表中添加另一列,顯示每部電影的搜索排名。 此列僅在我們按電影名稱過濾時顯示,並且可以排序。 這種方式不會有“默認”排序,但您可以按任何方式排序,包括搜索排名。

訣竅是使用上面的CASE將一個計算列插入到查詢中,但是在SELECT子句中。 我們稱之為search_rank ,它可以在任何返回的電影上訪問,如movie.attributes['search_rank']

應用程序/管理/ movies.rb

ActiveAdmin.register Movie do

  filter :movie_search, as: string, label: 'Movie Name'

  index do

    # only show this column when a search term is present
    if params[:q] && params[:q][:movie_search]

      # we'll alias this column to `search_rank` in our scope so it can be sorted by
      column :search_rank, sortable: 'search_rank' do |movie|
        movie.attributes['search_rank']
      end
    end

  end
end

現在在我們的模型中,我們需要定義movie_search范圍(作為類方法)並將其標記為ransackable。

應用程序/模型/ movie.rb

class Movie < ActiveRecord::Base

  def self.ransackable_scopes(opts)
    [:movie_search]
  end

  def self.movie_search(term)
    # do search here
    ids = elasticSearch(term, :name).map(&id)

    # build extra column
    rank_col = "(CASE movies.id "
    ids.each_with_index do |id, index|
      rank_col << "WHEN #{ id } THEN #{ index } "
    end
    rank_col << 'ELSE NULL END) AS search_rank'

    select("movies.*, #{ rank_col }").where(id: ids)
  end
end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM