1
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 }], 

我需要找到与我共同最感兴趣的用户(ids数组)的前5个android_ids。我可以使用来自interest数组的唯一匹配元素的数组。如何匹配和排序基于共同阵列元素的文档

回答

3

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

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

这使得你的“五大”基本上是的产品“不是我,并在共同的最利益”,这意味着你基本上需要数相比,当前用户的利益,对每个用户的“重叠” 。

这基本上是两个数组的$setIntersection或“套”,其中返回通用元素。为了统计有多少是共同的,还有$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运算符被引入,那么你仍然可以做到这一点,但它只是需要一个更长的形式处理数组。

主要是你需要$unwind阵列和处理每个单独的比赛:

 { "$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 } 

哪个更实际编码,以产生在管道中的condtional比赛:

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充其量,每对)被传递给累计器,该累计器对相同的匹配进行计数。您可以与再次$in条件交替$match

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

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

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

只是为了好玩,这里是一个基本的node.js上市,显示普通比赛的影响: VAR异步=要求(“异步”), 猫鼬=需要(“猫鼬”), 模式=猫鼬。模式;

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 } ] 
+0

由于一吨,我很欣赏的例子和预2.6查询结构太。 –