MongoDB Map/reduce Tableau d'agrégation question
J'ai une collection de MongoDB, dont les docs utiliser plusieurs niveaux d'imbrication, dont je voudrais extraire un tableau multidimensionnel compilé à partir d'un sous-ensemble de leurs champs. J'ai une solution qui fonctionne pour moi, mais je veux mieux comprendre ce concept de "idempotence" et ses conséquences liées à la fonction de réduction.
{
"host_name" : "gateway",
"service_description" : "PING",
"last_update" : 1305777787,
"performance_object" : [
[ "rta", 0.105, "ms", 100, 500, 0 ],
[ "pl", 0, "%", 20, 60, 0 ]
]
}
Et voici la map/reduce fonctions
var M = function() {
var hn = this.host_name,
sv = this.service_description,
ts = this.last_update;
this.performance_object.forEach(function(P){
emit( {
host: hn,
service: sv,
metric: P[0]
}, {
time: ts,
value: P[1]
} );
});
}
var R = function(key,values) {
var result = {
time: [],
value: []
};
values.forEach(function(V){
result.time.push(V.time);
result.value.push(V.value);
});
return result;
}
db.runCommand({
mapreduce: <colname>,
out: <col2name>,
map: M,
reduce: R
});
Les données sont renvoyées dans une structure utile, j'ai reformater/tri à finaliser pour la représentation graphique.
{
"_id" : {
"host" : "localhost",
"service" : "Disk Space",
"metric" : "/var/bck"
},
"value" : {
"time" : [
[ 1306719302, 1306719601, 1306719903, ... ],
[ 1306736404, 1306736703, 1306737002, ... ],
[ 1306766401, 1306766701, 1306767001, ... ]
],
"value" : [
[ 122, 23423, 25654, ... ],
[ 336114, 342511, 349067, ... ],
[ 551196, 551196, 551196, ... ]
]
}
}
Enfin...
[ [1306719302,122], [1306719601,23423], [1306719903,25654], ... ]
TL;DR: Quel est le comportement attendu avec l'observés "chunking" du tableau des résultats?
Je comprends que la fonction de réduction peut être appelée plusieurs fois sur array(s) d'émissions de valeurs, c'est pourquoi il y a plusieurs "morceaux" de l'complet des tableaux, plutôt que d'un seul tableau. Le tableau morceaux sont généralement de 25 à 50 éléments et il est assez facile de nettoyer cette dans finalize(). Je concat() les tableaux, interleave comme [le temps,la valeur] et de tri. Mais ce que je veux vraiment savoir c'est si cela peut devenir plus complexe:
1) Est le chunking observée à cause de mon code, MongoDB la mise en œuvre ou de la Carte/Réduire l'algorithme lui-même?
2) Sera-t-il jamais être plus profond (récursive) de nidification de la matrice de morceaux dans fragmenté configurations ou même simplement à cause de ma mise en place précipitée? Ce serait briser la méthode concat ().
3) Est-il simplement d'une meilleure stratégie pour obtenir des résultats tableau comme indiqué ci-dessus?
EDIT: Modifié d'émettre des tableaux:
J'ai pris de Thomas conseiller et ré-écrit-il à émettre des tableaux. Il n'est absolument pas de sens de séparer les valeurs.
var M = function() {
var hn = this.host_name,
sv = this.service_description,
ts = this.last_update;
this.performance_object.forEach(function(P){
emit( {
host: hn,
service: sv,
metric: P[0]
}, {
value: [ ts, P[1] ]
} );
});
}
var R = function(key,values) {
var result = {
value: []
};
values.forEach(function(V){
result.value.push(V.value);
});
return result;
}
db.runCommand({
mapreduce: <colname>,
out: <col2name>,
map: M,
reduce: R
});
Maintenant la sortie est similaire à ceci:
{
"_id" : {
"host" : "localhost",
"service" : "Disk Space",
"metric" : "/var/bck"
},
"value" : {
"value" : [
[ [1306736404,336114],[1306736703,342511],[1306737002,349067], ... ],
[ [1306766401,551196],[1306766701,551196],[1306767001,551196], ... ],
[ [1306719302,122],[1306719601,122],[1306719903,122], ... ]
]
}
}
Et j'ai utilisé cette finaliser la fonction de concaténation de la matrice de morceaux et de les trier.
...
var F = function(key,values) {
return (Array.concat.apply([],values.value)).sort(function(a,b){
if (a[0] < b[0]) return -1;
if (a[0] > b[0]) return 1;
return 0;
});
}
db.runCommand({
mapreduce: <colname>,
out: <col2name>,
map: M,
reduce: R,
finalize: F
});
Qui fonctionne très bien:
{
"_id" : {
"host" : "localhost",
"service" : "Disk Space",
"metric" : "/mnt/bck"
},
"value" : [ [1306719302,122],[1306719601,122],[1306719903,122],, ... ]
}
Je suppose que la seule question qui taraude à moi est de savoir si ce Tableau.concat.appliquer([],des valeurs.valeur) peut être digne de confiance pour nettoyer la sortie de réduire tout le temps.
DERNIÈRE ÉDITION: Beaucoup plus simple...
J'ai modifié la structure du document depuis le premier exemple donné ci-dessus, mais cela ne change l'exemple en faisant de la fonction map vraiment simple.
Je suis encore à essayer d'envelopper mon cerveau autour de laquelle la Matrice.le prototype.push.appliquer(résultat, V. de données) fonctionne de manière très différente de résultat.push(V. données)... mais ça marche.
var M = function() {
emit( {
host: this.host,
service: this.service,
metric: this.metric
} , {
data: [ [ this.timestamp, this.data ] ]
} );
}
var R = function(key,values) {
var result = [];
values.forEach(function(V){
Array.prototype.push.apply(result, V.data);
});
return { data: result };
}
var F = function(key,values) {
return values.data.sort(function(a,b){
return (a[0]<b[0]) ? -1 : (a[0]>b[0]) ? 1 : 0;
});
}
Il a la même sortie, comme indiqué juste au-dessus de la DERNIÈRE ÉDITION de la rubrique.
Merci, Thomas!
OriginalL'auteur jcampbelly | 2011-06-10
Vous devez vous connecter pour publier un commentaire.
La "chunking" vient de ton code: votre fonction de réduction de valeurs du paramètre peut contenir soit
{time:<timestamp>,value:<value>}
émis à partir de votre carte de fonction, ou{time:[<timestamps>],value:[<values]}
retourné à partir d'un précédent appel à votre fonction de réduction.Je ne sais pas si elle arrivera dans la pratique, mais il peut se produire en théorie.
N'ont tout simplement votre carte de fonction émettent le même type d'objets que votre réduire les retours de fonction, c'est à dire
emit(<id>, {time: [ts], value: [P[1]]})
, et de changer votre fonction de réduction en conséquence, c'est à direArray.push.apply(result.time, V.time)
et de même pourresult.value
.Bien en fait, je ne comprends pas pourquoi vous n'êtes pas à l'aide d'un tableau de temps/couples de valeurs, au lieu d'une paire de tableaux, c'est à dire
emit(<id>, { pairs: [ {time: ts, value: P[1] ] })
ouemit(<id>, { pairs: [ [ts, P[1]] ] })
dans la fonction map, etArray.push.apply(result.pairs, V.pairs)
dans la fonction de réduction. De cette façon, vous n'aurez même pas besoin de le finaliser la fonction (sauf peut-être pour "ouvrir" le tableau de la paires bien: parce que la fonction de réduction ne peut pas retourner un tableau, votre avez de l'envelopper dans un objet)Vous avez fait exactement la même erreur que précédemment: ce que vous émettez dans votre fonction map est différent de ce que vous retournez dans votre fonction de réduction: l'un contient une "paire", tandis que l'autre dispose d'un tableau de paires. Bâton avec exactement le même "schéma" et vous n'aurez pas de problème, et pas de tableau imbriqué.
Je vais donner un coup de cette dès que je reçois une chance: emit( { host: hn, service: sv, métrique: P[0] }, { valeur: [ [ ts, P[1] ] ] } );
OriginalL'auteur Thomas Broyer