2011-03-21 60 views
380

我是新来的MongoDB - 来自关系数据库的背景。我想用一些评论来设计一个问题结构,但我不知道用哪个关系来评论:embedreferenceMongoDB关系:嵌入还是引用?

一些评论的问题,如stackoverflow,就会有这样的结构:

Question 
    title = 'aaa' 
    content = bbb' 
    comments = ??? 

起初,我想用嵌入式的意见(我认为embed MongoDB中建议),像这样:

Question 
    title = 'aaa' 
    content = 'bbb' 
    comments = [ { content = 'xxx', createdAt = 'yyy'}, 
       { content = 'xxx', createdAt = 'yyy'}, 
       { content = 'xxx', createdAt = 'yyy'} ] 

清楚,但我很担心这种情况下:如果我想编辑指定的评论,我怎么得到它的内容和它的问题吗?有没有_id让我找到一个,也没有question_ref让我找到它的问题。 (我这样的新手,我不知道是否有任何的方式来做到这一点没有_idquestion_ref

我一定要使用refembed?那么我必须创建一个新的评论集合?

+0

无论是否创建字段,所有Mongo对象都使用_ID创建。所以在技术上每个评论都会有一个ID。 – 2014-01-10 05:31:28

+16

@RobbieGuilfoyle不正确 - 请参阅http://stackoverflow.com/a/11263912/347455 – pennstatephil 2014-05-14 20:57:41

+6

我站在更正,谢谢@pennstatephil :) – 2014-05-15 13:23:29

回答

623

这是一门艺术而不是科学。该Mongo Documentation on Schemas是一个很好的参考,但这里有一些事情要考虑:

  • 把尽可能多的尽可能

    的文档数据库的乐趣在于它消除了大量加入的。你的第一本能应该尽可能多地放在单个文件中。由于MongoDB文档具有结构,并且因为您可以在该结构中有效地进行查询(这意味着您可以获取所需的文档部分,因此文档大小不应该太担心),因此不需要对数据进行标准化你会在SQL中。特别是除了母文档之外没有用的任何数据应该是同一文档的一部分。

  • 可以从多个地方引用到自己的集合中的单独数据。

    这不是一个“存储空间”问题,因为它是一个“数据一致性”问题。如果许多记录都会引用相同的数据,那么更高效且更少错误更新单个记录并在其他地方保留引用。

  • 文献尺寸考虑

    MongoDB中施加了4MB(16MB与1.8)上的单个文件的大小限制。在GB数据世界中,这听起来很小,但它也是3万个推文或250个典型的堆栈溢出答案或20个闪烁照片。另一方面,这是比一次想要在典型网页上呈现的信息多得多的信息。首先考虑一下会让你的查询更容易。在许多情况下,关于文档大小的问题将不成熟。

  • 复杂的数据结构:

    MongoDB中可以存储任意深嵌套的数据结构,但不能有效地搜索。如果您的数据形成树,森林或图形,则实际上需要将每个节点及其边缘存储在单独的文档中。(请注意,有些数据存储专门为此类数据设计,因此也应该考虑)

    它也有been pointed out比它不可能返回文档中元素的子集。如果您需要挑选每个文档的几个位,将它们分开更容易。

  • 数据一致性

    MongoDB中做出折衷效率和一致性之间。规则是对单个文档的更改为始终为原子,但对多个文档的更新永远不应被认为是原子的。也无法“锁定”服务器上的记录(可以使用例如“锁定”字段将其构建到客户端的逻辑中)。在设计模式时,考虑如何保持数据的一致性。一般来说,您保存在文档中的内容越多越好。

对于您所描述的内容,我将嵌入注释,并为每个注释提供一个带有ObjectID的id字段。 ObjectID中嵌入了时间戳,所以如果你喜欢,你可以使用它来代替创建。

+1

我想添加到OP问题:我的评论模型包含用户名和他的链接头像。考虑到用户可以修改他的名字/头像,最好的方法是什么? – user1102018 2013-02-05 09:36:17

+0

用户,我不确定在这种情况下“链接”是什么意思。如果可能,我想我会嵌入。 – 2013-02-06 00:48:08

+3

关于'复杂的数据结构',似乎可以使用聚合框架返回文档中的元素子集(尝试$ unwind)。 – 2013-09-23 10:33:08

36

如果我想编辑一个指定的评论,如何获取它的内容和它的问题?

您可以通过子文档查询:db.question.find({'comments.content' : 'xxx'})

这将返回整个问题文档。要编辑指定的评论,您必须在客户端上找到评论,进行编辑并将其保存回数据库。

一般来说,如果你的文档包含一个对象数组,你会发现这些子对象需要修改客户端。

+2

