O MapReduce executaria o JavaScript em um encadeamento separado e usaria o código fornecido para emitir e reduzir partes do seu documento para agregar em determinados campos. Você certamente pode olhar para o exercício como agregando sobre cada "fieldValue". A estrutura de agregação também pode fazer isso, mas seria muito mais rápida, pois a agregação seria executada no servidor em C++ em vez de em um thread JavaScript separado. Mas a estrutura de agregação pode retornar mais dados do que 16 MB, caso em que você precisaria fazer um particionamento mais complexo do conjunto de dados.
Mas parece que o problema é muito mais simples do que isso. Você só quer descobrir para cada perfil quais outros perfis compartilham atributos específicos com ele - sem saber o tamanho do seu conjunto de dados e seus requisitos de desempenho, vou supor que você tenha um índice em fieldValues para que seja eficiente consultar nele e então você pode obter os resultados desejados com este loop simples:
> db.profiles.find().forEach( function(p) {
print("Matching profiles for "+tojson(p));
printjson(
db.profiles.find(
{"fieldValues": {"$in" : p.fieldValues},
"_id" : {$gt:p._id}}
).toArray()
);
} );
Resultado:
Matching profiles for {
"_id" : 1,
"firstName" : "John",
"lastName" : "Smith",
"fieldValues" : [
"favouriteColour|red",
"food|pizza",
"food|chinese"
]
}
[
{
"_id" : 2,
"firstName" : "Sarah",
"lastName" : "Jane",
"fieldValues" : [
"favouriteColour|blue",
"food|pizza",
"food|mexican",
"pets|yes"
]
},
{
"_id" : 3,
"firstName" : "Rachel",
"lastName" : "Jones",
"fieldValues" : [
"food|pizza"
]
}
]
Matching profiles for {
"_id" : 2,
"firstName" : "Sarah",
"lastName" : "Jane",
"fieldValues" : [
"favouriteColour|blue",
"food|pizza",
"food|mexican",
"pets|yes"
]
}
[
{
"_id" : 3,
"firstName" : "Rachel",
"lastName" : "Jones",
"fieldValues" : [
"food|pizza"
]
}
]
Matching profiles for {
"_id" : 3,
"firstName" : "Rachel",
"lastName" : "Jones",
"fieldValues" : [
"food|pizza"
]
}
[ ]
Obviamente, você pode ajustar a consulta para não excluir perfis já correspondentes (alterando
{$gt:p._id}
para {$ne:{p._id}}
e outros ajustes. Mas não tenho certeza de qual valor adicional você obteria usando a estrutura de agregação ou mapreduce, pois isso não está realmente agregando uma única coleção em um de seus campos (a julgar pelo formato da saída que você mostra). Se seus requisitos de formato de saída forem flexíveis, certamente é possível que você também possa usar uma das opções de agregação incorporadas. Eu verifiquei para ver como seria se agregar valores de campo individuais e não é ruim, pode ajudá-lo se sua saída puder corresponder a isso:
> db.profiles.aggregate({$unwind:"$fieldValues"},
{$group:{_id:"$fieldValues",
matchedProfiles : {$push:
{ id:"$_id",
name:{$concat:["$firstName"," ", "$lastName"]}}},
num:{$sum:1}
}},
{$match:{num:{$gt:1}}});
{
"result" : [
{
"_id" : "food|pizza",
"matchedProfiles" : [
{
"id" : 1,
"name" : "John Smith"
},
{
"id" : 2,
"name" : "Sarah Jane"
},
{
"id" : 3,
"name" : "Rachel Jones"
}
],
"num" : 3
}
],
"ok" : 1
}
Isso basicamente diz "Para cada fieldValue ($ unwind) agrupar por fieldValue uma matriz de _ids e nomes de perfil correspondentes, contando quantas correspondências cada fieldValue acumula ($ group) e, em seguida, exclua aqueles que têm apenas um perfil correspondente.