2013-10-30 76 views
8

我使用MongoDB和一個nodejs REST服務來暴露存儲在裏面的數據。我有一個關於如何查詢使用$ ref的數據的問題。

這裏是包含花葯集合中的引用到另一個對象(細節)的對象的樣品:

{ 
    "_id" : ObjectId("5962c7b53b6a02100a000085"), 
    "Title" : "test", 
    "detail" : { 
     "$ref" : "ObjDetail", 
     "$id" : ObjectId("5270c7b11f6a02100a000001") 
    }, 
    "foo" : bar 
} 

其實用的Node.js和MongoDB模塊,i執行以下操作:

db.collection("Obj").findOne({"_id" : new ObjectID("5962c7b53b6a02100a000085"}, 
function(err, item) { 
    db.collection(item.$ref).findOne({"_id" : item.$id}, function(err,subItem){ 
     ... 
    }); 
}); 

事實上,我做了2個查詢,並得到2個對象。這是一種「延遲加載」(不完全,但幾乎)

我的問題很簡單:是否有可能在一個查詢中檢索整個對象圖?

謝謝

回答

4

不,你不能。

要解決DBRefs,您的應用程序必須執行其他查詢才能返回引用的文檔。許多驅動程序都有助手方法,它們自動爲DBRef構成查詢。驅動程序不會自動將DBRefs解析爲文檔。

來自MongoDB文檔http://docs.mongodb.org/manual/reference/database-references/

+1

而且據我所知節點MongoDB的原生驅動程序沒有辦法解決這些爲您準備。 – Brett

4

是否有可能使用單個MongoDB查詢獲取父對象及其$ ref?

不,這是不可能的。 Mongo對refs沒有內在的支持,所以它取決於您的應用程序來填充它們(請參閱Brett's answer)。

但是有可能通過單個node.js命令來獲取父對象及其所有引用?

是的,這是可能的。你可以用Mongoose來完成。它內置了裁判的人口支持。你需要稍微改變你的數據模型才能使它工作,但它幾乎是你想要的。當然,要做到這一點,Mongoose將完成與您所做的相同的兩個MongoDB查詢。

1

不,很少有MongoDb的驅動程序包含對DBRef的特殊支持。有兩個原因:

  1. MongoDb沒有任何特殊命令可以檢索引用文件。因此,增加支持的驅動程序人爲填充結果對象。
  2. API越「裸」,它就越有意義。事實上,如。 MongoDb集合是無模式的,如果NodeJs驅動程序帶回所有引用實現的主文檔,如果代碼然後保存文檔而不中斷引用,則會導致嵌入的子文檔。當然,那將是一團糟。

除非您的字段值不同,否則我不會打擾DBRef類型,而是直接存儲ObjectId。正如你所看到的,一個DBRef真的沒有什麼好處,除了每個引用需要大量重複的磁盤空間,因爲更豐富的對象必須與其類型信息一起存儲。無論哪種方式,您都應該考慮存儲包含引用集合文檔的字符串的潛在不必要開銷。

許多開發人員和MongoDb,Inc.都在現有基礎驅動程序之上添加了對象文檔映射層。 MongoDb和Nodejs的一個流行選項是Mongoose。由於MongoDb服務器沒有真正意識到引用文檔,引用的責任轉移到客戶端。由於持續引用特定文檔中的特定集合更爲常見,因此Mongoose可以將引用定義爲Schema。貓鼬不是無模式的。

如果您接受並使用架構有用,那麼Mongoose絕對值得一看。它可以從一組文檔中高效地獲取一批相關文檔(來自單個集合)。它總是使用本地驅動程序,但它通常非常高效地執行操作,並將更多複雜的應用程序體系結構中的一些苦差事帶走。

我強烈建議你看看populate方法(here),看看它能做什麼。

Demo  /* Demo would be a Mongoose Model that you've defined */ 
.findById(theObjectId) 
.populate('detail') 
.exec(function (err, doc) { 
    if (err) return handleError(err); 
    // do something with the single doc that was returned 
}) 

如果相反的findById,它總是返回一個單一的文件,find被使用,populate,所有返回的文檔details屬性將被自動填充。它也很聰明,它會多次請求相同的參考文檔。

如果您不使用Mongoose,我建議您考慮一個緩存層,以儘可能避免進行客戶端引用連接,並儘可能使用$in查詢運算符進行批處理。

+0

謝謝,我會看看貓鼬。 –

1

我到旁邊例如期望的結果:

collection.find({}, function (err, cursor) { 
    cursor.toArray(function (err, docs) { 
     var count = docs.length - 1; 
     for (i in docs) { 
      (function (docs, i) { 
       db.dereference(docs[i].ref, function(err, doc) { 
        docs[i].ref = doc; 
        if (i == count) { 
         (function (docs) { 
          console.log(docs); 
         })(docs); 
        } 
       }); 
      })(docs, i) 
     } 
    }); 
}); 

不知道它的解決方案是最好的最好的,但它是我發現最簡單的解決方案。作爲db.dereference方法是從MongoDB的API的NodeJS刪除弗拉基米爾