如果两个注释具有相同的内容,则这不起作用。有人可能会争辩说,我们也可以将作者添加到搜索查询中,如果作者用相同的内容创作两个相同的评论,那么这仍然行不通。 – 2015-07-24 22:45:49

+0

@SteelBrain:如果他保留了评论索引,则点符号可能会有所帮助。请参阅http://stackoverflow.com/a/33284416/1587329 – 2015-10-22 15:11:07

+3

我不明白这个答案有34个upvotes,第二个多个人评论整个系统会崩溃的相同的东西。这是一个绝对可怕的设计,永远不要使用。 @user这样做的方式是去 – user2073973 2017-03-23 09:30:51

15

我知道这是很老,但如果你正在寻找的答案OP的关于如何只返回指定的注释问题,你可以使用$ (query)操作是这样的:

db.question.update({'comments.content': 'xxx'}, {'comments.$': true}) 
+0

,如果两个评论具有相同的内容,这将不起作用。有人可能会争辩说,我们也可以将作者添加到搜索查询中,如果作者以相同的内容创作了两个相同的评论,这仍然不起作用 – 2015-07-24 22:46:14

13

好,我有点晚了,但仍然想分享我的模式创建方式。

我有一个可以用一个词来描述的所有事物的模式,就像你会在传统的OOP中做的那样。

E.G.

  • 评论
  • 帐户
  • 用户
  • 博文
  • ...

每个模式可以保存为一个文档或子文档,所以我宣布这对每个方案。

文献:

  • 可以用作参考。 (例如。用户发表了评论 - >评论具有“制作者”引用用户)
  • 是您的应用程序中的“根”。 (例如博客帖子 - >还有一个页面大约博客帖子)

子文档:

  • 只能使用一次/从来都不是一个参考。 (例如评论保存在博客帖子中)
  • 永远不会是你的应用程序中的“根”。 (注释只是表明了在博客帖子页面,但该页面仍看好博客帖子)
6

是的,我们可以用在document.To参考填充另一个文档就像SQL我joins.In蒙戈DB他们没有加入到映射一对多的关系document.Instead,我们可以使用填充履行我们的场景..

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' }] 
}); 

人口与文件自动替换文档中的指定路径的过程(S )来自其他收藏。我们可以填充单个文档,多个文档,普通对象,多个普通对象或从查询返回的所有对象。我们来看一些例子。

你可以更好的了解更多信息,请访问

http://mongoosejs.com/docs/populate.html

+2

Mongoose将为每个填充的字段发出单独的请求。这与SQL JOINS不同,因为它们在服务器上执行。这包括应用程序服务器和mongodb服务器之间的额外流量。再次,你可能会考虑这一点,当你优化。尽管如此,你的接吻者仍然是正确的。 – Max 2015-12-02 14:46:51

19

在一般情况下,嵌入是好的,如果你有一个一对一或一对许多实体之间的关系,并引用是好的,如果你有很多一对多的关系。

+3

你可以添加一个参考链接吗?谢谢。 – db80 2015-11-25 09:45:06

0

如果我想编辑一个指定的评论,我该如何得到它的内容和 它的问题?

如果你一直保持轨道的评论数量,你想改变注释的指数,你可以使用the dot operatorSO example)。

你可以做f.ex.

db.questions.update(
    { 
     "title": "aaa"  
    }, 
    { 
     "comments.0.contents": "new text" 
    } 
) 

(作为另一种方式来编辑的问题里面的评论)我碰到这个小演示出来,同时研究我自己这个问题

13

。我很惊讶它的布局如何,它的信息和介绍。

http://openmymind.net/Multiple-Collections-Versus-Embedded-Documents

它概括为:

作为一般规则,如果你有很多的[子文档]或如果他们是大的,一个单独的集合可能是最好的。

较小和/或较少的文档倾向于自然适合嵌入。

+1

“很多”是多少? 3? 10? 100?什么是'大'? 1KB? 1MB? 3个字段? 20场?什么是“较小”/“较少”? – Traxo 2017-10-24 13:07:27

+1

这是一个很好的问题,我没有具体的答案。同样的演示文稿包含一张幻灯片,上面写着“一个文档,包括其所有嵌入式文档和数组,不能超过16MB”,因此可能是您的临界值,或者只是针对您的具体情况采取合理/舒适的操作。在我目前的项目中,大多数嵌入式文档都是1:1关系,或1:很多嵌入式文档非常简单的地方。 – 2017-10-24 21:01:52

+0

另请参阅@ john-f-miller当前的最新评论,该评论尽管没有提供针对阈值的具体数字,但确实包含了一些可帮助指导您的决定的额外指针。 – 2017-10-24 21:05:45

0

这取决于文档的用法。当你使用文档时,如果你总是使用注释,使用嵌入的最佳方式。但你应该考虑最大的文件大小(16MB)。