Вы перенаправляете цепочку областей действия, когда вы вызываете Movie.where
в movie_search_name
. Вы хотите отправить where
вместо self
(т. Е. Просто удалите часть Movie.
), чтобы сохранить прежние условия.
scope :movie_name_search, -> (term) {
movie_ids = elasticSearch(term, :name).map(&id)
where(id: movie_ids) unless movie_ids.nil?
}
Edit: Я понимаю, этот вопрос сейчас
Как вы говорите, Elastic Search возвращает массив id
с в отсортированном порядке, но where
не уважает этот порядок. Он просто извлекает записи из базы данных по мере их нахождения, пока их id
s находятся в массиве. Нам нехорошо сортировать записи впоследствии как массив, потому что тогда 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.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 сфера использует как where
и order_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/
Edit II: Реальный хак
Примечание: Для этого требуется последняя версия ActiveAdmin, которая использует Ransack.
Проблема, с которой мы сталкиваемся, заключается в том, что фильтры не очень хорошо подходят для сортировки. Итак, вот новый план: давайте добавим еще один столбец в нашу индексную таблицу, которая показывает поиск ранга каждого фильма. Этот столбец появится только в том случае, если мы отфильтровали по имени фильма , и оно будет сортироваться. Таким образом, не будет сортировки по умолчанию, но вы можете сортировать все, что угодно, включая ранжирование поиска.
Трюк состоит в том, чтобы вставить вычисленный столбец в запрос, используя 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
Там не кажется, что ничего плохого с кодом выше, за исключением места, где подчеркивание должно быть в 'столбец: релиз date'. Здесь также должна быть лишняя часть 'sortable'. – ahmacleod
Вы правы, после некоторой отладки я сузил проблему и обновил свой вопрос. – Nate