MapReduce spustí JavaScript v samostatném vláknu a použije kód, který poskytnete, k odeslání a zmenšení částí vašeho dokumentu tak, aby se agregovaly v určitých polích. Určitě se na cvičení můžete dívat jako na agregaci nad každou „hodnotou pole“. Agregační framework to umí také, ale byl by mnohem rychlejší, protože agregace by běžela na serveru v C++ spíše než v samostatném vlákně JavaScriptu. Ale agregační rámec může vrátit více dat než 16 MB, v takovém případě budete muset provést složitější rozdělení datové sady.
Ale zdá se, že problém je mnohem jednodušší než tento. Chcete pouze pro každý profil najít, jaké jiné profily s ním sdílejí konkrétní atributy – aniž byste znali velikost vaší datové sady a vaše požadavky na výkon, budu předpokládat, že máte index na fieldValues, takže by bylo efektivní dotazovat na něm a pak můžete získat požadované výsledky pomocí této jednoduché smyčky:
> 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()
);
} );
Výstup:
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"
]
}
[ ]
Dotaz samozřejmě můžete vyladit tak, aby nevylučoval již spárované profily (změnou {$gt:p._id}
na {$ne:{p._id}}
a další vychytávky. Nejsem si ale jistý, jakou další hodnotu byste získali z použití agregačního rámce nebo mapreduce, protože to ve skutečnosti není agregace jedné kolekce na jednom z jejích polí (soudě podle formátu výstupu, který zobrazujete). Pokud jsou vaše požadavky na výstupní formát flexibilní, určitě je možné, že byste také mohli použít jednu z integrovaných možností agregace.
Zkontroloval jsem, jak by to vypadalo při agregaci kolem jednotlivých hodnot polí a není to špatné, mohlo by vám pomoci, kdyby váš výstup mohl odpovídat tomuto:
> 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
}
To v podstatě říká:„Pro každou fieldValue ($unwind) seskupte podle fieldValue pole odpovídajících _id a názvů profilů, přičemž se počítá, kolik shod každá fieldValue nashromáždí ($group) a pak se vyloučí ty, které mají pouze jeden odpovídající profil.