2010-03-08 12 views
6

Chciałbym dokonać paginacji przez losowo posortowaną listę modeli ActiveRecord (wiersze z bazy danych MySQL).Stały/powtarzalny sortowanie losowe (MySQL, Rails)

Jednak ta randomizacja musi trwać przez jedną sesję, aby inne osoby odwiedzające tę stronę otrzymały losową, możliwą do paginacji listę rekordów.

Załóżmy, że istnieje wystarczająco dużo encji (dziesiątek tysięcy), które przechowują losowo posortowane wartości ID w sesji lub plik cookie jest zbyt duży, więc muszę tymczasowo utrzymywać go w jakiś inny sposób (MySQL, plik itp.).).

Początkowo myślałem, że mogę utworzyć funkcję opartą na identyfikatorze sesji i identyfikatorze strony (zwracanie identyfikatorów obiektu dla tej strony), jednak ponieważ wartości identyfikatora obiektu w MySQL nie są sekwencyjne (są luki), które wydawały się być rozpadnie się, gdy go szturchałem. Fajną rzeczą jest to, że nie wymagałoby to/minimalnego przechowywania, ale wadą jest to, że prawdopodobnie jest dość skomplikowany w implementacji i prawdopodobnie intensywny procesor.

moim odczuciu należy utworzyć tabelę przecięcia, coś takiego:

random_sorts(sort_id, created_at, user_id NULL if guest) 

random_sort_items(sort_id, item_id, position) 

A potem po prostu przechowywać „sort_id” w sesji. Następnie mogę w paginacji random_sorts WHERE sort_id = n ORDER BY position LIMIT ... jak zwykle.

Oczywiście, musiałbym włożyć tam pewnego rodzaju żniwiarkę, aby usunąć je po pewnym okresie bezczynności (na podstawie random_sorts.created_at).

Niestety, musiałbym unieważnić sortowanie w miarę tworzenia nowych obiektów (i/lub usuwania starych obiektów, chociaż usuwanie jest bardzo rzadkie). I, jak obciążenie zwiększa rozmiar/wydajność tej tabeli (nawet poprawnie indeksowane) spada.

Wygląda na to, że powinien to być problem rozwiązany, ale nie mogę znaleźć wtyczek, które to robią ... Jakieś pomysły? Dzięki!!

Odpowiedz

6

MySQL ma funkcję RAND, którą można użyć w klauzuli ORDER, przekazując nasienie związane z sesją użytkownika.

ZAMÓW PRZEZ RAND (?)

Gdzie? jest wartością początkową z sesji. Zapewni to powtarzalne porządkowanie we wszystkich żądaniach.

+0

Tak, to działa tak długo, jak wiersze w tabeli nigdy się nie zmieniają (jeśli dodany jest nowy, rozumiem, że cały zestaw może się zmienić). Co więcej, powoduje to skanowanie tabeli za każdym razem, co może być dużym hitem wydajności ... –

+0

Twój komentarz wydaje się prawidłowy (choć czasami * chcesz * wszystko trzeba zastosować, jeśli tabela podstawowa dodaje coś nowego). Niestety nie mogę wymyślić sposobu, aby uniknąć skanowania całego stołu za każdym razem, ale może "zamówienie według id 10" może być wystarczająco inteligentne, aby wyjść wcześnie ...). Jeśli jednak nie wykonasz "zamówienia przez", to dodanie nowego wpisu może nadal losować twoje dane wyjściowe, AFAIK, więc to może być trudne pytanie, aby dostać się dobrze ... – rogerdpack

+0

wygląda jak zamówienie przez rand, następnie limit "robi "co najmniej pełne skanowanie tabeli (w mysql), chociaż mogą istnieć sztuczki, których można by użyć, aby go przyspieszyć w przypadku dużych tabel: http://stackoverflow.com/questions/211329/quick-selection-a- random-row-from-a-large-table-in-mysql/211388 – rogerdpack

2

jestem prawdopodobnie brakuje czegoś, ale nie coś takiego

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

pracy daje losową uporządkowane, powtarzalne listy każdej sesji numeracją stron? Niezbyt przyjemne korzystanie z indeksu, ale unikasz tabel wypełniania/tabel tmp/unieważnień.

0

Osobiście, aby zaoszczędzić miejsce do przechowywania i zdrowie psychiczne, po prostu użyłbym losowego materiału siewnego przy użyciu identyfikatora użytkownika.

srand user_id 
items.sort_by{ rand } 
+0

To by zużyło dużo rubinowej pamięci do posortowania całej tablicy, prawda? –

+0

Jestem trochę poza moją głębią, jeśli chodzi o elementy wewnętrzne, ale skoro to tylko sortowanie wskaźników, co byłoby bardziej efektywnym sposobem? Być może niestandardowy .shuffle! metoda z przekazanym losowym nasieniem? http://stackoverflow.com/questions/2039902/how-does-rubys-sort-by-rand-work – ghoppe

+0

Inną możliwą trudnością jest to, że srand jest udostępniany w całym procesie rublicznym (AFAICT), więc jeśli jesteś multi -wystąpione i niezsynchronizowane, może pojawić się sytuacja wyścigowa.To powiedziawszy, w wersji 1.9.2 uważam, że istnieje nowa klasa losowa, której mógłbyś użyć zamiast tego (ale tak, ładowałbyś cały zestaw do pamięci RAM, którego możesz uniknąć, zobacz Toby'ego Hede'a w sposób mysql). – rogerdpack