標簽:trait lan summary mongo rom 完整 技術 mon 模型設計
本系列教程目錄:
MongoDB入門實戰教程(1)
MongoDB入門實戰教程(2)
MongoDB入門實戰教程(3)
MongoDB入門實戰教程(4)
MongoDB入門實戰教程(5)
MongoDB入門實戰教程(6)
前面我们学习了聚合查询,本篇我们来看看在模型設計中如何应用引用模式来提高查询效率。
在进行MongoDB的模型設計中,基于JSON文档模型,我们很容易就可以设计出一个内嵌模式的文档模型出来。
可以不誇張地說,80%~90%的场景下,我们优先都会使用内嵌对象 或 内嵌数组 的方式来设计文档模型的所谓的1-1、1-N、N-N的关系。
例如下面這個Contacts的文檔模型,它描述了一個聯系人的關系建模:
Contacts { name: "Edison Zhou", company: "CSCEC YZW", title: ".NET Engineer", portraits: { mimetype: xxx, data: xxxx }, addresses: [ { type: home, … }, { type: work, … } ], groups: [ {name: "YZW Football Assocation" }, {name: "YZW .NET Assocation" } ] }
可以看到,所谓的内嵌类 其实 类似于 預先聚合(關聯),這樣的操作(引用+冗余)其實對讀操作更有性能優勢。
但是,內嵌設計有一個大前提限制:即內嵌後文檔大小不能超過16MB。
此外,如果內嵌的數組(通常是數組)的長度太大,比如數萬或更多的時候,也是不適合采用內嵌模式的。
那麽,此時我們應該怎麽設計呢?
這裏我們仍然適用上面提到的Contacts模型,假設其中的groups是一個內嵌數組,這個groups的數據可能有百萬級的長度,且每個Contacts文檔都需要冗余這麽一份數據,而且groups數據還面臨著頻繁修改的需求。
Contacts { name: "Edison Zhou", company: "CSCEC YZW", title: ".NET Engineer", ...... // 假設下面groups有百萬級,且一個group的信息改動會引發百萬級的DB操作 groups: [ {name: "YZW Football Assocation" }, {name: "YZW .NET Assocation" } ] }
解决方案很简单,就是针对groups使用单独的collection来存储,在Contancts模型中添加对group id的集合的引用。
Collection 1 - Contacts:
Contacts { name: "Edison Zhou", company: "CSCEC YZW", title: ".NET Engineer", ...... // 假設下面groups有百萬級,且一個group的信息改動會引發百萬級的DB操作 group_ids: [1,2,3,4,5...] }
Collection 2 - Groups:
Groups
{
groups_id,
name
}
这样的设计其实类似于关系型數據庫模型的设计,用Id来关联,我们再熟悉不过了。
但是,在MQL中,我们就需要额外使用$lookup来实现类似SQL中的关联查询了,严格来说,应该算是LEFT OUTER JOIN查询。
嗯,這又是一種聚合操作:
db.Contacts.aggregate([ { $lookup: { from: "groups", localField: "group_ids", foreignField: "group_id", as: "groups" } }]);
這個查詢會得到如下圖所示的結果:
上面講解了如何通過MQL進行操作,那麽,在.NET中如何實現$lookup的效果呢?
好在MongoDB Driver已经帮我们提供了这样的一个LookUp,且看下面的代码示例:
假設我們的實體定義如下:
public class Contact { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } public string Name { get; set; } public string Company { get; set; } public string Title { get; set; } public int[] GroupIds { get; set; } public IList<Group> Groups { get; set; } } public class Group { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } public int GroupId { get; set; } public string Name { get; set; } }
那麽,可以通過Driver實現以下操作:
public async Task<IList<Contact>> GetAsync() { return await _contacts .Aggregate() .Lookup<Contact, Group, Contact>( _groups, local => local.GroupIds, from => from.GroupId, result => result.Groups) .ToListAsync(); }
完整示例github地址:https://github.com/EdisonChou/EDT.Mongo.Sample
運行結果如下所示:
綜上所述,當滿足以下條件之一時,你可以開始考慮引用模式設計文檔模型:
(1)當內嵌後的文檔太大,有可能超過16MB限制的時候;
(2)内嵌的文档 或 数组元素 有可能会频繁修改的时候;
(3)内嵌数组元素 有可能会持续增长且没有封顶的时候;
引用模式也並非銀彈,它存在以下一些限制:
(1)MongoDB對于使用引用的集合之間沒有所謂的外鍵檢查;
(2)MongoDB使用聚合框架的$lookup來模仿關聯查詢;
(3)$lookup只支持LEFT OUTER JOIN,且关联目标(from)不能是分片表;
db.Contacts.aggregate([ { $lookup: { from: "groups", // 這裏的from不能是分片表 ...... } }]);
本文简单介绍了MongoDB的模型設計中的内嵌模式和引用模式,探讨了引用模式的使用、何时使用 及 使用限制。
下一篇,我們會學習MongoDB的模式設計中的一些設計模式並套用這些設計模式簡化設計難度。
唐建法,《MongoDB高手課》(極客時間)
郭遠威,《MongoDB實戰指南》(圖書)
△推薦訂閱學習
作者:周旭龍
出處:https://edisonchou.cnblogs.com
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
標簽:trait lan summary mongo rom 完整 技術 mon 模型設計
原文地址:https://www.cnblogs.com/edisonchou/p/mongodb_learning_summary_part7.html