Szukam losowego rekordu z ogromnego (100 milionów rekordów) mongodb
.
Jaki jest najszybszy i najbardziej efektywny sposób? Dane już tam są i nie ma pola, w którym mogę wygenerować losową liczbę i uzyskać losowy wiersz.
Jakieś sugestie?
mongodb
mongodb-query
Will M.
źródło
źródło
Odpowiedzi:
Począwszy od wersji 3.2 MongoDB, możesz pobrać N losowych dokumentów z kolekcji za pomocą
$sample
operatora potoku agregacji:Jeśli chcesz wybrać losowe dokumenty z odfiltrowanego podzbioru kolekcji, dołącz
$match
etap do potoku:Jak zauważono w komentarzach, gdy wartość
size
jest większa niż 1, w zwróconej próbce dokumentu mogą występować duplikaty.źródło
Zliczyć wszystkie rekordy, wygenerować losową liczbę od 0 do zliczenia, a następnie:
źródło
Aktualizacja dla MongoDB 3.2
3.2 wprowadził $ sample do potoku agregacji.
Jest też dobry post na blogu dotyczący jego praktycznego zastosowania.
Dla starszych wersji (poprzednia odpowiedź)
To była właściwie prośba o dodanie funkcji: http://jira.mongodb.org/browse/SERVER-533, ale została złożona w polu „Nie naprawię”.
Książka kucharska ma bardzo dobry przepis na wybranie losowego dokumentu z kolekcji: http://cookbook.mongodb.org/patterns/random-attribute/
Aby sparafrazować przepis, przypisujesz losowe liczby do dokumentów:
Następnie wybierz losowy dokument:
Odpytywanie z oboma
$gte
i$lte
konieczne jest znalezienie dokumentu z losową liczbę najbliższegorand
.I oczywiście będziesz chciał zaindeksować losowe pole:
Jeśli już korzystasz z indeksu, po prostu upuść go, dołącz
random: 1
do niego i dodaj ponownie.źródło
$gte
jest pierwszy. Alternatywne rozwiązanie stackoverflow.com/a/9499484/79201 działałoby lepiej w tym przypadku.Możesz także użyć funkcji indeksowania geoprzestrzennego MongoDB, aby wybrać dokumenty „najbliższe” losowej liczbie.
Najpierw włącz indeksowanie geoprzestrzenne w kolekcji:
Aby utworzyć wiązkę dokumentów z losowymi punktami na osi X:
Następnie możesz pobrać losowy dokument z kolekcji w następujący sposób:
Lub możesz pobrać kilka dokumentów najbliższych losowemu punktowi:
Wymaga to tylko jednego zapytania i bez sprawdzania wartości NULL, a ponadto kod jest czysty, prosty i elastyczny. Możesz nawet użyć osi Y geopoint, aby dodać drugi wymiar losowości do zapytania.
źródło
Poniższy przepis jest nieco wolniejszy niż rozwiązanie książki kucharskiej mongo (dodaj losowy klucz na każdym dokumencie), ale zwraca bardziej równomiernie rozmieszczone losowe dokumenty. Jest nieco mniej równomiernie rozłożony niż
skip( random )
rozwiązanie, ale o wiele szybszy i bardziej bezpieczny w przypadku usunięcia dokumentów.Wymaga to również dodania losowego „losowego” pola do dokumentów, więc nie zapomnij dodać tego podczas ich tworzenia: może być konieczne zainicjowanie kolekcji, jak pokazuje Geoffrey
Wyniki testu
Ta metoda jest znacznie szybsza niż
skip()
metoda (ceejayoz) i generuje bardziej jednolicie losowe dokumenty niż metoda „książki kucharskiej” zgłoszona przez Michaela:W przypadku kolekcji z 1 000 000 elementów:
Ta metoda zajmuje mniej niż milisekundę na moim komputerze
skip()
sposobie, 180 ms średnioMetoda książki kucharskiej spowoduje, że duża liczba dokumentów nigdy nie zostanie wybrana, ponieważ ich losowa liczba ich nie sprzyja.
Ta metoda zbierze wszystkie elementy równomiernie w czasie.
W moim teście było tylko 30% wolniejsze niż metoda książki kucharskiej.
losowość nie jest w 100% idealna, ale jest bardzo dobra (w razie potrzeby można ją poprawić)
Ten przepis nie jest idealny - idealne rozwiązanie byłoby wbudowaną funkcją, jak zauważyli inni.
Jednak powinien być dobrym kompromisem dla wielu celów.
źródło
Oto sposób użycia
ObjectId
wartości domyślnych_id
i odrobiny matematyki i logiki.Jest to ogólna logika reprezentacji powłoki i łatwa do dostosowania.
Więc w punktach:
Znajdź minimalną i maksymalną wartość klucza podstawowego w kolekcji
Wygeneruj losową liczbę, która przypada między znacznikami czasu tych dokumentów.
Dodaj liczbę losową do minimalnej wartości i znajdź pierwszy dokument, który jest większy lub równy tej wartości.
Używa „padding” z wartości znacznika czasu w „hex”, aby utworzyć prawidłową
ObjectId
wartość, ponieważ tego właśnie szukamy. Używanie liczb całkowitych jako_id
wartości jest zasadniczo prostsze, ale ta sama podstawowa idea w punktach.źródło
W Pythonie za pomocą pymongo:
źródło
count()
zeestimated_document_count()
jakcount()
jest przestarzałe w Mongdo v4.2.Teraz możesz użyć agregatu. Przykład:
Zobacz dokument .
źródło
jest to trudne, jeśli nie ma danych, które można by usunąć. jakie są pola _id? czy są to identyfikatory obiektów mongodb? Jeśli tak, możesz uzyskać najwyższe i najniższe wartości:
to jeśli założymy, że identyfikatory są rozmieszczone równomiernie (ale tak nie jest, ale przynajmniej to początek):
źródło
Za pomocą Pythona (pymongo) działa również funkcja agregująca.
Takie podejście jest znacznie szybsze niż uruchamianie zapytania o liczbę losową (np. Collection.find ([random_int]). Dotyczy to zwłaszcza dużych kolekcji.
źródło
Możesz wybrać losowy znacznik czasu i wyszukać pierwszy obiekt, który został później utworzony. Będzie skanował tylko jeden dokument, choć niekoniecznie zapewnia jednolitą dystrybucję.
źródło
Moje rozwiązanie na php:
źródło
Aby uzyskać określoną liczbę losowych dokumentów bez duplikatów:
pętla uzyskuje losowy indeks i pomija duplikaty
źródło
Sugerowałbym użycie mapy / zmniejszenia, gdzie używasz funkcji mapy, aby emitować tylko wtedy, gdy losowa wartość przekracza podane prawdopodobieństwo.
Powyższa funkcja zmniejszania działa, ponieważ tylko jeden klawisz („1”) jest emitowany z funkcji mapy.
Wartość „prawdopodobieństwa” jest zdefiniowana w „zasięgu” podczas wywoływania mapRreduce (...)
Korzystanie z mapReduce w ten sposób powinno być również możliwe na dzielonym db.
Jeśli chcesz wybrać dokładnie n spośród m dokumentów z bazy danych, możesz to zrobić w następujący sposób:
Gdzie „countTotal” (m) to liczba dokumentów w bazie danych, a „countSubset” (n) to liczba dokumentów do pobrania.
Takie podejście może powodować pewne problemy w dzielonych bazach danych.
źródło
Możesz wybrać losowy _id i zwrócić odpowiedni obiekt:
Tutaj nie musisz tracić miejsca na przechowywanie losowych liczb w kolekcji.
źródło
Sugeruję dodanie losowego pola int do każdego obiektu. Następnie możesz po prostu zrobić
wybrać losowy dokument. Tylko upewnij się, że masz indeksIndex ({random_field: 1})
źródło
Kiedy miałem do czynienia z podobnym rozwiązaniem, wycofałem się i stwierdziłem, że zlecenie biznesowe dotyczyło stworzenia jakiejś formy rotacji prezentowanych zapasów. W takim przypadku istnieją znacznie lepsze opcje, które mają odpowiedzi z wyszukiwarek takich jak Solr, a nie ze sklepów danych takich jak MongoDB.
Krótko mówiąc, z wymogiem „inteligentnego obracania” treści, powinniśmy zrobić zamiast losowej liczby we wszystkich dokumentach, aby uwzględnić osobisty modyfikator q score. Aby wdrożyć to samodzielnie, zakładając niewielką populację użytkowników, możesz przechowywać dokument na użytkownika, który ma identyfikator produktu, liczbę wyświetleń, liczbę kliknięć, datę ostatniego wyświetlenia i wszelkie inne czynniki, które firma uzna za istotne dla obliczenia wyniku aq modyfikator. Podczas pobierania zestawu do wyświetlenia, zwykle żądasz więcej danych z magazynu danych niż żąda tego użytkownik końcowy, a następnie zastosujesz modyfikator q score, weź liczbę rekordów wymaganych przez użytkownika końcowego, a następnie losowo przejrzysz stronę wyników, niewielki ustaw, więc po prostu posortuj dokumenty w warstwie aplikacji (w pamięci).
Jeśli wszechświat użytkowników jest zbyt duży, możesz podzielić użytkowników na kategorie zachowań i indeksować według grup zachowań, a nie według użytkowników.
Jeśli wszechświat produktów jest wystarczająco mały, możesz utworzyć indeks dla użytkownika.
Uważam, że ta technika jest znacznie bardziej wydajna, ale co ważniejsze, bardziej efektywna w tworzeniu odpowiedniego, wartościowego doświadczenia w korzystaniu z oprogramowania.
źródło
żadne z rozwiązań nie działało dla mnie dobrze. szczególnie, gdy jest wiele luk i zestaw jest mały. to działało bardzo dobrze dla mnie (w php):
źródło
find
+skip
jest dość złe, zwracasz wszystkie dokumenty tylko po to, aby wybrać jeden: S.Jeśli używasz mangusty, możesz użyć mongoose-random mongoose-random
źródło
Moje sortowanie / zamówienie PHP / MongoDB według rozwiązania RANDOM. Mam nadzieję, że to pomoże każdemu.
Uwaga: W mojej kolekcji MongoDB mam identyfikatory numeryczne, które odnoszą się do rekordu bazy danych MySQL.
Najpierw tworzę tablicę z 10 losowo generowanymi liczbami
W mojej agregacji korzystam z operatora potoku $ addField w połączeniu z $ arrayElemAt i $ mod (moduł). Operator modułu da mi liczbę od 0 do 9, której następnie używam do wybrania liczby z tablicy z losowo wygenerowanymi liczbami.
Następnie możesz użyć sortowania Pipeline.
źródło
Jeśli masz prosty klucz identyfikatora, możesz przechowywać wszystkie identyfikatory w tablicy, a następnie wybrać losowy identyfikator. (Odpowiedź Ruby):
źródło
Używając Map / Reduce, możesz z pewnością uzyskać losowy rekord, ale niekoniecznie bardzo efektywnie, w zależności od wielkości wynikowej filtrowanej kolekcji, z którą ostatecznie pracujesz.
Przetestowałem tę metodę z 50 000 dokumentów (filtr zmniejsza ją do około 30 000) i działa w około 400 ms na procesorze Intel i3 z 16 GB pamięci RAM i dyskiem twardym SATA3 ...
Funkcja mapy tworzy po prostu tablicę identyfikatorów wszystkich dokumentów pasujących do zapytania. W moim przypadku przetestowałem to z około 30 000 z 50 000 możliwych dokumentów.
Funkcja Reduce po prostu wybiera losową liczbę całkowitą od 0 do liczby elementów (-1) w tablicy, a następnie zwraca ten _id z tablicy.
400 ms brzmi jak długi czas, a tak naprawdę, jeśli masz pięćdziesiąt milionów płyt zamiast pięćdziesięciu tysięcy, może to zwiększyć obciążenie do tego stopnia, że stanie się bezużyteczne w sytuacjach, w których korzysta wielu użytkowników.
MongoDB ma otwarty problem, aby włączyć tę funkcję do rdzenia ... https://jira.mongodb.org/browse/SERVER-533
Jeśli ta „losowa” selekcja została wbudowana w przegląd indeksu zamiast gromadzenia identyfikatorów w tablicy, a następnie wybierania jednej, pomogłoby to niewiarygodnie. (idź głosuj w górę!)
źródło
Działa to dobrze, jest szybkie, działa z wieloma dokumentami i nie wymaga
rand
wypełniania pola, które ostatecznie zapełni się:ps. Jak znaleźć losowe rekordy w pytaniu mongodb jest oznaczony jako duplikat tego pytania. Różnica polega na tym, że kwestia ta wyraźnie prosi o pojedynczy rekord jako drugi wyraźnie o uzyskanie losowych dokumentu s .
źródło
Jeśli używasz mongoid, otoki dokumentu na obiekt, możesz wykonać następujące czynności w Ruby. (Zakładając, że twój model to Użytkownik)
W moim .irbrc mam
więc w konsoli szyn mogę zrobić na przykład
aby losowo pobierać dokumenty z dowolnej kolekcji.
źródło
możesz także użyć tablicy losowej po wykonaniu zapytania
var shuffle = wymagany („shuffle-array”);
Accounts.find (qry, funkcja (err, tablica wyników) {newIndexArr = shuffle (tablica wyników);
źródło
To, co działa wydajnie i niezawodnie, to:
Dodaj pole o nazwie „losowe” do każdego dokumentu i przypisz do niego losową wartość, dodaj indeks do pola losowego i postępuj w następujący sposób:
Załóżmy, że mamy zbiór linków o nazwie „linki” i chcemy z nich losowy link:
Aby mieć pewność, że ten sam link nie pojawi się po raz drugi, zaktualizuj jego losowe pole o nową liczbę losową:
źródło