2010-06-09 4 views
18

Mam datastore z około 1,000,000 podmiotów w modelu. Chcę pobrać z tego 10 losowych elementów.Pobieranie losowego rekordu z Google App Engine Datastore?

Nie jestem pewien, jak to zrobić? Czy ktoś może pomóc?

+0

możliwy duplikat [kwerendy dla N losowych rekordów w magazynie danych Appengine] (http://stackoverflow.com/questions/1105004/querying-for-n-random-records-on-appengine-datastore) –

Odpowiedz

21

Przypisz każdej jednostce liczbę losową i zapisz ją w encji. Następnie zapytaj o dziesięć rekordów, których liczba losowa jest większa niż (lub mniejsza niż) inna liczba losowa.

Nie jest to jednak całkowicie przypadkowe, ponieważ podmioty z pobliskimi liczbami losowymi będą się często pojawiać razem. Jeśli chcesz pokonać to, zrób dziesięć zapytań opartych na dziesięciu losowych liczbach, ale będzie to mniej efektywne.

+0

Dokładnie dobrze. Może chcieć wymienić zakres (0..1 jest standardem) dla liczb losowych. –

+4

Jedną z możliwości zwiększenia losowości bez uszczerbku na skuteczności odczytu jest dodanie zadania polegającego na przypisaniu nowych liczb losowych do pobranych elementów, więc jeśli raz uderzysz w jeden z nich, nie uzyskasz z nim tych samych sąsiadów. – geoffspear

+0

@NickJohnson czy mógłbyś wyjaśnić standardowy zakres? Przepraszam, nie rozumiem, co miałeś na myśli (0..1)? Również dla was wszystkich: martwię się o użycie mojego filtru nierówności dla tej operacji (ponieważ w niektórych zapytaniach potrzebuję tego, aby był losowy, ale w tym samym czasie uruchom filtr równości na innej właściwości). Jak źle jest zrobić 10 zapytań, czy to w zasadzie 10 razy więcej? – iceanfire

3

Odpowiedź Jason Hall i the one here nie są straszne, ale jak wspomina, nie są też przypadkowe. Nawet wykonanie dziesięciu zapytań nie będzie losowe, jeśli na przykład wszystkie liczby losowe zostaną zgrupowane razem. Aby zachować rzeczy prawdziwie losowych, tu są dwa możliwe rozwiązania:

Rozwiązanie 1

Przypisz indeks do każdego obiektu magazynu danych, śledzenie maksymalnego wskaźnika i losowo wybrać indeks za każdym razem, gdy chcesz uzyskać losowy rekord:

MyObject.objects.filter('index =', random.randrange(0, maxindex+1))

Upside: Prawdziwie losowe. Szybki.

Down-side: Podczas dodawania i usuwania obiektów należy odpowiednio dbać o indeksy, co może spowodować, że obie operacje będą operacją O (N).

Rozwiązanie 2

przypisać liczbę losową do każdego numeru magazynu danych, gdy jest on tworzony. Następnie, aby uzyskać losowy rekord za pierwszym razem, zapytaj o rekord o losowej liczbie większej niż jakaś inna losowa liczba i kolejność według liczb losowych (tj. MyObject.order('rand_num').filter('rand_num >=', random.random())). Następnie zapisz to zapytanie jako kursor w memcache. Aby uzyskać losowy rekord po raz pierwszy, załaduj kursor z memcache i przejdź do następnego elementu. Jeśli po pierwszym nie ma elementu, ponownie uruchom zapytanie.

Aby zapobiec powtarzaniu się sekwencji obiektów, na każdym odczytywanym magazynie danych podaj podmiot, który właśnie przeczytałeś, nowy losowy numer i zapisz go ponownie w magazynie danych.

Up-side: Naprawdę losowy. Brak złożonych wskaźników do utrzymania.

Down-side: Konieczność śledzenia kursora. Musisz zrobić put za każdym razem, gdy otrzymasz losowy rekord.

+0

"Nawet wykonanie dziesięciu zapytań nie będzie losowe, jeśli na przykład wszystkie liczby losowe zostaną zgrupowane razem" - zakładam, że mówisz o liczbach losowych, które zostały przypisane do wierszy magazynu danych. Jest to problem tylko dla niewielkiej liczby rekordów - standardowe odchylenie odstępów między wartościami zmniejsza się wraz ze wzrostem liczby wartości do punktu, w którym jest statystycznie nieistotny. Twoje rozwiązanie 1 wymaga monotonicznego licznika, który jest powolną i kosztowną operacją w App Engine. Rozwiązanie 2 wykorzystuje wybór bez wymiany, który różni się od tego, o co prosił OP. –

+0

W porządku, podejście naiwne ulega rozpadowi, jeśli nie ma wielu rekordów lub jeśli pobiera się je z dużą szybkością. Ponadto po ustawieniu wartości rand_num ich dystrybucja jest stała. Nie dostaniesz dobrej jednolitej dystrybucji, a niektóre rekordy będą rzadko wybierane. – speedplane

+0

Nie, to był mój punkt - im większa liczba rekordów, tym mniejsze odchylenie standardowe w interwale. Oznacza to, że proporcjonalnie mniej jednostek zostanie przypisanych do nienormalnie małych odstępów czasu. Sugerowana przez Wooble'a zmiana przydziału numerów po wybraniu rekordu również pomoże przeciwdziałać temu. –