MapReduce ще стартира JavaScript в отделна нишка и ще използва кода, който предоставяте, за да излъчи и намали части от вашия документ, за да ги агрегира в определени полета. Със сигурност можете да разглеждате упражнението като агрегиране на всяка "fieldValue". Рамката за агрегиране също може да направи това, но ще бъде много по-бързо, тъй като агрегацията ще се изпълнява на сървъра в C++, а не в отделна JavaScript нишка. Но рамката за агрегиране може да върне повече данни обратно от 16 MB, в който случай ще трябва да направите по-сложно разделяне на набора от данни.
Но изглежда, че проблемът е много по-прост от това. Просто искате да намерите за всеки профил какви други профили споделят определени атрибути с него - без да знам размера на вашия набор от данни и вашите изисквания за производителност, ще приема, че имате индекс на fieldValues, така че би било ефективно да правите заявки върху него и след това можете да получите желаните резултати с този прост цикъл:
> 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()
);
} );
Изход:
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"
]
}
[ ]
Очевидно можете да промените заявката, за да не изключвате вече съвпадащи профили (като промените {$gt:p._id}
към {$ne:{p._id}}
и други настройки. Но не съм сигурен каква допълнителна стойност бихте получили от използването на рамка за агрегиране или mapreduce, тъй като това всъщност не е агрегиране на една колекция в едно от полетата (съдейки по формата на изхода, който показвате). Ако вашите изисквания за изходен формат са гъвкави, със сигурност е възможно да използвате и една от вградените опции за агрегиране.
Проверих, за да видя как би изглеждало това, ако се агрегира около отделни fieldValues и не е лошо, може да ви помогне, ако изходът ви може да съответства на това:
> 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
}
Това основно казва „За всяка fieldValue ($unwind) група по fieldValue масив от съвпадащи _ids и имена на профили, като се брои колко съвпадения натрупва всяка fieldValue ($group) и след това се изключват тези, които имат само един съответстващ профил.