我是MongoDB的新手 - 来自关系数据库背景。我想设计一个包含一些评论的问题结构,但我不知道哪个关系用于评论:embed
或reference
?
有一些评论的问题,例如stackoverflow,会有这样的结构:
Question
title = 'aaa'
content = bbb'
comments = ???
首先,我想使用嵌入式注释(我认为MongoDB中推荐使用embed
),如下所示:
Question
title = 'aaa'
content = 'bbb'
comments = [ { content = 'xxx', createdAt = 'yyy'},
{ content = 'xxx', createdAt = 'yyy'},
{ content = 'xxx', createdAt = 'yyy'} ]
很清楚,但我担心这种情况:如果我想编辑指定的评论,我该如何获取其内容及其问题?没有_id
到让我找一个,question_ref
让我找到它的问题。 (我是新手,我不知道在没有_id
和question_ref
的情况下是否有任何办法可以做到这一点。)
我必须使用ref
而不是embed
吗?然后我必须创建一个新的评论集合?
答案 0 :(得分:708)
这更像是一门艺术,而不是一门科学。 Mongo Documentation on Schemas是一个很好的参考,但有些事情需要考虑:
尽可能多地放入
文档数据库的乐趣在于它消除了大量的连接。你的第一直觉应该是尽可能多地放在一个文件中。因为MongoDB文档具有结构,并且因为您可以在该结构中有效地进行查询(这意味着您可以获取所需文档的一部分,因此文档大小不应该让您担心太多)没有立即需要将数据标准化你会在SQL中。特别是除了父文档之外没有用的任何数据都应该是同一文档的一部分。
将可以从多个地方引用的数据分离到自己的集合中。
这不是一个“存储空间”问题,因为它是一个“数据一致性”问题。如果许多记录将引用相同的数据,那么更新单个记录并在其他地方保留对它的引用会更有效且更不容易出错。
文档大小注意事项
MongoDB对单个文档施加了4MB(16MB,1.8)大小限制。在GB数据的世界中,这听起来很小,但它也是3万条推文或250个典型的Stack Overflow答案或20张闪烁的照片。另一方面,这比在一个典型的网页上一次可能想要呈现的信息要多得多。首先考虑什么会使您的查询更容易。在许多情况下,对文档大小的关注将是过早优化。
复杂的数据结构:
MongoDB可以存储任意深层嵌套数据结构,但不能有效地搜索它们。如果您的数据形成树,林或图形,则实际上需要将每个节点及其边缘存储在单独的文档中。 (请注意,还有专门为此类数据设计的数据存储,也应该考虑)
它还有been pointed out,而不可能返回文档中的元素子集。如果您需要挑选每个文档的几个部分,将它们分开会更容易。
数据一致性
MongoDB在效率和一致性之间进行权衡。规则是对单个文档的更改始终原子,而对多个文档的更新永远不应该被假定为原子。也无法“锁定”服务器上的记录(您可以使用例如“锁定”字段将其构建到客户端的逻辑中)。在设计架构时,请考虑如何保持数据的一致性。通常,您在文档中保留的越多越好。
对于您所描述的内容,我会嵌入注释,并为每个注释指定一个带有ObjectID的id字段。 ObjectID中嵌入了一个时间戳,因此您可以使用它而不是根据需要创建。
答案 1 :(得分:32)
如果我想编辑指定的评论,如何获取其内容及其问题?
您可以按子文档查询:db.question.find({'comments.content' : 'xxx'})
。
这将返回整个问题文档。要编辑指定的注释,您必须在客户端上找到注释,进行编辑并将其保存回数据库。
通常,如果您的文档包含一个对象数组,您会发现这些子对象需要在客户端进行修改。
答案 2 :(得分:29)
一般来说,如果你在实体之间有一对一或一对多的关系,嵌入是好的,如果你有多对多的关系,那么引用是好的。
答案 3 :(得分:19)
好吧,我有点迟了但仍想分享我的架构创建方式。
我有一些可用一个词描述的模式,就像你在古典OOP中所做的一样。
E.G。
每个架构都可以保存为Document或Subdocument,因此我为每个架构声明了这个。
文件:
子文档:
答案 4 :(得分:17)
我知道这已经很老了但是如果你正在寻找关于如何只返回指定评论的OP问题的答案,你可以像这样使用$ (query)运算符:
db.question.update({'comments.content': 'xxx'}, {'comments.$': true})
答案 5 :(得分:17)
我在自己研究这个问题时遇到了这个小小的演讲。我很惊讶它的布局,信息和它的呈现方式。
http://openmymind.net/Multiple-Collections-Versus-Embedded-Documents
总结道:
作为一般规则,如果你有很多[子文件]或者它们很大,那么单独的收藏可能是最好的。
较小和/或较少的文档往往非常适合嵌入。
答案 6 :(得分:10)
是的,我们可以使用文档中的引用。就像sql i join一样填充另一个文档。在mongo db中,他们没有连接来映射一个到多个关系文档。相反我们可以使用 populate < / strong>实现我们的方案..
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var personSchema = Schema({
_id : Number,
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
_creator : { type: Number, ref: 'Person' },
title : String,
fans : [{ type: Number, ref: 'Person' }]
});
填充是使用其他集合中的文档自动替换文档中指定路径的过程。我们可以填充单个文档,多个文档,普通对象,多个普通对象或从查询返回的所有对象。我们来看一些例子。
最好能获得更多信息,请访问:http://mongoosejs.com/docs/populate.html
答案 7 :(得分:1)
如果我想编辑指定的评论,我该如何获取其内容和 这个问题?
如果您已经跟踪了要更改的评论数量和评论索引,可以使用the dot operator(SO example)。
你可以做f.ex。
db.questions.update(
{
"title": "aaa"
},
{
"comments.0.contents": "new text"
}
)
(作为编辑问题内部评论的另一种方式)
答案 8 :(得分:1)
实际上,我很好奇为什么没人谈论UML规范。经验法则是,如果有聚合,则应使用引用。但是,如果它是一个组合,那么耦合会更强,并且应该使用嵌入式文档。
您将很快理解它为什么合乎逻辑。如果对象可以独立于父对象而存在,那么即使父对象不存在,您也要对其进行访问。由于您无法将其嵌入到不存在的父级中,因此必须使其活在它自己的数据结构中。如果存在父对象,只需在父对象中添加对象的引用即可将它们链接在一起。
真的不知道两者之间的区别是什么? 这是解释它们的链接: Aggregation vs Composition in UML
答案 9 :(得分:0)
我创建了此测验作为参考,以了解您是否应该使用一个或另一个
答案 10 :(得分:0)
MongoDB赋予了无需架构的自由,如果考虑周全或计划不周,此功能可能会长期造成痛苦。
有2个嵌入选项或“引用”选项。由于上面的答案对它们进行了很好的定义,因此我将不进行定义。
嵌入时,您应该回答一个问题:您的嵌入式文档将要增长,如果是,则增长多少(请记住,每个文档限制为16 MB)。因此,如果您对帖子发表评论,评论数限制,如果该帖子传播迅速并且人们开始添加评论。在这种情况下,引用可能是一个更好的选择(但甚至可以增加引用并达到16 MB的限制)。
因此,如何平衡它,答案是不同模式的组合,检查这些链接并根据用例创建自己的混搭。
https://www.mongodb.com/blog/post/building-with-patterns-a-summary
https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design-part-1