Dit is een enigszins ingewikkelde oplossing. Het idee is om eerst de DB te gebruiken om de populatie van mogelijke paren te krijgen, dan om te draaien en de DB te vragen de paren te vinden in de _user
veld. Pas op dat duizenden gebruikers een behoorlijk grote koppelingslijst zullen maken. We gebruiken $addFields
voor het geval er meer aan de invoerrecords is dan we in het voorbeeld zien, maar als dat niet het geval is, vervang dan voor efficiëntie door $project
om de hoeveelheid materiaal die door de buis stroomt te verminderen.
//
// Stage 1: Get unique set of username pairs.
//
c=db.foo.aggregate([
{$unwind: "$_user"}
// Create single deduped list of users:
,{$group: {_id:null, u: {$addToSet: "$_user"} }}
// Nice little double map here creates the pairs, effectively doing this:
// for index in range(0, len(list)):
// first = list[index]
// for p2 in range(index+1, len(list)):
// pairs.append([first,list[p2]])
//
,{$addFields: {u:
{$map: {
input: {$range:[0,{$size:"$u"}]},
as: "z",
in: {
$map: {
input: {$range:[{$add:[1,"$$z"]},{$size:"$u"}]},
as: "z2",
in: [
{$arrayElemAt:["$u","$$z"]},
{$arrayElemAt:["$u","$$z2"]}
]
}
}
}}
}}
// Turn the array of array of pairs in to a nice single array of pairs:
,{$addFields: {u: {$reduce:{
input: "$u",
initialValue:[],
in:{$concatArrays: [ "$$value", "$$this"]}
}}
}}
]);
// Stage 2: Find pairs and tally up the fileids
doc = c.next(); // Get single output from Stage 1 above.
u = doc['u'];
c2=db.foo.aggregate([
{$addFields: {_x: {$map: {
input: u,
as: "z",
in: {
n: "$$z",
q: {$setIsSubset: [ "$$z", "$_user" ]}
}
}
}
}}
,{$unwind: "$_x"}
,{$match: {"_x.q": true}}
// Nice use of grouping by an ARRAY here:
,{$group: {_id: "$_x.n", v: {$push: "$_id.fileid"}, n: {$sum:1} }}
,{$match: {"n": {"$gt":1}}}
]);
show(c2);