2012-11-21 49 views
0

經過如此習慣SQL之後,我遇到了mongoDB這個問題。 首先,我正在使用貓鼬。MongoDB查詢結果中的自定義字段

現在,問題所在。我有一個名爲User的集合。

var UserSchema = new Schema ({ 
    id : ObjectId, 
    name : {type : String, trim : true, required : true}, 
    email: {type:String, trim:true, required: true, index: { unique: true }}, 
    password: {type:String, required: true, set: passwordToMD5}, 
    age: {type:Number, min: 18, required: true, default: 18}, 
    gender: {type: Number, default:0, required: true}, 

    height: {type: Number, default:180, min: 140, max: 220}, 
    _eye_color: {type: ObjectId, default: null}, 
    location: { 
      lon: {type: Number, default: 0}, 
      lat: {type: Number, default: 0} 
    }, 
    status: {type:Number, required: true, default:0} 
    },{ 
     toObject: { virtuals: true }, 
     toJSON: { virtuals: true }, 
     collection:"user"}); 

現在我需要從這個集合中選擇所有用戶和特殊attribude排序他們(說「等級」)。這個排名與一定的邏輯從點取決於他們的距離計算,年齡與特定的年齡,等於是現在比較...

我不知道如何選擇這個級別,然後用它的排序?我試圖使用虛擬化,他們很方便地計算其他信息,但不幸的是,不可能通過虛擬字段對find()結果進行排序。 當然我可以在虛擬中計算這個等級,然後選擇所有記錄,然後在回調中做一些javascript。但是,在這種情況下,我選擇的所有用戶,然後進行排序,然後限制,JavaScript部分可能要花很長時間...... 我想用的MapReduce,但我不知道它會做我想做的。 有人可以給我一個提示,如果我的任務可以做mongoDB/mongoose

編輯1

我也曾嘗試使用聚合框架,並在第一次似乎與$project能力的最佳解決方案。但是,當我需要進行排名計算時,我發現聚合不支持很多數學函數,如sincossqrt。而且在投影中也不可能使用預先定義的通常的javascript函數。我的意思是,函數被調用,但我無法將當前記錄字段傳遞給它。

{$project: { 
    distance_from_user: mUtils.getDistance(point, this.location) 
} 

函數裏面的第二個attr是「undefined」。

所以我想這是不可能做我的排名計算與聚合框架。

EDIT 2 好吧,我知道每個人都告訴我不要使用MapReduce的,因爲它不利於實時查詢,但我不能用聚集,我想我會嘗試的MapReduce。所以我們說我有這個地圖縮小。

function map() { 
      emit(1, // Or put a GROUP BY key here 
       {name: this.name, // the field you want stats for 
        age: this.age, 
        lat: this.location.lat, 
        lon: this.location.lon, 
        distance:0, 
        rank:0 

       }); 
     } 

     function reduce(key, values) { 


      return val; 
     } 

     function finalize(key, value){ 

      return value; 
     } 


     var command = {'mapreduce': "user", 'map': map.toString(), 'reduce': reduce.toString(), query:{$and: [{gender: user_params.gender}, {_id: {$ne: current_user_id}}]}, 'out': {inline:1}}; 

     mongoose.connection.db.executeDbCommand(command, function(error, result){ 
      if(error) { 
       log(error); 
       return; 
      } 
      log(result); 
      return; 
     }); 

我應該寫在減少(或可能改變地圖)來計算每個用戶的排名?

+1

您不應該使用mapreduce進行'實時'查詢。看看聚合框架http://docs.mongodb.org/manual/applications/aggregation/ – Alex

+0

我已經嘗試過聚合,但是在聚合框架中,不可能使用我需要的功能進行排名計算。例如,沒有'sqrt()','sin()','cos()'等。 – ArVan

回答

1

唯一真正的解決辦法是計算你的排名爲每個文檔並將其存儲在文檔中。由於只要文檔中的值保持不變,只要您更新影響該值的字段,就可以簡單地計算此值,此值將保持不變。

地圖/減少肯定不是一個很好的解決方案也不是任何其他類型的聚集。如果您使用的是MongoDB,則預先計算您的排名並將其與文檔一起存儲是唯一可擴展的選項。

+0

我無法計算和存儲結果,因爲計算是關於登錄的用戶詳細信息完成的。所以這個排名對於系統的每個用戶都是不同的。 – ArVan

+0

這就是爲什麼我嘗試了虛擬。 – ArVan

+0

我想唯一的另一種選擇是定期運行以生成排名的mapreduce? – Alex

0

它看起來像一個良好的用例MongoDB + Hadoop

presentation顯示了這種組合的一些可能性。

+0

嗯......我認爲這是不可能與** node.js **一起使用** – ArVan

+0

如果您將它與[批量聚合]一起使用(http://www.mongodb.org/display/DOCS/Hadoop+Scenarios )場景中,node.js不應該與hadoop進行交互。你將不得不在Java中編寫你的mapreduce。一些[示例](https://github.com/mongodb/mongo-hadoop/tree/master/examples)在github上。 – Eric

1

你知道這種東西需要的計算量 - 如果你每次用戶登錄時都會這樣做,那麼當很多人在較短的時間內登錄時,就會出現有趣的負載峯值 - 而且你的頁面(接口)將嚴重限制資源(這是不好的)。
我推薦給你一些不同的東西 - 保持每個登錄用戶的排名並間隔地更新它們:保持「短會話」和「長會話」(長會話 - 您在網頁瀏覽器和短會話中使用的會話) 「在線,目前正在使用該網站」),並定期爲「短時間活躍」用戶定期創建排名,很少在長時間段內登錄。像每五分鐘一次。更可升級 - 如果用戶不滿意他沒有計算他的等級 - 你可能會隨時調整系統以根據需求計算他的等級。
在這種情況下,您可能會使用mapredurce - 您的地圖功能只應發出用於計算給定用戶的排名(例如年齡,經緯度,長度,需要的任何數據)以及測試用戶的結果(排名) (排空)。對於reduce函數,您需要考慮使用mapreduce進行排序(這很大程度上取決於您創建排名的方式) - 也可以爲其他用戶計算排名(或某種子值)。

+2

與任何按需計算相比,此解決方案會導致資源消耗顯着增加。您很可能會重新計算相同用戶的排名,而不會影響排名的任何值發生變化。此外,如果功能上允許異步執行排名計算,則負載峯值不應該成爲問題(如果峯值恰好是您想要的性能模式,則只需對排序計算器排隊,而且它們需要更長的時間)。 –

+0

這一切都取決於 - 如果經常使用頁面並且計算可能會佔用大量資源,那麼在給定的時間段內控制計算量要好得多 - 它可能是通過某種緩存按需創建的,或者使用某種緩存像基於時間的計算,默認情況下不按需求完成。我不鼓勵按要求做所有事情,因爲它可能完全不可行。 –

+1

您可以將相同的限制規則應用於按需異步工作負載隊列,因爲您可以將這些限制規則應用於定期工作負載。負載可預測性絕對是一個重要因素,但我認爲如果我們討論負載峯值的話,可預測性已經很有限。請注意,點播是指幾乎每個需要處理峯值登錄的系統都會這樣做,因爲它自動實現自動平衡(較慢的登錄/較大的登錄隊列 - >按需計算次數較少),並允許更輕鬆地進行調節(例如登錄隊列,等等看到幾乎所有的大型遊戲服務)。 –