MapReduce zou JavaScript in een aparte thread uitvoeren en de door u verstrekte code gebruiken om delen van uw document uit te zenden en te verkleinen om op bepaalde velden te aggregeren. Je kunt de oefening zeker zien als geaggregeerd over elke "fieldValue". Aggregatieframework kan dit ook doen, maar zou veel sneller zijn omdat de aggregatie op de server in C++ zou worden uitgevoerd in plaats van in een afzonderlijke JavaScript-thread. Maar het aggregatieraamwerk kan meer gegevens teruggeven dan 16 MB, in welk geval u een complexere partitionering van de gegevensset zou moeten uitvoeren.
Maar het lijkt erop dat het probleem veel eenvoudiger is dan dit. U wilt gewoon voor elk profiel vinden welke andere profielen er bepaalde kenmerken mee delen - zonder de grootte van uw dataset en uw prestatievereisten te kennen, ga ik ervan uit dat u een index op fieldValues heeft, zodat het efficiënt zou zijn om te zoeken erop en dan kun je de gewenste resultaten krijgen met deze eenvoudige lus:
> 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()
);
} );
Uitgang:
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"
]
}
[ ]
Uiteraard kunt u de zoekopdracht aanpassen om reeds overeenkomende profielen niet uit te sluiten (door {$gt:p._id}
te wijzigen naar {$ne:{p._id}}
en andere tweaks. Maar ik weet niet zeker welke extra waarde je zou krijgen door het gebruik van een aggregatieraamwerk of mapreduce, aangezien dit niet echt een enkele verzameling op een van zijn velden aggregeert (te oordelen naar het formaat van de uitvoer die je laat zien). Als uw vereisten voor uitvoerformaten flexibel zijn, is het zeker mogelijk dat u ook een van de ingebouwde aggregatie-opties kunt gebruiken.
Ik heb gecontroleerd om te zien hoe dit eruit zou zien als je zou aggregeren rond individuele veldwaarden en het is niet slecht, het kan je helpen als je output dit kan evenaren:
> 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
}
Dit zegt in feite "Voor elke fieldValue ($unwind) groep op fieldValue een array van overeenkomende profiel-ID's en namen, waarbij wordt geteld hoeveel overeenkomsten elke fieldValue verzamelt ($groep) en sluit vervolgens degenen uit die slechts één profiel hebben dat ermee overeenkomt.