MapReduce skulle köra JavaScript i en separat tråd och använda koden du tillhandahåller för att skicka ut och reducera delar av ditt dokument för att samlas på vissa fält. Du kan säkert se övningen som en aggregering över varje "fieldValue". Aggregationsramverket kan också göra detta men skulle vara mycket snabbare eftersom aggregeringen skulle köras på servern i C++ snarare än i en separat JavaScript-tråd. Men aggregeringsramverket kan returnera mer data tillbaka än 16 MB i vilket fall du skulle behöva göra en mer komplex partitionering av datamängden.
Men det verkar som om problemet är mycket enklare än så här. Du vill bara hitta för varje profil vilka andra profiler som delar särskilda attribut med den - utan att veta storleken på din datamängd och dina prestandakrav, kommer jag att anta att du har ett index på fieldValues så det skulle vara effektivt att fråga på den och sedan kan du få de resultat du vill ha med denna enkla loop:
> 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()
);
} );
Utdata:
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"
]
}
[ ]
Självklart kan du justera frågan så att den inte utesluter redan matchade profiler (genom att ändra {$gt:p._id}
till {$ne:{p._id}}
och andra justeringar. Men jag är inte säker på vilket mervärde du skulle få av att använda aggregeringsramverk eller mapreduce eftersom detta inte riktigt är att aggregera en enda samling på ett av dess fält (att döma av formatet på utdata som du visar). Om dina krav på utdataformat är flexibla är det säkert möjligt att du också kan använda ett av de inbyggda aggregeringsalternativen.
Jag kollade för att se hur det här skulle se ut om man aggregerar kring individuella fieldValues och det är inte dåligt, det kan hjälpa dig om din produktion kan matcha detta:
> 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
}
Detta säger i princip "För varje fieldValue ($unwind) gruppera för fieldValue en array av matchande profil _id och namn, räknar hur många matchningar varje fieldValue ackumulerar ($group) och exkludera sedan de som bara har en profil som matchar den.