Ich suche nach einer zufälligen Aufzeichnung von einer riesigen (100 Millionen Aufzeichnung) mongodb
.
Was ist der schnellste und effizienteste Weg, dies zu tun? Die Daten sind bereits vorhanden und es gibt kein Feld, in dem ich eine Zufallszahl generieren und eine Zufallszeile erhalten kann.
Irgendwelche Vorschläge?
mongodb
mongodb-query
Will M.
quelle
quelle
Antworten:
Ab der Version 3.2 von MongoDB können Sie mithilfe des
$sample
Aggregationspipeline-Operators N zufällige Dokumente aus einer Sammlung abrufen :Wenn Sie die zufälligen Dokumente aus einer gefilterten Teilmenge der Sammlung auswählen möchten, stellen Sie
$match
der Pipeline eine Stufe voran :Wie in den Kommentaren angegeben,
size
kann das zurückgegebene Dokumentmuster Duplikate enthalten , wenn es größer als 1 ist.quelle
Zählen Sie alle Datensätze, generieren Sie eine Zufallszahl zwischen 0 und der Zählung und führen Sie dann Folgendes aus:
quelle
Update für MongoDB 3.2
3.2 führte $ sample in die Aggregationspipeline ein.
Es gibt auch einen guten Blog-Beitrag zur praktischen Umsetzung.
Für ältere Versionen (vorherige Antwort)
Dies war eigentlich eine Feature-Anfrage: http://jira.mongodb.org/browse/SERVER-533, aber sie wurde unter "Wird nicht repariert" abgelegt.
Das Kochbuch enthält ein sehr gutes Rezept, um ein zufälliges Dokument aus einer Sammlung auszuwählen: http://cookbook.mongodb.org/patterns/random-attribute/
Um das Rezept zu paraphrasieren, weisen Sie Ihren Dokumenten Zufallszahlen zu:
Wählen Sie dann ein zufälliges Dokument aus:
Abfrage mit beiden
$gte
und$lte
ist erforderlich, um das Dokument mit einer Zufallszahl am nächsten zu findenrand
.Und natürlich möchten Sie das Zufallsfeld indizieren:
Wenn Sie bereits einen Index abfragen, legen Sie ihn einfach ab, hängen
random: 1
Sie ihn an und fügen Sie ihn erneut hinzu.quelle
$gte
es das erste ist. Alternative Lösung stackoverflow.com/a/9499484/79201 würde in diesem Fall besser funktionieren.Sie können auch die Geo-Indizierungsfunktion von MongoDB verwenden, um die Dokumente auszuwählen, die einer Zufallszahl am nächsten liegen.
Aktivieren Sie zunächst die Geodatenindizierung für eine Sammlung:
So erstellen Sie eine Reihe von Dokumenten mit zufälligen Punkten auf der X-Achse:
Dann können Sie ein zufälliges Dokument aus der Sammlung wie folgt erhalten:
Oder Sie können mehrere Dokumente abrufen, die einem zufälligen Punkt am nächsten liegen:
Dies erfordert nur eine Abfrage und keine Nullprüfungen. Außerdem ist der Code sauber, einfach und flexibel. Sie können sogar die Y-Achse des Geopunkts verwenden, um Ihrer Abfrage eine zweite Zufallsdimension hinzuzufügen.
quelle
Das folgende Rezept ist etwas langsamer als die Mongo-Kochbuchlösung (fügen Sie jedem Dokument einen zufälligen Schlüssel hinzu), gibt jedoch gleichmäßigere zufällige Dokumente zurück. Es ist etwas weniger gleichmäßig verteilt als die
skip( random )
Lösung, aber viel schneller und ausfallsicherer, wenn Dokumente entfernt werden.Außerdem müssen Sie Ihren Dokumenten ein zufälliges "zufälliges" Feld hinzufügen. Vergessen Sie also nicht, dieses Feld beim Erstellen hinzuzufügen: Möglicherweise müssen Sie Ihre Sammlung wie von Geoffrey gezeigt initialisieren
Benchmark-Ergebnisse
Diese Methode ist viel schneller als die
skip()
Methode (von Ceejayoz) und generiert gleichmäßigere zufällige Dokumente als die von Michael berichtete "Kochbuch" -Methode:Für eine Sammlung mit 1.000.000 Elementen:
Diese Methode dauert auf meinem Computer weniger als eine Millisekunde
Die
skip()
Methode dauert durchschnittlich 180 msDie Kochbuchmethode führt dazu, dass eine große Anzahl von Dokumenten nie ausgewählt wird, da ihre Zufallszahl sie nicht bevorzugt.
Diese Methode wählt alle Elemente im Laufe der Zeit gleichmäßig aus.
In meinem Benchmark war es nur 30% langsamer als die Kochbuchmethode.
Die Zufälligkeit ist nicht 100% perfekt, aber sie ist sehr gut (und kann bei Bedarf verbessert werden).
Dieses Rezept ist nicht perfekt - die perfekte Lösung wäre eine integrierte Funktion, wie andere angemerkt haben.
Es sollte jedoch für viele Zwecke ein guter Kompromiss sein.
quelle
Hier ist eine Möglichkeit, die Standardwerte
ObjectId
für_id
und ein wenig Mathematik und Logik zu verwenden.Das ist die allgemeine Logik in der Shell-Darstellung und leicht anpassbar.
Also in Punkten:
Suchen Sie die minimalen und maximalen Primärschlüsselwerte in der Sammlung
Generieren Sie eine Zufallszahl, die zwischen den Zeitstempeln dieser Dokumente liegt.
Addieren Sie die Zufallszahl zum Mindestwert und suchen Sie das erste Dokument, das größer oder gleich diesem Wert ist.
Dies verwendet "Auffüllen" aus dem Zeitstempelwert in "hex", um einen gültigen
ObjectId
Wert zu bilden , da dies das ist, wonach wir suchen. Die Verwendung von Ganzzahlen als_id
Wert ist wesentlich einfacher, aber die gleiche Grundidee in den Punkten.quelle
In Python mit Pymongo:
quelle
count()
mit ,estimated_document_count()
wiecount()
in Mongdo v4.2 ist veraltet.Jetzt können Sie das Aggregat verwenden. Beispiel:
Siehe das Dokument .
quelle
Es ist schwierig, wenn dort keine Daten zum Abschlüsseln vorhanden sind. Was sind die _id Felder? Sind sie Mongodb-Objekt-IDs? Wenn ja, könnten Sie die höchsten und niedrigsten Werte erhalten:
Wenn Sie dann annehmen, dass die IDs gleichmäßig verteilt sind (aber nicht, aber zumindest ist es ein Anfang):
quelle
Mit Python (Pymongo) funktioniert auch die Aggregatfunktion.
Dieser Ansatz ist viel schneller als das Ausführen einer Abfrage für eine Zufallszahl (z. B. collection.find ([random_int]). Dies gilt insbesondere für große Sammlungen.
quelle
Sie können einen zufälligen Zeitstempel auswählen und nach dem ersten Objekt suchen, das anschließend erstellt wurde. Es wird nur ein einzelnes Dokument gescannt, obwohl dies nicht unbedingt eine gleichmäßige Verteilung ergibt.
quelle
Meine Lösung auf PHP:
quelle
Um eine bestimmte Anzahl zufälliger Dokumente ohne Duplikate zu erhalten:
Schleife, die zufälligen Index erhält und dupliziert überspringt
quelle
Ich würde vorschlagen, map / redu zu verwenden, wobei Sie die Map-Funktion verwenden, um nur zu emittieren, wenn ein zufälliger Wert über einer bestimmten Wahrscheinlichkeit liegt.
Die obige Reduktionsfunktion funktioniert, da nur eine Taste ('1') von der Kartenfunktion ausgegeben wird.
Der Wert der "Wahrscheinlichkeit" wird beim Aufrufen von mapRreduce (...) im "Bereich" definiert.
Die Verwendung von mapReduce wie diesem sollte auch für eine Sharded-Datenbank verwendbar sein.
Wenn Sie genau n von m Dokumenten aus der Datenbank auswählen möchten, können Sie dies folgendermaßen tun:
Dabei ist "countTotal" (m) die Anzahl der Dokumente in der Datenbank und "countSubset" (n) die Anzahl der abzurufenden Dokumente.
Dieser Ansatz kann bei Sharded-Datenbanken zu Problemen führen.
quelle
Sie können zufällige _id auswählen und das entsprechende Objekt zurückgeben:
Hier müssen Sie keinen Platz für das Speichern von Zufallszahlen in der Sammlung aufwenden.
quelle
Ich würde vorschlagen, jedem Objekt ein zufälliges int-Feld hinzuzufügen. Dann kannst du einfach eine machen
ein zufälliges Dokument auswählen. Stellen Sie einfach sicher, dass Sie den Index sicherstellen ({random_field: 1}).
quelle
Als ich mit einer ähnlichen Lösung konfrontiert wurde, ging ich zurück und stellte fest, dass die Geschäftsanforderung tatsächlich dazu diente, eine Art Rotation des präsentierten Inventars zu erstellen. In diesem Fall gibt es viel bessere Optionen, die Antworten von Suchmaschinen wie Solr haben, nicht von Datenspeichern wie MongoDB.
Kurz gesagt, mit der Anforderung, Inhalte "intelligent zu drehen", sollten wir anstelle einer Zufallszahl in allen Dokumenten einen persönlichen q-Score-Modifikator einfügen. Um dies selbst zu implementieren, können Sie unter der Annahme einer kleinen Anzahl von Benutzern ein Dokument pro Benutzer speichern, das die Produkt-ID, die Anzahl der Impressionen, die Anzahl der Klicks, das Datum der letzten Anzeige und alle anderen Faktoren enthält, die das Unternehmen für die Berechnung der aq-Bewertung als sinnvoll erachtet Modifikator. Wenn Sie den anzuzeigenden Satz abrufen, fordern Sie normalerweise mehr Dokumente aus dem Datenspeicher an, als vom Endbenutzer angefordert wurden. Wenden Sie dann den Modifikator q score an, nehmen Sie die Anzahl der vom Endbenutzer angeforderten Datensätze und ordnen Sie die Ergebnisseite nach dem Zufallsprinzip zu So sortieren Sie einfach die Dokumente in der Anwendungsschicht (im Speicher).
Wenn das Benutzeruniversum zu groß ist, können Sie Benutzer in Verhaltensgruppen einteilen und nach Verhaltensgruppen und nicht nach Benutzern indizieren.
Wenn das Produktuniversum klein genug ist, können Sie pro Benutzer einen Index erstellen.
Ich habe festgestellt, dass diese Technik viel effizienter, aber vor allem effektiver ist, um eine relevante und lohnende Erfahrung mit der Verwendung der Softwarelösung zu schaffen.
quelle
Keine der Lösungen hat bei mir gut funktioniert. Besonders wenn es viele Lücken gibt und die Menge klein ist. das hat bei mir sehr gut funktioniert (in php):
quelle
find
+skip
ist ziemlich schlecht, Sie geben alle Dokumente zurück, nur um eines auszuwählen: S.Wenn Sie Mungo verwenden, können Sie Mungo-Zufall Mungo-Zufall verwenden
quelle
Meine PHP / MongoDB-Sortierung / Bestellung nach RANDOM-Lösung. Hoffe das hilft jedem.
Hinweis: Ich habe numerische IDs in meiner MongoDB-Sammlung, die auf einen MySQL-Datenbankdatensatz verweisen.
Zuerst erstelle ich ein Array mit 10 zufällig generierten Zahlen
In meiner Aggregation verwende ich den Pipeline-Operator $ addField in Kombination mit $ arrayElemAt und $ mod (Modul). Der Moduloperator gibt mir eine Zahl von 0 bis 9, mit der ich dann eine Zahl aus dem Array mit zufällig generierten Zahlen auswähle.
Danach können Sie die Sortierpipeline verwenden.
quelle
Wenn Sie einen einfachen ID-Schlüssel haben, können Sie alle IDs in einem Array speichern und dann eine zufällige ID auswählen. (Ruby Antwort):
quelle
Mit Map / Reduce können Sie sicherlich einen zufälligen Datensatz erhalten, der jedoch nicht unbedingt sehr effizient ist, abhängig von der Größe der resultierenden gefilterten Sammlung, mit der Sie am Ende arbeiten.
Ich habe diese Methode mit 50.000 Dokumenten getestet (der Filter reduziert sie auf ungefähr 30.000) und sie wird in ungefähr 400 ms auf einem Intel i3 mit 16 GB RAM und einer SATA3-Festplatte ausgeführt ...
Die Map-Funktion erstellt einfach ein Array der IDs aller Dokumente, die der Abfrage entsprechen. In meinem Fall habe ich dies mit ungefähr 30.000 der 50.000 möglichen Dokumente getestet.
Die Reduce-Funktion wählt einfach eine zufällige Ganzzahl zwischen 0 und der Anzahl der Elemente (-1) im Array aus und gibt dann diese _id aus dem Array zurück.
400 ms klingen nach einer langen Zeit, und wenn Sie fünfzig statt fünfzigtausend Datensätze hatten, kann dies den Overhead so weit erhöhen, dass er in Mehrbenutzersituationen unbrauchbar wird.
Es gibt ein offenes Problem für MongoDB, diese Funktion in den Kern aufzunehmen ... https://jira.mongodb.org/browse/SERVER-533
Wenn diese "zufällige" Auswahl in eine Indexsuche integriert würde, anstatt IDs in einem Array zu sammeln und dann eine auszuwählen, würde dies unglaublich helfen. (Geh und stimme ab!)
quelle
Dies funktioniert gut, ist schnell, funktioniert mit mehreren Dokumenten und erfordert kein Ausfüllen von
rand
Feldern, die sich schließlich selbst ausfüllen:ps. Wie man zufällige Datensätze in der Mongodb- Frage findet, wird als Duplikat dieser Frage markiert. Der Unterschied besteht darin , dass diese Frage ausdrücklich zu einzelnen Datensatz wie der andere fragt explizit über zufälliges Dokument bekommen s .
quelle
Wenn Sie mongoid, den Dokument-zu-Objekt-Wrapper, verwenden, können Sie in Ruby Folgendes tun. (Angenommen, Ihr Modell ist Benutzer)
In meinem .irbrc habe ich
So kann ich in der Rails-Konsole zum Beispiel Folgendes tun:
um Dokumente zufällig aus einer Sammlung zu erhalten.
quelle
Sie können Shuffle-Array auch verwenden, nachdem Sie Ihre Abfrage ausgeführt haben
var shuffle = require ('shuffle-array');
Accounts.find (qry, function (err, results_array) {newIndexArr = shuffle (results_array);
quelle
Was effizient und zuverlässig funktioniert, ist Folgendes:
Fügen Sie jedem Dokument ein Feld mit dem Namen "zufällig" hinzu und weisen Sie ihm einen zufälligen Wert zu. Fügen Sie einen Index für das zufällige Feld hinzu und gehen Sie wie folgt vor:
Nehmen wir an, wir haben eine Sammlung von Weblinks, die als "Links" bezeichnet werden, und wir möchten einen zufälligen Link daraus:
Aktualisieren Sie das Zufallsfeld mit einer neuen Zufallszahl, um sicherzustellen, dass derselbe Link nicht ein zweites Mal angezeigt wird:
quelle