5

Ich habe eine Datenbank von Filmen mit Attributen. Ich möchte eine abgefragte Menge dieser Filme in zufälliger Reihenfolge an eine Vorlage mit Paginierung zurückgeben. Ich benutze will_paginate. Ich habe folgendes versucht:Rails 3, will_paginate, zufällige, wiederholte Datensätze, Postgres, setseed Fehler

## MoviesController 

movies = Movie.get_movies(query_string) # a method in Movie model that takes in 
              # a query_string and fetches movies 
              # with user-set params 

@movies = movies.order('random()').page(params[:page]).per_page(16) 

Dies funktioniert gut, außer Filme werden von Seite zu Seite wiederholt. Lösungen für dieses Problem wurden veröffentlicht here und here. Diese Links erklären, dass Random() Seed von Seite zu Seite zurückgesetzt wird, gibt es keine Konsistenz und OFFSET wird unbrauchbar gemacht. Sie bieten großartige Lösungen für MySQL-Benutzer, da die rand (n)-Funktion einen Seed n übernimmt. Postgres tut dies jedoch nicht. Sie haben setseed (n) in SELECT vor random() in ORDER Ausgabe zu erklären.

So habe ich versucht, den Postgres Weg, um die Samen zu setzen:

@movies = movies.select('setseed(.5)').order('random()').page(params[:page]).per_page(16) 

Seltsamerweise, die Film mit absolut keine Attribute Objekte zurückgegeben. Das Folgende wurde aus der Vorlage erhöht:

ActiveModel :: MissingAttributeError in Filme # Aktion

fehlende Attribut: some_movie_attribute

ich dies und debuggt, sobald der Stapel action_controller erreicht/Metall, @movies enthalten:

[#<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >]

Die Anzahl der Filmobjekte (18) entspricht der Anzahl der Filme, die von der Abfrage zurückgegeben werden.

Ich habe versucht, auch die folgenden zu sehen, ob setseed (n) das Problem Verfahren durch Entfernen der zufälligen Reihenfolge war:

@movies = movies.select('setseed(.5)').page(params[:page]).per_page(16) 

Dies ergab die gleichen Nicht-Attribut Filmobjekte wie oben verlegt werden. So scheint es, dass setseed (n) tatsächlich das Problem ist.

habe ich versucht, ein paar Workarounds, wie:

# MoviesController 

movies = Movie.get_movies(query_string) 
shuf_movs = movies.shuffle ## effectively turns shuf_movs into an array 

@movies = shuf_movs.paginate(:page => params[:page], :per_page => 16) 

die auch Seiten mit sich wiederholenden Filmen zurück. Ich dachte, das sei deshalb, weil die Paginierung die Objekte benötigt, die von etwas bestellt werden sollen, und Sie können in Array # shuffle keine Seeds festlegen. Also habe ich versucht, meinen eigenen Randomisierungscode zu schreiben, der vorübergehend eine ID speichern würde, um in den Movie-Objekten geordnet zu werden. (Bitte entschuldigen Sie den schlampigen Code):

# Movie model 

attr_accessor :rand_id 

# MoviesController 

movies = get_movies(query_string) 
movies_count = movies.count 
r = Random.new 
nums = [] 
rand_movs = [] 
id = 1 
while nums.count != movies_count 
    num = r.rand(0..movies_count - 1) 
    if !(nums.include?(num)) 
    movie = movies[num] 
    movie.rand_id = id 
    rand_movs << movie 
    nums  << num 
    id += 1 
    end 
end 

@movies = rand_movs.sort_by { |a| a.rand_id }.paginate(:page => params[:page], :per_page => 16) 

Das produzierte immer noch das Wiederholen von Filmen in verschiedenen Seiten.An diesem Punkt merke ich, dass wil_paginate nicht berücksichtigt, was Sie sortiert haben, bevor paginate aufgerufen wird. Also habe ich das versucht:

@movies = rand_movs.paginate(:order => 'rand_id', :page => params[:page], :per_page => 16) 

Das wiederholt immer noch Datensätze. : order -> 'rand_id' wird ignoriert, weil : order nur dann von Bedeutung ist, wenn Sie mit ActiveRecord-Objekten, nicht mit Arrays arbeiten.

Also setseed (n) scheint meine einzige Hoffnung für das Erreichen von randomisierten sich nicht wiederholenden Aufzeichnungen mit will_paginate zu sein. Irgendwelche Ideen?

Danke!

+0

Die select gibt die Spalten mit Saatgut in der Abfrage, und Sie haben keine angegeben, nur den Seed. Nicht sicher, ob es funktioniert, aber versuchen Sie es: '.select ('*, setseed (1)')' – Matzi

Antwort

18

kein Postgres Person, aber ... Ich würde versuchen,

Movie.connection.execute "select setseed(0.5)" 
Movie.where(...).order('random()').page(params[:page]).per_page(15) 

Im Hinblick auf Array#shuffle keinen Samen nehmen, verwendet es Kernel.rand so können Sie es Kernel.srand

+0

Danke, Frederick. Das hat den Trick. Auch, danke für die Köpfe re : Seeding Array # Shuffle mit Kernel – jkym

+0

Ich versuche das gleiche zu erreichen.Dies gibt mir die gleiche zufällige Reihenfolge bei jedem Reload, aber wie bekomme ich eine neue zufällige Reihenfolge für jeden Benutzer der Website besuchen? – funkylaundry

+2

Sie möchten einen anderen Seed für jeden Benutzer übergeben –

1

versuchen eine Reihe von Feldern select vorbei:

@movies = movies.select(['setseed(.5)', 'some_movie_attribute']).order('random()').page(params[:page]).per_page(16) 

Einige Ergebnis some_movie_attribute verwendet, die durch Ihre Frage nicht so ausgewählt ist, und so ist nicht verfügbar. Wenn Sie es als eines der Auswahlfelder hinzufügen, sollte dieses Problem gelöst werden.

+0

Vielen Dank. Ich probierte 'movies.select (['setseed (.5)', '*']). Order ('random()'). Page (params [: page]). Per_page (16) ' Übergeben eines Arrays wie Du hast vorgeschlagen, aber stattdessen mit '*' alle Movie-Felder aufzurufen (meine Vorlage ruft eine Reihe von Attributen auf), rendert die Vorlage (hurra!). Aber jetzt mit dem Set, die Datensätze nicht nach dem Zufallsprinzip sortieren - sie sind von ID angeordnet, egal, was Saatgut weitergegeben wird. :-( – jkym