2010-03-08 12 views
6

Mi piacerebbe impaginare attraverso un elenco ordinato a caso di modelli ActiveRecord (righe dal database MySQL).Ordinamento casuale stabile/ripetibile (MySQL, Rails)

Tuttavia, questa randomizzazione deve persistere su una base per sessione, in modo che altre persone che visitano il sito Web ricevano anche un elenco di record casuali e impaginabili.

Diciamo che ci sono abbastanza entità (decine di migliaia) che l'archiviazione dei valori ID ordinati in modo casuale nella sessione o in un cookie è troppo grande, quindi devo persistere temporaneamente in un altro modo (MySQL, file, ecc.).

Inizialmente pensavo di poter creare una funzione basata sull'ID di sessione e sull'ID di pagina (restituendo gli ID oggetto per quella pagina), tuttavia poiché i valori di ID oggetto in MySQL non sono sequenziali (ci sono spazi vuoti), che sembrava cadere a pezzi mentre lo stavo colpendo. La cosa bella è che non richiede uno storage minimo/minimo, ma gli svantaggi sono che è probabilmente piuttosto complesso da implementare e probabilmente richiede un uso intensivo della CPU.

La mia sensazione è che dovrei creare una tabella di intersezione, qualcosa di simile:

random_sorts(sort_id, created_at, user_id NULL if guest) 

random_sort_items(sort_id, item_id, position) 

E poi semplicemente memorizzare il 'sort_id' nella sessione. Quindi, posso impaginare random_sorts WHERE sort_id = n ORDER BY position LIMIT ... come al solito.

Ovviamente, dovrei mettere una specie di mietitore per rimuoverli dopo un certo periodo di inattività (basato su random_sorts.created_at).

Sfortunatamente, dovrei invalidare l'ordinamento quando sono stati creati nuovi oggetti (e/o vecchi oggetti rimossi, sebbene la cancellazione sia molto rara). E, come il carico aumenta le dimensioni/prestazioni di questa tabella (anche indicizzati correttamente) scende.

Sembra che questo dovrebbe essere un problema risolto ma non riesco a trovare alcun plugin per i binari che faccia questo ... Qualche idea? Grazie!!

risposta

6

MySQL ha una funzione RAND è possibile utilizzare nel vostro clausola ORDER, passando un seme legato alla sessione utente.

ORDER BY RAND (?)

Dove? è un valore seme dalla sessione. Ciò ti fornirà ordini ripetibili tra le richieste.

+0

Sì, funziona fintanto che le righe nella tabella non cambiano mai (se ne viene aggiunto uno nuovo, sono a conoscenza che l'intero set potrebbe cambiare). Inoltre, causa una scansione della tabella ogni volta, il che può essere un grande successo di prestazioni ... –

+0

il tuo commento sembra valido (anche se a volte tu * vuoi * tutto da fare se la tabella sottostante aggiunge qualcosa di nuovo). Sfortunatamente non riesco a pensare ad un modo per evitare di scansionare l'intero tavolo ogni volta, anche se forse un 'ordine entro il limite 10' potrebbe essere abbastanza intelligente da uscire presto ...). Se non si esegue un "ordine per", tuttavia, aggiungere una nuova voce potrebbe ancora randomizzare l'output, AFAIK, quindi questa potrebbe essere una domanda difficile da ottenere nel modo giusto ... – rogerdpack

+0

sembra ordine per rand quindi limitare " "Almeno una scansione completa della tabella (in mysql) anche se potrebbero esserci trucchi che potreste usare per evitarlo per velocizzarlo per tabelle di grandi dimensioni: http://stackoverflow.com/questions/211329/quick-selection-of-a- random-fila-da-un-grande-tavolo-in-mysql/211.388 – rogerdpack

2

Sono probabilmente manca qualcosa, ma non sarebbe qualcosa di simile

select ... order by sha1(concat($session_id,item_id)) limit m,n;

lavoro per darvi un elenco casuale-ordinata, ripetibile per sessione impaginato? Non molto bello sull'uso dell'indice, ma si evitano le tabelle di pre-riempimento/tmp/invalidazione.

0

Personalmente, per risparmiare spazio di archiviazione e sanità mentale, utilizzerei solo un seme casuale utilizzando il tuo id_utente.

srand user_id 
items.sort_by{ rand } 
+0

Quello userebbe molta memoria ruby ​​per ordinare l'intero array, giusto? –

+0

Sono un po 'al di fuori della mia profondità quando si tratta di interni, ma dal momento che è solo ordinamento dei puntatori, quale sarebbe un modo più efficiente? Forse un costume .shuffle! metodo con un seme casuale passato? http://stackoverflow.com/questions/2039902/how-does-rubys-sort-by-rand-work – ghoppe

+0

Un'altra possibile difficoltà qui è che srand è condiviso su tutto il processo di ruby ​​(AFAICT), quindi se sei multi -threaded, e non sincronizzato, ci potrebbe essere la possibilità per le condizioni di gara.Detto questo, nella 1.9.2 credo che ci sia una nuova classe Random che potresti usare al posto tuo (ma sì, dovresti caricare l'intero set nella RAM, cosa che potresti essere in grado di evitare, vedere Toby Hede's per un modo mysql). – rogerdpack