2013-06-12 3 views
1

Tout d'abord, désolé pour mon mauvais anglais.Mongodb agrégat sur filtre comme sous-document

Si nous avons ci-dessous les documents à MongoDB,

Les données de test

{id:1, filter:{f1:'v1-1', f2:'v2-1', f3:['v3-1', 'v3-3']}} 
{id:2, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-2', 'v3-3']}} 
{id:3, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-1', 'v3-3']}} 

Préparer la collection

db.test.drop() 
db.test.insert({id:1, filter:{f1:'v1-1', f2:'v2-1', f3:['v3-1', 'v3-3']}}) 
db.test.insert({id:2, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-2', 'v3-3']}}) 
db.test.insert({id:3, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-1', 'v3-3']}}) 

Vous pouvez considérer le champ de filtre en fonction de filtre utilisé sur de nombreux sites de shopping, par exemple, ils vous diront combien LE D téléviseurs et combien de téléviseurs LCD sur le site. Je veux utiliser MongoDB pour calculer combien de documents avec chaque option de filtre (inclure chaque élément dans le champ tableau), le résultat attendu est comme ci-dessous.

Résultat attendu

[ 
    { 
     _id : { key: 'f1', value: 'v1-1' }, count: 3 
    }, 
    { 
     _id : { key: 'f2', value: 'v2-1' }, count: 1 
    }, 
    { 
     _id : { key: 'f2', value: 'v2-2' }, count: 2 
    }, 
    { 
     _id : { key: 'f3', value: 'v3-1' }, count: 2 
    }, 
    { 
     _id : { key: 'f3', value: 'v3-2' }, count: 1 
    }, 
    { 
     _id : { key: 'f3', value: 'v3-3' }, count: 3 
    } 
] 

Il est facile d'obtenir le résultat en utilisant la carte/réduire,

Map/Reduce solution

map = function() { 
    for (k in this.filter) { 
     if (this.filter[k] instanceof Array) { 
      for (j in this.filter[k]) { 
       emit({ key: k, value: this.filter[k][j]}, 1); 
      } 
     } else { 
      emit({ key: k, value: this.filter[k]}, 1); 
     } 
    } 
} 

reduce = function (k, values) { 
    result = 0; 
    values.forEach(function(v) { result += v; }); 
    return result; 
} 

db.test.mapReduce(map, reduce, {out:{inline:1}}) 

Mais comme le problème de performance map/reduce, il ne peut pas être utilisé pour une requête en temps réel. et le jeu de résultats peut être modifié si j'ajoute des conditions de requête, donc je ne peux pas enregistrer la carte/réduire le résultat dans une autre collection pour une requête en temps réel.

Et je peux utiliser le cadre global pour calculer le nombre d'un filtre,

solution globale pour un seul filtre

db.test.aggregate(
{$project: {"filter.f2":1, "_id":0}}, 
{$group: {"_id": {"key": {$ifNull: [null, "f2"]}, "value":"$filter.f2"}, "count" : {$sum: 1}}} 
) 

[ 
    { 
     "_id" : { "key" : "f2", "value" : "v2-2" }, "count" : 2 
    }, 
    { 
     "_id" : { "key" : "f2", "value" : "v2-1" }, "count" : 1 
    } 
] 

Mais je ne sais pas comment faire pour tout filtre options Une idée?

Répondre

1

Si vous modifiez la structure de données à quelque chose comme cela, notez que toutes les valeurs sont des tableaux, même ceux avec des valeurs simples:

{ 
    _id: 1, 
    filters: [{ 
    key: 'f1', 
    values: ['v1-1'] 
    },{ 
    key: 'f2', 
    values: ['v2-1'] 
    },{ 
    key: 'f3', 
    values: ['v3-1', 'v3-3'] 
    }] 
} 
{ 
    _id: 2, 
    filters: [{ 
    key: 'f1', 
    values: ['v1-1'] 
    },{ 
    key: 'f2', 
    values: ['v2-2'] 
    },{ 
    key: 'f3', 
    values: ['v3-2', 'v3-3'] 
    }] 
} 
{ 
    _id: 3, 
    filters: [{ 
    key: 'f1', 
    values: ['v1-1'] 
    },{ 
    key: 'f2', 
    values: ['v2-2'] 
    },{ 
    key: 'f3', 
    values: ['v3-1', 'v3-3'] 
    }] 
} 

Vous pouvez faire une fonction d'agrégation quelque chose comme ceci:

db.test.aggregate({ 
    $unwind: "$filters" 
},{ 
    $project: { 
    _id: 1, 
    key: "$filters.key", 
    values: "$filters.values" 
    } 
},{ 
    $unwind: "$values" 
},{ 
    $group: { 
    _id: { 
     $concat: ["$key","|","$values"] 
    }, 
    count: { $sum: 1 } 
    } 
}) 

Vous pourriez probablement passer l'étape du projet si vous voulez, je l'ai juste mis là comme une délicatesse. Vous aurez besoin de deux déroulements, peu importe quoi.

+0

Bonne réponse, merci! J'utilise mongodb 2.2, donc j'ai besoin d'utiliser '_id: {key:" $ key ", valeurs:" $ values ​​"}' dans la section $ group. – jxie