2012-04-01 20 views
5

Próbuję dokonać paginacji przetasowanej kwerendy ActiveRecord. Składnia robi to za pomocą gem Kaminari jest:Paginowanie przetasowanej kwerendy ActiveRecord

@users = Kaminari.paginate_array(User.all.shuffle).page(params[:page]).per(20) 

Problem polega na tym, że User.all jest ponownie tasuje na każde żądanie stronicowania, powodując zduplikowane rekordy nazywać. Czy istnieje sposób, aby zapobiec tego rodzaju duplikacji?

+1

Wywołanie "User.all" może znacznie spowolnić twój serwer. Nawet jeśli masz tylko 100 użytkowników, będziesz płacić niepotrzebne koszty związane z doprowadzeniem wszystkich użytkowników do przestrzeni pamięci ruby ​​w każdym żądaniu. –

Odpowiedz

5

Trzeba zdać ziarno dla rand między zapytaniami

params[:seed] ||= Random.new_seed 
srand params[:seed].to_i 
@users = Kaminari.paginate_array(User.all.shuffle).page(params[:page]).per(20) 

iw świetle dodać params [: nasiona] do wszystkich linków Kaminari do stron

+0

dzięki za trop! jeśli to możliwe, czy możesz wyjaśnić, jak to działa? Nie podążam całkowicie, wciąż nie jestem pewien, jak wprowadzić tę odpowiedź. – neon

+0

Możesz przeczytać dokumentację API, jak działa nasiono http://ruby-doc.org/core-1.9.2/Random.html – MikDiet

+0

Zorientowałem się, ale teraz tablica pojawia się w tej samej kolejności w kolejności losowej - co oznacza nawet po użytkownik wylogowuje się i wraca, tablica pozostaje w tym samym, początkowo przetasowanym zamówieniu, a nie ponownie przetasowana. Dziwne. – neon

3

Jak KandadaBoggu zwraca uwagę powyżej, pobieranie wszystkich User rekordy z bazy danych są nieefektywne, gdy potrzebujesz tylko 20. Sugeruję użycie MySQL's RAND() function do wykonania randomizacji przed po powrocie z bazy danych. Nadal możesz przekazać wartość początkową do RAND(), aby upewnić się, że losowanie odbywa się tylko raz na sesję.

Na przykład:

class User < ActiveRecord::Base 
    def self.randomized(seed = nil) 
    seed = seed.to_i rescue 0 
    order("RAND(#{seed})") 
    end 
end 

class UsersController < ApplicationController 
    before_filter :set_random_seed 

    def index 
    @users = User.randomized(session[:seed]).page(params[:page]).per(20) 
    end 

private 

    def set_random_seed 
    session[:seed] ||= Random.new_seed 
    end 
end 

nie mam instalacji MySQL do przetestowania przed, ale to powinno działać lepiej niż oryginalny kod.

0

Można też to zrobić:

class UsersController < ApplicationController 
    USERS_SEED = 1000 # Or any another not-so-big number 

    def set_random_seed 
    session[:seed] ||= Random.rand(USERS_SEED) 
    end 
end 

Ponieważ Random.new_seed wygeneruje najprawdopodobniej ten sam wynik, jeśli dane nie jest duża.