MapReduce uruchomi JavaScript w osobnym wątku i użyje podanego kodu do emisji i zmniejszenia części dokumentu w celu agregacji w określonych polach. Z pewnością możesz spojrzeć na ćwiczenie jako agregację po każdej „wartości pola”. Framework agregacji również może to zrobić, ale byłby znacznie szybszy, ponieważ agregacja działałaby na serwerze w C++, a nie w osobnym wątku JavaScript. Ale struktura agregacji może zwrócić więcej danych niż 16 MB, w takim przypadku konieczne byłoby bardziej złożone partycjonowanie zestawu danych.
Ale wydaje się, że problem jest o wiele prostszy. Chcesz tylko znaleźć dla każdego profilu, jakie inne profile współdzielą z nim określone atrybuty - bez znajomości rozmiaru zestawu danych i wymagań dotyczących wydajności, zakładam, że masz indeks na fieldValues, więc wydajne byłoby zapytanie na nim, a następnie możesz uzyskać pożądane wyniki za pomocą tej prostej pętli:
> 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()
);
} );
Wyjście:
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"
]
}
[ ]
Oczywiście możesz dostosować zapytanie, aby nie wykluczać już dopasowanych profili (zmieniając {$gt:p._id}
do {$ne:{p._id}}
i inne poprawki. Ale nie jestem pewien, jaką dodatkową wartość można by uzyskać, używając struktury agregacji lub mapreduce, ponieważ tak naprawdę nie jest to agregowanie pojedynczej kolekcji na jednym z jej pól (sądząc po formacie wyświetlanych danych wyjściowych). Jeśli Twoje wymagania dotyczące formatu wyjściowego są elastyczne, z pewnością możliwe jest również skorzystanie z jednej z wbudowanych opcji agregacji.
Sprawdziłem, jak by to wyglądało, gdyby agregacja wokół poszczególnych wartości pól nie była zła, może ci pomóc, jeśli twoje dane wyjściowe będą pasować do tego:
> 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 w zasadzie mówi:„Dla każdego fieldValue ($unwind) grupuj według fieldValue tablicę pasujących identyfikatorów i nazw profili, licząc, ile dopasowań gromadzi każda wartość fieldValue ($group), a następnie wyklucz te, które mają tylko jeden pasujący profil.