MapReduce würde JavaScript in einem separaten Thread ausführen und den von Ihnen bereitgestellten Code verwenden, um Teile Ihres Dokuments auszugeben und zu reduzieren, um sie in bestimmten Feldern zu aggregieren. Sie können die Übung sicherlich als Aggregation über jeden "fieldValue" betrachten. Das Aggregationsframework kann dies ebenfalls, wäre aber viel schneller, da die Aggregation auf dem Server in C++ und nicht in einem separaten JavaScript-Thread ausgeführt würde. Das Aggregations-Framework kann jedoch mehr Daten als 16 MB zurückgeben. In diesem Fall müssten Sie eine komplexere Partitionierung des Datensatzes vornehmen.
Aber das Problem scheint viel einfacher zu sein. Sie möchten nur für jedes Profil herausfinden, welche anderen Profile bestimmte Attribute mit ihm teilen - ohne die Größe Ihres Datensatzes und Ihre Leistungsanforderungen zu kennen, gehe ich davon aus, dass Sie einen Index für fieldValues haben, damit die Abfrage effizient ist darauf und dann können Sie mit dieser einfachen Schleife die gewünschten Ergebnisse erzielen:
> 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()
);
} );
Ausgabe:
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"
]
}
[ ]
Natürlich können Sie die Abfrage so anpassen, dass bereits übereinstimmende Profile nicht ausgeschlossen werden (indem Sie {$gt:p._id}
ändern zu {$ne:{p._id}}
und andere Optimierungen. Aber ich bin mir nicht sicher, welchen zusätzlichen Wert Sie durch die Verwendung von Aggregation Framework oder Mapreduce erhalten würden, da dies nicht wirklich eine einzelne Sammlung in einem ihrer Felder aggregiert (nach dem Format der von Ihnen gezeigten Ausgabe zu urteilen). Wenn Ihre Anforderungen an das Ausgabeformat flexibel sind, können Sie sicherlich auch eine der integrierten Aggregationsoptionen verwenden.
Ich habe überprüft, wie dies aussehen würde, wenn es um einzelne Feldwerte aggregiert würde, und es ist nicht schlecht, es könnte Ihnen helfen, wenn Ihre Ausgabe damit übereinstimmen kann:
> 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
}
Dies besagt im Wesentlichen:„Für jeden fieldValue ($unwind) gruppiere nach fieldValue ein Array übereinstimmender Profil-IDs und -namen, zähle, wie viele Übereinstimmungen jeder fieldValue ansammelt ($group) und schließe dann diejenigen aus, die nur ein passendes Profil haben.