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查詢結構太。 –