如何基于数组元素进行匹配和sorting

var UserSchema = Schema ( { android_id: String, created: {type: Date, default:Date.now}, interests: [{ type: Schema.Types.ObjectId, ref: 'Interests' }], }); Users.aggregate([ { $match: {android_id: {$ne: userID}, interests: {$elemMatch: {$in: ids}} }}, { $group: { _id: { android_id: '$android_id'},count: {$sum: 1}}}, { $sort: {count: -1}}, { $limit: 5 }], 

我需要find与我共同最感兴趣的用户(ids数组)的前5个android_ids。 我可以使用来自interest数组的唯一匹配元素的数组。

你似乎在这里沿着正确的路线走,但是你需要考虑数组有比较特殊的考虑。

你的基本开始是find所有不是当前用户的用户,并且至less还需要当前用户的“兴趣”数组。 您似乎已经这样做了,但是在这里让我们考虑一下,您将拥有将在列表中使用的当前用户的整个user对象。

这使得你的“前五名”基本上是“不是我,最常用的”的一个产品,这意味着你基本上需要把每个用户的兴趣与当前用户相比“重叠”。

这基本上是两个数组的$setIntersection ,或者返回通用元素的“集合”。 为了统计有多less是共同的,还有$size操作符。 所以你这样申请:

 Users.aggregate( [ { "$match": { "android_id": { "$ne": user.android_id }, "interests": { "$in": user.interests } }}, { "$project": { "android_id": 1, "interests": 1, "common": { "$size": { "$setIntersection": [ "$interests", user.interests ] } } }}, { "$sort": { "common": -1 } }, { "$limit": 5 } ], function(err,result) { } ); 

“普通”返回的结果是数据中当前用户与被检查用户之间的共同利益计数。 这个数据然后通过$sort进行处理,以便将最大数量的共同兴趣置于顶部,然后$limit仅返回前5个。

如果由于某种原因,你的MongoDB版本目前低于MongoDB 2.6(其中引入了$setIntersection$size操作符),那么你仍然可以做到这一点,但它只是需要一个更长的forms处理数组。

主要你需要$unwindarrays,并单独处理每场比赛:

  { "$match": { "android_id": { "$ne": user.android_id }, "interests": { "$in": user.interests } }}, { "$unwind": "$interests" }, { "$group": { "_id": "$_id", "android_id": { "$first": "$android_id" }, "interests": { "$push": "$interests" }, "common": { "$sum": { "$add": [ { "$cond": [{ "$eq": [ "$interests", user.interests[0] ] },1,0 ] }, { "$cond": [{ "$eq": [ "$interests", user.interests[1] ] },1,0 ] }, { "$cond": [{ "$eq": [ "$interests", user.interests[2] ] },1,0 ] } ] } } }}, { "$sort": { "common": -1 }}, { "$limit": 5 } 

哪个更实际地编码来生成pipe道中的条件匹配:

  var pipeline = [ { "$match": { "android_id": { "$ne": user.android_id }, "interests": { "$in": user.interests } }}, { "$unwind": "$interests" } ]; var group = { "$group": { "_id": "$_id", "android_id": { "$first": "$android_id" }, "interests": { "$push": "$interests" }, "common": { "$sum": { "$add": [] } } }}; user.interests.forEach(function(interest) { group.$group.common.$sum.$add.push( { "$cond": [{ "$eq": [ "$interests", interest ] }, 1, 0 ] } ); }); pipeline.push(group); pipeline = pipeline.concat([ { "$sort": { "common": -1 }}, { "$limit": 5 } ]) User.aggregate(pipeline,function(err,result) { }); 

当前用户和被检查用户之间的关键因素是将他们的“兴趣”分离出来,以便比较是否“相等”。 $cond的结果是1 ,如果是true,或者0地方是false。

任何回报(并且只有预期为1 ,最好,每对)传递给$sum累加器,这个累加器统计匹配的共同点。 您可以再次与$in条件再次$match

  { "$unwind": "$interests" }, { "$match": { "interests": { "$in": user.interests } }, { "$group": { "_id": "$_id", "android_id": { "$first": "$android_id" }, "common": { "$sum": 1 } }} 

但是这是数组内容的自然破坏性,因为不匹配被过滤掉。 所以这取决于你希望在回应中有什么。

这是获得“常用”计数的基本过程,用于进一步处理,如$sort$limit ,以获得您的“前五名”。

只是为了好玩,这里是一个基本的node.js列表来显示常见匹配的效果:var async = require('async'),mongoose = require('mongoose'),Schema = mongoose.Schema;

 mongoose.connect('mongodb://localhost/sample'); var interestSchema = new Schema({ name: String }); var userSchema = new Schema({ name: String, interests: [{ type: Schema.Types.ObjectId, ref: 'Interest' }] }); var Interest = mongoose.model( 'Interest', interestSchema ); var User = mongoose.model( 'User', userSchema ); var interestHash = {}; async.series( [ function(callback) { async.each([Interest,User],function(model,callback) { model.remove({},callback); },callback); }, function(callback) { async.each( [ "Tennis", "Football", "Gaming", "Cooking", "Yoga" ], function(interest,callback) { Interest.create({ name: interest},function(err,obj) { if (err) callback(err); interestHash[obj.name] = obj._id; callback(); }); }, callback ); }, function(callback) { async.each( [ { name: "Bob", interests: ["Tennis","Football","Gaming"] }, { name: "Tom", interests: ["Football","Cooking","Yoga"] }, { name: "Sue", interests: ["Tennis","Gaming","Yoga","Cooking"] } ], function(data,callback) { data.interests = data.interests.map(function(interest) { return interestHash[interest]; }); User.create(data,function(err,user) { //console.log(user); callback(err); }) }, callback ); }, function(callback) { async.waterfall( [ function(callback) { User.findOne({ name: "Bob" },callback); }, function(user,callback) { console.log(user); User.aggregate( [ { "$match": { "_id": { "$ne": user._id }, "interests": { "$in": user.interests } }}, { "$project": { "name": 1, "interests": 1, "common": { "$size": { "$setIntersection": [ "$interests", user.interests ] } } }}, { "$sort": { "common": -1 } } ], function(err,result) { if (err) callback(err); Interest.populate(result,'interests',function(err,result) { console.log(result); callback(err); }); } ); } ], callback ); } ], function(err) { if (err) throw err; //console.dir(interestHash); mongoose.disconnect(); } ); 

哪个会输出:

 { _id: 55dbd7be0e5516ac16ea62d1, name: 'Bob', __v: 0, interests: [ 55dbd7be0e5516ac16ea62cc, 55dbd7be0e5516ac16ea62cd, 55dbd7be0e5516ac16ea62ce ] } [ { _id: 55dbd7be0e5516ac16ea62d3, name: 'Sue', interests: [ { _id: 55dbd7be0e5516ac16ea62cc, name: 'Tennis', __v: 0 }, { _id: 55dbd7be0e5516ac16ea62ce, name: 'Gaming', __v: 0 }, { _id: 55dbd7be0e5516ac16ea62d0, name: 'Yoga', __v: 0 }, { _id: 55dbd7be0e5516ac16ea62cf, name: 'Cooking', __v: 0 } ], common: 2 }, { _id: 55dbd7be0e5516ac16ea62d2, name: 'Tom', interests: [ { _id: 55dbd7be0e5516ac16ea62cd, name: 'Football', __v: 0 }, { _id: 55dbd7be0e5516ac16ea62cf, name: 'Cooking', __v: 0 }, { _id: 55dbd7be0e5516ac16ea62d0, name: 'Yoga', __v: 0 } ], common: 1 } ]