Đây là một giải pháp có liên quan. Ý tưởng là trước tiên sử dụng DB để lấy tổng số các cặp có thể có, sau đó quay lại và yêu cầu DB tìm các cặp trong _user
đồng ruộng. Hãy lưu ý rằng hàng nghìn người dùng sẽ tạo ra một danh sách ghép nối khá lớn. Chúng tôi sử dụng $addFields
chỉ trong trường hợp có nhiều bản ghi đầu vào hơn chúng ta thấy trong ví dụ, nhưng nếu không, hãy thay thế bằng $project
để đạt hiệu quả để cắt giảm lượng vật chất chảy qua đường ống.
//
// 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);