/ / MongoDB, MapReduce et tri - mongodb, mongodb-php, mongodb-query

MongoDB, MapReduce et tri - mongodb, mongodb-php, mongodb-query

Je suis peut-être un peu au-dessus de ma tête à ce sujet car j'apprends toujours les tenants et les aboutissants de MongoDB, mais voilà.

En ce moment, je travaille sur un outil de recherche / filtragedans un ensemble de données, triez-le par un point de données arbitraire (par exemple, popularité), puis regroupez-le par un identifiant. La seule façon dont je vois que je peux le faire est grâce à la fonctionnalité MapReduce de Mongo.

Je ne peux pas utiliser .group () car je travaille avec plus de 10 000 clés et je dois également pouvoir trier l'ensemble de données.

Mon code MapReduce fonctionne très bien, sauf pour une chose: le tri. Le tri ne veut tout simplement pas fonctionner du tout.

db.runCommand ({ "mapreduce": "produits", "map": fonction () { émettre({ product_id: this.product_id, popularité: this.popularity }, 1); }, "réduire": fonction (clé, valeurs) { var sum = 0; values.forEach (fonction (v) { somme + = v; });  somme de retour; }, "requête": {category_id: 20}, "out": {inline: 1}, "sort": {popularité: -1} });

J'ai déjà un index décroissant sur le point de données de popularité, donc ça ne fonctionne vraiment pas à cause d'un manque de cela:

{"v": 1, "clé": {"popularité": -1}, "ns": "app.products", "nom": "popularité_-1"}

Je n'arrive pas à comprendre pourquoi il ne veut pas trier.

Au lieu d'insérer le jeu de résultats, je ne peux pas le sortir vers une autre collection, puis exécuter un .find (). Sort ({popularité: -1}) à ce sujet en raison de la façon dont cette fonctionnalité va fonctionner.

Réponses:

14 pour la réponse № 1

Tout d'abord, Mongo map / Reduce n'est pas conçu pour être utilisé comme outil de requête (comme c'est le cas dans CouchDB), il est conçu pour vous permettre d'exécuter des tâches d'arrière-plan. Je l'utilise au travail pour analyser les données de trafic.

Ce que vous faites mal cependant, c'est que vous "appliquez le sort () à votre entrée, mais cela est inutile car lorsque le map() étape se fait les documents intermédiaires sont triés par chacun keys. Parce que votre clé est un document, elle est triée par product_id, popularity.

Voici comment j'ai généré mon jeu de données

function generate_dummy_data() {
for (i=2; i < 1000000; i++) {
db.foobar.save({
_id: i,
category_id: parseInt(Math.random() * 30),
popularity:    parseInt(Math.random() * 50)
})
}
}

Et ceci ma tâche de carte / réduire:

var data = db.runCommand({
"mapreduce": "foobar",
"map": function() {
emit({
sorting: this.popularity * -1,
product_id: this._id,
popularity: this.popularity,
}, 1);
},
"reduce": function(key, values) {
var sum = 0;
values.forEach(function(v) {
sum += v;
});

return sum;
},
"query": {category_id: 20},
"out": {inline: 1},
});

Et voici le résultat final (très long pour le coller ici):

http://cesarodas.com/results.txt

Cela fonctionne parce que maintenant nous trions par sorting, product_id, popularity. Vous pouvez jouer avec le tri comme bon vous semble, rappelez-vous simplement que le tri final se fait par key quel que soit le mode de tri de votre saisie.

Quoi qu'il en soit, comme je l'ai dit avant, vous devriez éviter de fairerequêtes avec Map / Reduce, il a été conçu pour le traitement en arrière-plan. Si j'étais vous, je concevrais mes données de manière à pouvoir y accéder avec des requêtes simples, il y a toujours un compromis dans ce cas, des insertions / mises à jour complexes pour avoir des requêtes simples (c'est ainsi que je vois MongoDB).


8 pour la réponse № 2

Comme indiqué lors de la discussion sur la question initiale:

  • Map / Reduce avec sortie en ligne ne peut actuellement pas utiliser de sort clé (voir SERVER-3973). Les solutions de contournement possibles incluent la confiance dans l'ordre des clés émises (voir la réponse de @crodas), la sortie vers une collection et l'interrogation de cette collection avec l'ordre de tri, ou le tri des résultats dans votre application en utilisant quelque chose comme usort ().

  • La préférence de l'OP est pour les résultats en ligne plutôt que de créer / supprimer des collections temporaires.

  • le Cadre d'agrégation dans MongoDB 2.2 (actuellement une version de production candidate) fournirait une solution appropriée.

Voici un exemple de requête similaire à la carte / réduction d'origine, mais utilisant à la place le cadre d'agrégation:

db.products.aggregate(
{ $match: { category_id: 20 }},
{ $group : {
_id : "$product_id",
"popularity" : { $sum : "$popularity" },
}},
{ $sort: { "popularity": -1 }}
)

.. et exemple de sortie:

{
"result" : [
{
"_id" : 50,
"popularity" : 139
},
{
"_id" : 150,
"popularity" : 99
},
{
"_id" : 123,
"popularity" : 55
}
],
"ok" : 1
}