MapReduce exécuterait JavaScript dans un thread séparé et utiliserait le code que vous fournissez pour émettre et réduire des parties de votre document à agréger sur certains champs. Vous pouvez certainement considérer l'exercice comme une agrégation sur chaque "fieldValue". Le framework d'agrégation peut également le faire, mais serait beaucoup plus rapide car l'agrégation s'exécuterait sur le serveur en C++ plutôt que dans un thread JavaScript séparé. Mais le cadre d'agrégation peut renvoyer plus de données que 16 Mo, auquel cas vous devrez effectuer un partitionnement plus complexe de l'ensemble de données.
Mais il semble que le problème soit beaucoup plus simple que cela. Vous voulez juste trouver pour chaque profil quels autres profils partagent des attributs particuliers avec lui - sans connaître la taille de votre ensemble de données et vos exigences de performance, je vais supposer que vous avez un index sur fieldValues donc il serait efficace d'interroger dessus et vous pouvez ensuite obtenir les résultats souhaités avec cette simple boucle :
> 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()
);
} );
Sortie :
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"
]
}
[ ]
Évidemment, vous pouvez modifier la requête pour ne pas exclure les profils déjà correspondants (en modifiant {$gt:p._id}
à {$ne:{p._id}}
et autres ajustements. Mais je ne suis pas sûr de la valeur supplémentaire que vous obtiendriez en utilisant le cadre d'agrégation ou mapreduce car cela n'agrège pas vraiment une seule collection sur l'un de ses champs (à en juger par le format de la sortie que vous affichez). Si vos exigences de format de sortie sont flexibles, il est certainement possible que vous puissiez également utiliser l'une des options d'agrégation intégrées.
J'ai vérifié pour voir à quoi cela ressemblerait si l'agrégation autour de fieldValues individuelles et ce n'est pas mauvais, cela pourrait vous aider si votre sortie peut correspondre à ceci :
> 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
}
Cela dit essentiellement "Pour chaque fieldValue ($ unwind) group by fieldValue un tableau de _ids et de noms de profil correspondants, en comptant le nombre de correspondances que chaque fieldValue accumule ($ group) puis exclure ceux qui n'ont qu'un seul profil correspondant.