2012-05-11 31 views
0

我正在用express和mongoose/mongodb構建nodejs應用程序。如何使用Mongoose從Schema方法訪問嵌入式文檔(DBRef-like)的屬性?

爲了管理用戶角色(用戶可能有0到n個角色),我決定實現一個用戶模式和一個單獨的角色模式,並將它們綁定在一起,如DBRefs和使用貓鼬填充功能從一個角色到另一個角色其他容易。我選擇了這種結構,因爲我認爲這是回答諸如「給我一個具有X角色的用戶列表」或「給我一個Y角色即將到期的用戶列表」的最佳方式。

這些被剝離下來的角色和用戶模式我的版本:

RoleSchema = new Schema { 
    _user: { type: Schema.ObjectId, ref: 'User' } 
    type: String 
    expiration: { type: Date, default : '0' } 
} 

UserSchema = new Schema { 
    email: { type: String, index: { unique: true }}  
    roles : [{ type: Schema.ObjectId, ref: 'Role' }] 
} 

有了這個,我可以從我的快遞腳本中創建/讀取/填充用戶和角色沒有問題。當我想向用戶添加角色時,我創建角色並保存,然後將其推送到用戶的角色數組,然後保存用戶。所以,mongoDB會發生什麼,每個模式都有自己的集合,並且它們通過一個id字段指向彼此。

現在,我想下一步要做的就是實現我的模式布爾類型的方法來檢查角色相關的問題,讓我可以做這樣的事情

if(user.isActiveSubscriber()) 

我認爲我將能夠只需添加一個方法到我的用戶模式,這樣來實現:

UserSchema.method "isActiveSubscriber", -> 
    result = false 
    now = new Date() 
    @roles.forEach (role) -> 
    result = true if role.type is "SUBSCRIBER" and role.expiration > now 
result 

我的問題是角色走出來空屬性。我想這是有道理的,因爲我的用戶集合只有角色的id,但實際屬性存儲在另一個集合中。

所以,在這裏走的問題:

一)有沒有辦法來加載角色從我的用戶架構方法中的屬性? 我試着調用方法內部的填充,但得到了一個錯誤,用戶對象不知道任何填充方法。我也嘗試從方法內部做一個Role.findById(),但也得到一個錯誤(嘗試與角色和RoleSchema)

b)萬一沒有辦法...我應該簡單地添加代碼來檢查在我的腳本?我討厭把這種邏輯與應用程序邏輯/流程混合在一起。有更好的選擇嗎?

c)將這些集合分爲兩部分是一個壞主意嗎?我完全錯過了NoSQL的觀點嗎?如果角色只是作爲用戶集合的一部分存儲的嵌入式文檔的數組,那會更好嗎?

回答

1

我來回答你的問題:

一)你可以做的是加載你的電話裏面角色。假設你做

Role = mongoose.model('Role', RoleSchema) 

你只需要運行

Role.where('_id').in(user.roles).run((err, res) -> 
    /* you have roles in res now */ 
) 

然而,這是一個異步操作,這需要回調要傳遞給你的方法,即

UserSchema.method "isActiveSubscriber", (callback) -> 
    now = new Date() 
    if @roles.length is 0 
     return callback(false) 
    if @roles[0].type 
     /* roles are loaded, 
      maybe there is some other fancy way to do the check */ 
     result = false 
     @roles.forEach (role) -> 
      if role.type is "SUBSCRIBER" and role.expiration > now 
       result = true 
     callback(result) 
    else 
     /* we have to load roles */ 
     Role.where('_id').in(@roles).run((err, res) => 
      /* you have roles in res now */ 
      /* you may want to populate the field, 
       note => sign in this function definition */ 
      @roles = res 
      result = false 
      res.forEach (role) -> 
       if role.type is "SUBSCRIBER" and role.expiration > now 
        result = true 
      callback(result) 
     ) 

現在對於用戶user你可以這樣調用

user.isActiveSubscriber((result) -> 
    /* do something with result */ 
) 

的問題是,該操作是異步的,它在你的代碼強制附加的回調嵌套(這將是痛苦,如果你想要檢查100個用戶的角色,您將需要一些異步調用處理庫,如async)。因此,無論何時加載用戶並使用您向我們顯示的代碼,我都會建議填充此字段。你可以添加靜態方法(也許你甚至可以覆蓋默認的find方法?我不確定這一點)。

b + c)另一種選擇是將角色存儲爲集合中的字符串,並在應用程序中硬編碼可能的角色。這將實現起來更簡單,但添加/刪除新角色將是$%^ & *中的一個痛點。你這樣做的方式(即通過引用)是好的,我認爲你唯一需要的是填充字段,每當你搜索用戶,一切都會好的。

+0

非常感謝。會嘗試這個。 –

0

所採用的方法是在如何貓鼬如何使用填充實現類似DBRef的行爲的範圍內。但是有些東西似乎被遺漏了。不確定這些細節是否已經小心處理,並且爲了簡潔起見未顯示。將貫穿整套步驟:

RoleSchema = new Schema { 
    type: String 
    expiration: { type: Date, default : '0' } 
} 

UserSchema = new Schema { 
    email: { type: String, index: { unique: true }}  
    _roles : [{ type: Schema.ObjectId, ref: 'Role' }] // array of pointers for roles 
} 

Role = mongo.model('Role', 'RoleSchema'); 
User = mongo.model('User', 'UserSchema'); 

var life_sub = new Role({ type: 'LIFE_SUBSCRIBER', .... }); 
var daily_sub = new Role({ type: 'DAILY_SUBSCRIBER', .... }); 
var monthly_sub = new Role({ type: 'MONTHLY_SUBSCRIBER', .... }); 

var jane = new User({email: '[email protected]', _roles: [daily_sub, monthly_sub]}); 

根據需要添加用戶。接着查找指定用戶和相應的一組角色的使用:

User 
.find({email:'[email protected]'}) 
.populate('_roles[]') 
.run(function(err, user) 

現在用戶是對應於指定的用戶的角色的指針數組。遍歷數組並查找用戶的每個角色和相應的截止日期。與Date.now()進行比較,以確定特定用戶的哪些角色已到期。

希望這會有所幫助。

+0

如果我從我的應用程序邏輯中調用填充,那工作正常。但我認爲這是一個更好的設計決策,可以通過模式函數實現,因此無論何時我想檢查給定用戶的角色(例如會話中的當前用戶),我都可以做一個簡單的操作: if( sessionUser.isActiveSubscriber()) ... 而不是必須讓代碼驗證角色,與我的應用程序邏輯混合。 –

相關問題