標籤:
原文地址
接上一篇
四、模型樹結構父引用的模型樹結構
這個資料模型描述了一個樹形結構,在子節點中儲存父節點的引用。
模式
父參考模式儲存每個樹節點到文檔中,除了樹節點外,文檔還儲存了父節點的id。
考慮以下目錄的層級關係。
以下為應用執行個體
db.categories.insert( { _id: "MongoDB", parent: "Databases" } )db.categories.insert( { _id: "dbm", parent: "Databases" } )db.categories.insert( { _id: "Databases", parent: "Programming" } )db.categories.insert( { _id: "Languages", parent: "Programming" } )db.categories.insert( { _id: "Programming", parent: "Books" } )db.categories.insert( { _id: "Books", parent: null } )
查詢一個節點的父節點變得快速且直觀
db.categories.findOne( { _id: "MongoDB" } ).parent
還可以對parent欄位建立索引以提高查詢速度
db.categories.createIndex( { parent: 1 } )
通過parent欄位查詢其直接子節點
db.categories.find( { parent: "Databases" } )
這種父參考模式是實現樹儲存的簡單方法,缺點是擷取子樹時則需要多個查詢。
子引用的模型樹結構
這個資料模型描述了樹形結構,儲存子節點的引用到父節點中。
模式
子參考模式儲存每個樹節點到一個文檔中,除了樹節點,文檔還儲存了包含其子節點id的數組。
考慮與上一個圖中相同的目錄層級關係,其子參考模式的實現如下
db.categories.insert( { _id: "MongoDB", children: [] } )db.categories.insert( { _id: "dbm", children: [] } )db.categories.insert( { _id: "Databases", children: [ "MongoDB", "dbm" ] } )db.categories.insert( { _id: "Languages", children: [] } )db.categories.insert( { _id: "Programming", children: [ "Databases", "Languages" ] } )db.categories.insert( { _id: "Books", children: [ "Programming" ] } )
查詢擷取一個節點的直接子節點則變得快速且直觀
db.categories.findOne( { _id: "Databases" } ).children
建立children欄位上的索引以實現快速查詢
db.categories.createIndex( { children: 1 } )
通過children資訊查詢其父節點以及兄弟節點資訊
db.categories.find( { children: "MongoDB" } )
子參考模式提供了一種樹儲存的合適方案,只要不需要對子樹操作就行(單個查詢無法保證擷取一個節點的所有子節點)。對於儲存映像,其中一個節點可能有多個父節點,那這個模式也是很好的方案。
祖先數組的模型樹結構
這個資料模型描述了樹形結構,使用對父節點的引用和一個數組來儲存所有的祖先節點。
模式
文檔中除了儲存每個樹節點,還儲存了一個數組,其他暴露了所有祖先節點的id或者路徑(祖先節點的id按順序排序組成路徑)。
考慮跟上面圖中相同的目錄層級關係
以下實際執行個體中,文檔除了ancestors欄位,還有parent欄位,parent欄位儲存了直接父節點的引用。
db.categories.insert( { _id: "MongoDB", ancestors: [ "Books", "Programming", "Databases" ], parent: "Databases" } )db.categories.insert( { _id: "dbm", ancestors: [ "Books", "Programming", "Databases" ], parent: "Databases" } )db.categories.insert( { _id: "Databases", ancestors: [ "Books", "Programming" ], parent: "Programming" } )db.categories.insert( { _id: "Languages", ancestors: [ "Books", "Programming" ], parent: "Programming" } )db.categories.insert( { _id: "Programming", ancestors: [ "Books" ], parent: "Books" } )db.categories.insert( { _id: "Books", ancestors: [ ], parent: null } )
查詢擷取一個節點的祖先節點或者路徑則變得快速而直觀
db.categories.findOne( { _id: "MongoDB" } ).ancestors
對ancestors欄位建立索引提高查詢速度
db.categories.createIndex( { ancestors: 1 } )
查詢ancestors欄位以尋找它的所有後代節點
db.categories.find( { ancestors: "Programming" } )
祖先數組模式提供了查詢某節點的後代節點以及某節點的祖先節點的有效方案,所以當需要對子樹節點進行操作時,這個模式是一個很好的選擇。
祖先數組模式比具體化路徑(Materialized Paths)模式稍慢,但是使用更直觀。
具體化路徑(Materialized Paths)的模型樹結構
這個資料模型描述了樹形結構,儲存文檔間的全關係路徑。
模式
具體化路徑中,文檔除了儲存樹節點之外,還儲存了節點的祖先的id或者路徑。儘管具體化路徑模式要求額外的步驟處理字串和Regex,這個模式也提供了處理路徑的靈活性,如通過部分路徑尋找節點。
考慮與上面的圖中相同的目錄層級關係
具體化路徑模式中,儲存路徑到path欄位中,路徑使用逗號作為分隔字元
db.categories.insert( { _id: "Books", path: null } )db.categories.insert( { _id: "Programming", path: ",Books," } )db.categories.insert( { _id: "Databases", path: ",Books,Programming," } )db.categories.insert( { _id: "Languages", path: ",Books,Programming," } )db.categories.insert( { _id: "MongoDB", path: ",Books,Programming,Databases," } )db.categories.insert( { _id: "dbm", path: ",Books,Programming,Databases," } )
通過path欄位查詢所有的節點
db.categories.find().sort( { path: 1 } ) // 按path的升序
對path欄位使用Regex尋找Programming的後代
db.categories.find( { path: /,Programming,/ } ) // 路徑包含",Programming,"
尋找Books的後代,因為Books是最高節點,故Regex以",Books,"開頭
db.categories.find( { path: /^,Books,/ } )
對path欄位建立索引
db.categories.createIndex( { path: 1 } )
根據具體查詢,這個索引可能會提高效能:
- 對於根節點Books的子樹上的查詢(例如,/^,Books,/ 或者 /^,Books,Programming,/),path欄位的索引會明顯提高查詢效能。
- 對於未提供到根節點的路徑的子樹上的查詢(例如, /,Databases,/),或者類似的子樹查詢,節點在加索引的字串的中間,這時查詢必須掃描所有索引。對這些查詢來說,如果索引比整個集合小很多,則索引會提高查詢效能。
內嵌集(Nested Sets)的模型樹結構
這個資料模型描述了樹形結構,最佳化了尋找子樹的效能,但是也會導致樹的易變性的增加。
模式
內嵌集模式對樹作一個往返遍曆並標示樹中每個節點為停留點。應用程式對每個節點訪問兩次,第一次為去往遍曆,第二次為返回遍曆。內嵌集模式中文檔除了儲存樹節點,還儲存了其父節點的id,儲存其去往停留點到left欄位,以及儲存返回停留點到right欄位。
考慮如下目錄的層級關係
下面代碼示範了內嵌集的實現
db.categories.insert( { _id: "Books", parent: 0, left: 1, right: 12 } )db.categories.insert( { _id: "Programming", parent: "Books", left: 2, right: 11 } )db.categories.insert( { _id: "Languages", parent: "Programming", left: 3, right: 4 } )db.categories.insert( { _id: "Databases", parent: "Programming", left: 5, right: 10 } )db.categories.insert( { _id: "MongoDB", parent: "Databases", left: 6, right: 7 } )db.categories.insert( { _id: "dbm", parent: "Databases", left: 8, right: 9 } )
查詢擷取一個節點的後代
var databaseCategory = db.categories.findOne( { _id: "Databases" } );db.categories.find( { left: { $gt: databaseCategory.left }, right: { $lt: databaseCategory.right } } );
內嵌集模式提供了為尋找子樹的一個快速並有效率的方案,但是在修改樹結構時較為麻煩。故這種模式適合不會改變結構的靜態樹。
五、模型特定的程式上下文原子操作的模型資料
MongoDB中的寫操作,例如db.collection.update(),db.collection.findAndModify(),db.collection.remove()在單個文檔層級是原子的。對於那些需要一起被更新的欄位,將欄位嵌入到文檔中可以確保對這些欄位的更新是原子的。
例如,考慮這樣一種情況,當需要維護書的資訊時,包括可以檢出的書數量以及當前檢出資訊,如何確定這兩個操作整體的原子性?
可獲得的書和檢出資訊必須同步。這樣,將available欄位和checkout欄位嵌入相同的文檔以確保更新文檔是原子性的。
{ _id: 123456789, title: "MongoDB: The Definitive Guide", author: [ "Kristina Chodorow", "Mike Dirolf" ], published_date: ISODate("2010-09-24"), pages: 216, language: "English", publisher_id: "oreilly", available: 3, checkout: [ { by: "joe", date: ISODate("2012-10-15") } ]}
那麼,根據新的檢出資訊,可以使用db.collection.update()進行更新,並且對available欄位和checkout欄位的更新是原子性的。
db.books.update ( { _id: 123456789, available: { $gt: 0 } }, //查詢條件 { $inc: { available: -1 }, //available欄位減1 $push: { checkout: { by: "abc", date: new Date() } } // checkout欄位數組增加一個元素 })
以上操作返回一個WriteResult()對象,包含了操作的有關資訊
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
nMatched欄位表明1個文檔匹配這個更新條件,nModified表明這個操作更新了一個文檔。
支援關鍵字查詢的模型資料
關鍵字搜尋與文本搜尋或者全文檢索搜尋不同,它不提供詞幹提取或者其他文本處理特性。
此模式描述了支援關鍵字搜尋的方法,以支援程式搜尋功能,這個搜尋方法使用儲存在相同文檔中的數組中的關鍵字,而這個數組作為這個文檔的文字欄位。與多鍵索引結合後,這個模式可以支援程式的關鍵字搜尋操作。
模式
為了向文檔中增加結構以支援基於關鍵字的查詢,首先向文檔中建立一個數組欄位,並將關鍵字以字串形式添加到數組中。然後可以對這個數組建立多鍵索引,並建立查詢從這個數組中選擇數值。
執行個體:
給定一個圖書卷的集合,並想提供一個機遇主題的搜尋。對每個卷而言,我們增加topics數組,並儘可能的對這個卷添加關鍵字到數組中。對Moby-Dick卷可以由如下文檔表示,
{ title : "Moby-Dick" , author : "Herman Melville" , published : 1851 , ISBN : 0451526996 , topics : [ "whaling" , "allegory" , "revenge" , "American" , "novel" , "nautical" , "voyage" , "Cape Cod" ]}
然後對topics數組建立多鍵索引
db.volumes.createIndex( { topics: 1 } )
多鍵索引對topics數組中的每個關鍵字建立索引項目。例如,索引中有一個是關於"whaling"的索引項目,另一個是關於"allegory"的索引項目。
然後基於關鍵字的查詢樣本如下
db.volumes.findOne( { topics : "voyage" }, { title: 1 } )
提示:數組中的元素數量很大時,如有幾百或上千的關鍵字,那插入操作會導致加索引花費很大。
關鍵字索引的限制
使用特定的資料模型和多鍵索引,MongoDB可以支援關鍵字搜尋。然而,這些關鍵字索引在以下方面與全文檢索搜尋比較則顯得不足或者無法相比:
- 詞幹提取。關鍵字查詢無法解析關鍵字為根或者有關詞語。
- 同義。關鍵字搜尋特性必須在應用程式層提供同義支援或相關查詢。
- 排序。關鍵字查詢不提供判斷結果權重的方式。
- 非同步索引。MongoDB同步建立索引,這意味著為關鍵字使用的索引總是處於當前的並可即時操作。然而,非同步建立的索引在某種內容和工作負載下效率更高。
MongoDB資料模型(二)