MongoDB支持丰富的文档,其中可以包括嵌入式文档。 此功能很好地体现了“ 具有”关系,并且如果建模正确,由于Mongo中没有联接,因此可以减少确定某些数据所需的查找次数。
作为将文档集合嵌入父文档中的经典示例,是与人相关联的联系地址(即,邮件,电子邮件,推特等)。 想想名片。 当然,您可以采用多种方式对此进行建模-在传统的关系世界中,这将是至少两个表之间的一对多关系。 然而,随着面向文档的数据库,你可以模拟一个父person
用的嵌入式集合文件contacts
类型 (即手机,微博,电子邮件)都包含,比如说,每一个自己的文件和值 (这可能是555-555- 555,@ jon_doe等)。
如果子嵌入文档不需要在其父文档之外存在,则与Mongo的这种关系会很好地工作。 对于名片,例如代表电话号码的联系文档在其所属人员的上下文之外不一定有意义。 通过这种关系,您可以轻松地通过他/她的电话号码找到一个特定的人(也就是说,通过Mongo的查询语言,您可以轻松地通过点号到达内部数组)。 而且,一旦您可以与某个人打交道,就无需执行一系列查找即可确定联系信息–一切就在这里。
但是,如果您只想对单个嵌入式文档进行操作,事情就会很快变得很痛苦。 也就是说,如果您执行旨在处理预期的嵌入式结果文档的发现,那么您将需要进行一些工作:从Mongo 2.2开始,您无法通过以下方式从位于父级的集合中选择单个文档:查询。 在这种情况下,查找将提取所有内容 –筛选内容取决于您(即您的应用程序)。
一个示例可能会有所帮助:想象一下以前的名片示例-包含嵌入式contacts
集合的person
文档:
{
first_name: 'Andrew',
last_name: 'Glover',
contacts: [
{
type: 'cell',
value: '555-555-5555',
last_updated: 2012-09-01 23:41:51 UTC
},
{ type: 'home',
value: '555-555-5551',
last_updated: 2012-02-11 12:21:11 UTC
}
]
}
通过电话号码查找此文档很容易:
db.persons.find({'contacts.value':'555-555-5555'})
但是,如果您想查找最近更新的contact
(例如自本月初以来)并更改其值或添加一些其他元数据怎么办? 您想要的查询如下所示:
db.persons.find({'contacts.last_updated': {$gte: datetime(2012, 8, 1)}})
该查询有效,并且将匹配“ Andrew Glover”人-但是这里要注意的是返回的是整个文档。 您可以根据需要添加查询限制器(即{contacts:1}
),但这只会返回仅包含一系列contacts
的person
文档。 这样,您就可以遍历最终的contacts
集合并以这种方式工作。 也就是说,您仍然必须找到本月已编辑的联系文档! 在您的代码中!
没什么大不了,你说呢? 实际上,这个特定示例有些虚构。 但是,请想象一下,如果整个文档很大(也许不是一个person
而是一个organization
!),并且嵌入式文档集也很长(Google有多少名员工?)。 现在,这个简单的更新正在拉动很多字节(并在此过程中增加了Mongo的负担),然后您的应用程序正在使用大量字节的内存(现在文档正在使您的应用程序负担!)。 您是否也想在负载下快速进行此操作?
因此,嵌入文档的集合,如果你设想不得不在隔离特定的嵌入式文件的工作,最好是,在这一点上,以模型,一个具有鲜明的集合关系(即在这个例子中,生活会是否更容易有一个person
集合和一个contacts
集合)。 确实,面向文档的,无模式的数据存储的灵活性是快速进化发展的福音。 但是您仍然必须先做一些思考。 当然,除非您是受虐狂。
我是Mongo的忠实粉丝。 查看我完成的一些文章,视频和播客,这些文章,视频和播客集中在Mongo上,包括:
- Java开发2.0:MongoDB:带有(正确的)RDBMS的NoSQL数据存储
- 视频演示:MongoDB简介
- MongoDB上的Eliot Horowitz
- 10gen的Steve Francia与MongoDB交谈
参考: 《沟渠中的MongoDB》: The Disco Blog博客中来自JCG合作伙伴 Andrew Glover的受虐嵌入式集合 。