Je cherche à obtenir un enregistrement aléatoire d'un énorme (100 millions d'enregistrements) mongodb
.
Quel est le moyen le plus rapide et le plus efficace de le faire? Les données sont déjà là et il n'y a aucun champ dans lequel je peux générer un nombre aléatoire et obtenir une ligne aléatoire.
Aucune suggestion?
mongodb
mongodb-query
Will M
la source
la source
Réponses:
À partir de la version 3.2 de MongoDB, vous pouvez obtenir N documents aléatoires à partir d'une collection à l'aide de l'
$sample
opérateur de pipeline d'agrégation:Si vous souhaitez sélectionner le ou les documents aléatoires dans un sous-ensemble filtré de la collection, ajoutez une
$match
étape au pipeline:Comme indiqué dans les commentaires, lorsque
size
est supérieur à 1, il peut y avoir des doublons dans l'échantillon de document renvoyé.la source
Effectuez un décompte de tous les enregistrements, générez un nombre aléatoire entre 0 et le décompte, puis procédez comme suit:
la source
Mise à jour pour MongoDB 3.2
3.2 a introduit $ sample dans le pipeline d'agrégation.
Il y a aussi un bon article de blog sur la mise en pratique.
Pour les anciennes versions (réponse précédente)
Il s'agissait en fait d'une demande de fonctionnalité: http://jira.mongodb.org/browse/SERVER-533 mais elle a été déposée sous "Ne sera pas corrigé".
Le livre de cuisine a une très bonne recette pour sélectionner un document au hasard dans une collection: http://cookbook.mongodb.org/patterns/random-attribute/
Pour paraphraser la recette, vous attribuez des numéros aléatoires à vos documents:
Sélectionnez ensuite un document aléatoire:
Interroger avec les deux
$gte
et$lte
est nécessaire pour trouver le document avec un nombre aléatoire le plus procherand
.Et bien sûr, vous voudrez indexer sur le champ aléatoire:
Si vous interrogez déjà un index, déposez-le simplement, ajoutez
random: 1
-le et ajoutez-le à nouveau.la source
$gte
est le premier. La solution alternative stackoverflow.com/a/9499484/79201 fonctionnerait mieux dans ce cas.Vous pouvez également utiliser la fonction d'indexation géospatiale de MongoDB pour sélectionner les documents «les plus proches» d'un nombre aléatoire.
Tout d'abord, activez l'indexation géospatiale sur une collection:
Pour créer un groupe de documents avec des points aléatoires sur l'axe X:
Ensuite, vous pouvez obtenir un document aléatoire de la collection comme ceci:
Ou vous pouvez récupérer plusieurs documents les plus proches d'un point aléatoire:
Cela ne nécessite qu'une seule requête et aucune vérification nulle, de plus le code est propre, simple et flexible. Vous pouvez même utiliser l'axe Y du géopoint pour ajouter une deuxième dimension de caractère aléatoire à votre requête.
la source
La recette suivante est un peu plus lente que la solution de livre de cuisine mongo (ajoutez une clé aléatoire sur chaque document), mais retourne des documents aléatoires plus uniformément répartis. Elle est un peu moins uniformément distribuée que la
skip( random )
solution, mais beaucoup plus rapide et plus sûre en cas de suppression de documents.Cela vous oblige également à ajouter un champ aléatoire "aléatoire" à vos documents, alors n'oubliez pas de l'ajouter lorsque vous les créez: vous devrez peut-être initialiser votre collection comme indiqué par Geoffrey
Résultats de référence
Cette méthode est beaucoup plus rapide que la
skip()
méthode (de ceejayoz) et génère des documents plus uniformément aléatoires que la méthode du "livre de cuisine" rapportée par Michael:Pour une collection avec 1 000 000 d'éléments:
Cette méthode prend moins d'une milliseconde sur ma machine
la
skip()
méthode prend 180 ms en moyenneLa méthode du livre de cuisine empêchera de sélectionner un grand nombre de documents, car leur nombre aléatoire ne leur est pas favorable.
Cette méthode sélectionnera tous les éléments de manière uniforme au fil du temps.
Dans mon indice de référence, il n'était que 30% plus lent que la méthode du livre de cuisine.
le caractère aléatoire n'est pas parfait à 100% mais il est très bon (et il peut être amélioré si nécessaire)
Cette recette n'est pas parfaite - la solution parfaite serait une fonction intégrée comme d'autres l'ont noté.
Cependant, ce devrait être un bon compromis à de nombreuses fins.
la source
Voici un moyen d'utiliser les
ObjectId
valeurs par défaut_id
et un peu de mathématiques et de logique.C'est la logique générale de la représentation shell et facilement adaptable.
Donc en points:
Rechercher les valeurs de clé primaire min et max dans la collection
Générez un nombre aléatoire qui se situe entre les horodatages de ces documents.
Ajoutez le nombre aléatoire à la valeur minimale et recherchez le premier document supérieur ou égal à cette valeur.
Cela utilise "padding" à partir de la valeur d'horodatage dans "hex" pour former une
ObjectId
valeur valide puisque c'est ce que nous recherchons. L'utilisation d'entiers comme_id
valeur est essentiellement plus simple mais la même idée de base dans les points.la source
En Python avec pymongo:
la source
count()
parestimated_document_count()
ascount()
est déconseillé dans Mongdo v4.2.Vous pouvez maintenant utiliser l'agrégat. Exemple:
Voir le doc .
la source
c'est difficile s'il n'y a pas de données à déconnecter. quels sont le champ _id? sont-ils des identifiants d'objet mongodb? Si oui, vous pouvez obtenir les valeurs les plus élevées et les plus basses:
alors si vous supposez que les identifiants sont distribués uniformément (mais ils ne le sont pas, mais au moins c'est un début):
la source
En utilisant Python (pymongo), la fonction d'agrégation fonctionne également.
Cette approche est beaucoup plus rapide que d'exécuter une requête pour un nombre aléatoire (par exemple collection.find ([random_int]). C'est particulièrement le cas pour les grandes collections.
la source
Vous pouvez choisir un horodatage aléatoire et rechercher le premier objet créé par la suite. Il ne numérisera qu'un seul document, bien qu'il ne vous donne pas nécessairement une distribution uniforme.
la source
Ma solution sur php:
la source
Afin d'obtenir un nombre déterminé de documents aléatoires sans doublons:
boucle obtenant un index aléatoire et sautant dupliqué
la source
Je suggérerais d'utiliser map / Reduce, où vous utilisez la fonction map pour n'émettre que lorsqu'une valeur aléatoire est supérieure à une probabilité donnée.
La fonction de réduction ci-dessus fonctionne car une seule touche ('1') est émise par la fonction de carte.
La valeur de la "probabilité" est définie dans la "portée", lors de l'appel de mapRreduce (...)
L'utilisation de mapReduce comme ceci devrait également être utilisable sur une base de données fragmentée.
Si vous souhaitez sélectionner exactement n de m documents dans la base de données, vous pouvez le faire comme ceci:
Où "countTotal" (m) est le nombre de documents dans la base de données et "countSubset" (n) est le nombre de documents à récupérer.
Cette approche pourrait poser certains problèmes sur les bases de données fragmentées.
la source
Vous pouvez choisir random _id et renvoyer l'objet correspondant:
Ici, vous n'avez pas besoin de dépenser de l'espace pour stocker des nombres aléatoires dans la collection.
la source
Je suggère d'ajouter un champ int aléatoire à chaque objet. Ensuite, vous pouvez simplement faire un
pour choisir un document aléatoire. Assurez-vous simplement que vous assurezIndex ({random_field: 1})
la source
Lorsque j'ai été confronté à une solution similaire, j'ai fait marche arrière et j'ai constaté que la demande commerciale visait en fait à créer une certaine forme de rotation de l'inventaire présenté. Dans ce cas, il existe de bien meilleures options, qui ont des réponses de moteurs de recherche comme Solr, pas de magasins de données comme MongoDB.
En bref, avec l'exigence de "faire pivoter intelligemment" le contenu, ce que nous devrions faire au lieu d'un nombre aléatoire dans tous les documents est d'inclure un modificateur de score q personnel. Pour l'implémenter vous-même, en supposant une petite population d'utilisateurs, vous pouvez stocker un document par utilisateur qui a le productId, le nombre d'impressions, le nombre de clics, la dernière date vue et tous les autres facteurs que l'entreprise trouve utiles pour calculer le score aq modificateur. Lors de la récupération de l'ensemble à afficher, vous demandez généralement plus de documents de la banque de données que ne le demande l'utilisateur final, puis appliquez le modificateur de score q, prenez le nombre d'enregistrements demandés par l'utilisateur final, puis randomisez la page de résultats, un tout petit défini, il vous suffit donc de trier les documents dans la couche application (en mémoire).
Si l'univers des utilisateurs est trop grand, vous pouvez classer les utilisateurs en groupes de comportements et indexer par groupe de comportements plutôt que par utilisateur.
Si l'univers des produits est suffisamment petit, vous pouvez créer un index par utilisateur.
J'ai trouvé que cette technique était beaucoup plus efficace, mais surtout plus efficace pour créer une expérience pertinente et utile de l'utilisation de la solution logicielle.
la source
aucune des solutions n'a bien fonctionné pour moi. surtout quand il y a beaucoup de lacunes et que le jeu est petit. cela a très bien fonctionné pour moi (en php):
la source
find
+skip
est assez mauvais, vous retournez tous les documents juste pour choisir un: S.Si vous utilisez des mangoustes, vous pouvez utiliser des mangoustes au hasard des mangoustes au hasard
la source
Mon tri / ordre PHP / MongoDB par solution RANDOM. J'espère que cela aide n'importe qui.
Remarque: J'ai des ID numériques dans ma collection MongoDB qui font référence à un enregistrement de base de données MySQL.
Je crée d'abord un tableau avec 10 nombres générés aléatoirement
Dans mon agrégation, j'utilise l'opérateur de pipeline $ addField combiné avec $ arrayElemAt et $ mod (module). L'opérateur de module me donnera un nombre de 0 à 9 que j'utilise ensuite pour choisir un nombre dans le tableau avec des nombres générés de manière aléatoire.
Après cela, vous pouvez utiliser le pipeline de tri.
la source
Si vous avez une clé d'identification simple, vous pouvez stocker tous les identifiants dans un tableau, puis choisir un identifiant aléatoire. (Réponse rubis):
la source
En utilisant Map / Reduce, vous pouvez certainement obtenir un enregistrement aléatoire, mais pas nécessairement très efficacement en fonction de la taille de la collection filtrée résultante avec laquelle vous finissez par travailler.
J'ai testé cette méthode avec 50000 documents (le filtre la réduit à environ 30000), et elle s'exécute en environ 400 ms sur un Intel i3 avec 16 Go de RAM et un disque dur SATA3 ...
La fonction Carte crée simplement un tableau des identifiants de tous les documents qui correspondent à la requête. Dans mon cas, j'ai testé cela avec environ 30 000 des 50 000 documents possibles.
La fonction Reduce choisit simplement un entier aléatoire entre 0 et le nombre d'éléments (-1) dans le tableau, puis renvoie cet _id dans le tableau.
400 ms sonne comme une longue période, et c'est vraiment le cas, si vous aviez cinquante millions d'enregistrements au lieu de cinquante mille, cela peut augmenter les frais généraux au point où ils deviennent inutilisables dans des situations multi-utilisateurs.
Il existe un problème ouvert pour MongoDB d'inclure cette fonctionnalité dans le noyau ... https://jira.mongodb.org/browse/SERVER-533
Si cette sélection "aléatoire" était intégrée dans une recherche d'index au lieu de collecter des identifiants dans un tableau, puis d'en sélectionner un, cela aiderait incroyablement. (allez voter!)
la source
Cela fonctionne bien, c'est rapide, fonctionne avec plusieurs documents et ne nécessite pas de
rand
champ de remplissage, qui finira par se remplir:ps. Comment trouver des enregistrements aléatoires dans la question mongodb est marqué comme doublon de cette question. La différence est que cette question demande explicitement de dossier unique que l'autre explicitement à obtenir le document au hasard s .
la source
Si vous utilisez mongoid, l'encapsuleur document-objet, vous pouvez effectuer les opérations suivantes dans Ruby. (En supposant que votre modèle est utilisateur)
Dans mon .irbrc, j'ai
donc dans la console des rails, je peux faire, par exemple,
pour obtenir des documents au hasard dans n'importe quelle collection.
la source
vous pouvez également utiliser le shuffle-array après avoir exécuté votre requête
var shuffle = require ('shuffle-array');
Accounts.find (qry, function (err, results_array) {newIndexArr = shuffle (results_array);
la source
Ce qui fonctionne de manière efficace et fiable est le suivant:
Ajoutez un champ appelé "aléatoire" à chaque document et attribuez-lui une valeur aléatoire, ajoutez un index pour le champ aléatoire et procédez comme suit:
Supposons que nous ayons une collection de liens Web appelés "liens" et que nous en voulions un lien aléatoire:
Pour vous assurer que le même lien n'apparaîtra pas une deuxième fois, mettez à jour son champ aléatoire avec un nouveau nombre aléatoire:
la source