Não é possível fazer o tipo de cálculo que você está descrevendo com a estrutura de agregação - e não porque não há
$unwind
método para não matrizes. Mesmo se os objetos person:value fossem documentos em uma matriz, $unwind
não ajudaria. A funcionalidade "agrupar por" (seja no MongoDB ou em qualquer banco de dados relacional) é feita no valor de um campo ou coluna. Agrupamos por valor de campo e soma/média/etc com base no valor de outro campo.
O exemplo simples é uma variante do que você sugere, campo de avaliações adicionado à coleção de artigos de exemplo, mas não como um mapa do usuário para a avaliação, mas como uma matriz como esta:
{ title : title of article", ...
ratings: [
{ voter: "user1", score: 5 },
{ voter: "user2", score: 8 },
{ voter: "user3", score: 7 }
]
}
Agora você pode agregar isso com:
[ {$unwind: "$ratings"},
{$group : {_id : "$ratings.voter", averageScore: {$avg:"$ratings.score"} } }
]
Mas este exemplo estruturado como você o descreve ficaria assim:
{ title : title of article", ...
ratings: {
user1: 5,
user2: 8,
user3: 7
}
}
ou mesmo isso:
{ title : title of article", ...
ratings: [
{ user1: 5 },
{ user2: 8 },
{ user3: 7 }
]
}
Mesmo se você pudesse
$unwind
isso, não há nada para agregar aqui. A menos que você conheça a lista completa de todas as chaves (usuários) possíveis, você não pode fazer muito com isso. [*] Um esquema de banco de dados relacional análogo ao que você tem seria:
CREATE TABLE T (
user1: integer,
user2: integer,
user3: integer
...
);
Isso não é o que seria feito, em vez disso, faríamos isso:
CREATE TABLE T (
username: varchar(32),
score: integer
);
e agora agregamos usando SQL:
select username, avg(score) from T group by username;
Há uma solicitação de aprimoramento para o MongoDB que pode permitir que você faça isso na estrutura de agregação no futuro - a capacidade de projetar valores para chaves e vice-versa. Enquanto isso, sempre há map/reduce.
[*] Existe uma maneira complicada de fazer isso se você conhece todas as chaves exclusivas (você pode encontrar todas as chaves exclusivas com um método semelhante a este), mas se você conhece todas as chaves, você também pode executar uma sequência de consultas do form
db.articles.find({"ratings.user1":{$exists:true}},{_id:0,"ratings.user1":1})
para cada userX que retornará todas as suas classificações e você pode somar e fazer a média delas de forma bastante simples, em vez de fazer uma projeção muito complexa que a estrutura de agregação exigiria.