A estrutura de dados aqui não é uma boa implementação, há muitos problemas com a forma como ela é estruturada e é completamente inadequada para agregação. Os principais problemas aqui são:
-
Sua estrutura na verdade não usa nenhum array, agora não
-
Todos os nomes de chave específicos são um problema real, e isso pode ser evitado.
Como tal, a única maneira de percorrer esse tipo de estrutura é usando JavaScript com mapReduce.
Definindo um mapeador:
var mapper = function () {
for ( var n in this.versions ) {
for ( var k in this.versions[n].content ) {
if (
( k != 'confirmed' ) ||
( k != 'visited' ) )
emit(
{
type: this.chairtype,
key: k
},
this.versions[n].content[k]
);
}
}
};
Então, o que isso está fazendo é percorrer cada uma das entradas das versões e também por tudo no conteúdo. A chave é emitida para cada uma das chaves de conteúdo desejadas, bem como pela chave "chairtype". E o valor é esse valor correspondente.
E então um redutor:
var reducer = function (key,values) {
return ( Array.sum( values ) != 0 )
? Array.sum( values ) / values.length : 0;
};
Que é apenas uma maneira simples de produzir uma média de todos os valores que chegam para o mapeador com a mesma chave.
Então, embora isso deva funcionar bem, o que você deve fazer é mudar sua estrutura. Então, de fato, se você tivesse algo assim:
{
"_id": ObjectId("52b85dfa32b6249513f15897"),
"parent": "47de3176-bbc3-44e0-8063-8920ac56fdc8",
"type": "chair",
"chairtype": "E",
"content": [
{ "key": "atkswlntfd", "value": 0, "version": 0 },
{ "key": "auwbsjqzir", "value": 0, "version": 0 },
{ "key": "avqrnjzbgd", "value": 0, "version": 0 }
]
}
Ou geralmente mais ou menos dessa forma, a operação de agregação se torna muito simples:
db.collection.aggregate([
{ "$unwind": "$content" },
{ "$group": {
"_id": {
"chairtype": "$chairtype",
"key": "$content.key"
},
"average": { "$avg": "$content.value" }
}}
])
Ou qualquer outra variação disso é necessária, mas agora é possível alterando a estrutura.
Portanto, sem que o documento seja estruturado de maneira diferente, você precisará usar mapReduce para fazer isso.