重新回答这个问题,因为原始答案由于阅读错误而将关系颠倒了。
问题 = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}
关于是否嵌入一些关于工单创建者的重要信息是一个明智的决定还是不明智的决定,取决于系统的具体情况。
如果您让这些用户能够登录并报告他们发现的问题,那么您可能希望将该关系拆分到一个用户集合中。
另一方面,如果不是这种情况,那么您可以轻松地使用这个模式。我在这里看到的一个问题是,如果您希望联系报告人,而他们的工作角色发生了变化,那就有点尴尬;然而,这是一个现实世界的困境,而不是数据库的困境。
由于子文档表示与报告人之间的一对一关系,您也不应该遇到我原来答案中提到的碎片化问题。
这个模式存在一个明显的问题,即重复数据的重复更改(规范化形式的问题)。
让我们举个例子。想象一下,你遇到了我之前提到的现实世界的困境,一个名叫Nigel的用户希望他的角色从现在开始反映他的新工作职位。这意味着你必须更新所有Nigel是报告人的行,并将他的角色更改为那个新职位。对于MongoDB来说,这可能是一个耗时和资源消耗大的查询。
再次自相矛盾,如果每个用户只有大约100个票(即可管理的内容),那么更新操作可能不会太糟糕,并且实际上对于数据库来说很容易管理;此外,由于文档(希望如此)缺乏移动,因此这将是完全就地更新。
因此,无论是否应该嵌入,都严重取决于您的查询和文档等,但是,我认为这种模式不是一个好主意;具体原因是在许多根文档中更改数据的重复。从技术上讲,是的,你可以做到,但我不会尝试。
我会把这两个分开。
引用:
如果我的文档中有对象(子文档),我可以在单个查询中更新它们吗?
就像我原始答案中的关系样式一样,是的,而且很容易。
例如,让我们更新
Nigel
的角色为
MD
(如之前所示),并将工单状态更改为已完成:
db.tickets.update({'reporter.username':'Nigel'},{$set:{'reporter.role':'MD', status: 'completed'}})
因此,在这种情况下,单个文档模式确实使CRUD更容易。
需要注意的一件事是,根据您的英语,您不能使用位置运算符更新根文档下的所有子文档。相反,它只会更新找到的第一个子文档。
再次希望这有意义,我没有漏掉任何内容。祝好运!
原始答案
这里我有一个与问题相关的用户。我应该创建另一个名为“用户”的文档,并通过其ID在“问题”文档中引用它(就像在关系数据库中一样),还是应该将所有用户数据放在子文档中?
这是一个值得考虑的问题,在继续之前需要一些背景知识。
首先要考虑的是问题的大小:
issue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}
这个文本并不是很大,而且由于您不再需要reporter
信息(该信息将在根文档中),因此它可能会更小,但是问题从来都不是那么简单。例如,如果您查看MongoDB JIRA:https://jira.mongodb.org/browse/SERVER-9548(作为证明我的观点的随机页面),一个“票”的内容实际上可能相当可观。
唯一能够从嵌入式票据中获得真正好处的方法是,如果您可以将所有用户信息存储在单个16 MB的连续存储块中,这是BSON文档的最大大小(目前由mongod
强制执行)。
我认为您无法将所有票据存储在单个用户下。
即使您将票据缩小到一个代码、标题和描述,您仍然可能遭受由于 MongoDB 中文档的定期更新和更改所导致的“瑞士奶酪”问题,就像这个:
http://www.10gen.com/presentations/storage-engine-internals 是我所说的内容的好参考。
通常情况下,用户将多个票据添加到其根用户文档中,您会看到这个问题。这些票据本身也会发生变化,但可能不会以剧烈或频繁的方式发生变化。
当然,您可以通过使用2的幂次分配大小的能力
http://docs.mongodb.org/manual/reference/command/collMod/#usePowerOf2Sizes来在一定程度上解决这个问题。
好吧,假设您只有
code
和
title
,那么是的,您可以将票据作为子文档存储在根用户中而不会遇到太多问题,但是,这取决于赏金任务的具体要求,目前还未提及。
如果我在一个文档中有对象(子文档),我可以使用单个查询更新它们吗?
是的,非常容易。这是嵌套时变得更容易的一件事情。你可以使用以下查询:
db.users.update({user_id:uid,'tickets.code':'asdf-1'}, {$set:{'tickets.$.title':'Oh NOES'}})
需要注意的是,您只能使用位置操作符一次更新一个子文档。因此,这意味着您不能在单个原子操作中将单个用户的所有票据日期更新为未来5天。
至于添加新票据,那很简单:
db.users.update({user_id:uid},{$push:{tickets:{code:asdf-1,title:"Whoop"}}})
是的,根据您的查询,您可以非常简单地在单个调用中更新整个用户数据。
这是一个相当长的答案,希望我没有漏掉任何东西,希望能帮到您。