Mongoose填充vs对象嵌套

时间:2014-06-07 11:10:47

标签: node.js mongodb mongoose

使用Mongoose population和直接对象包含之间是否存在任何性能差异(查询的处理时间)?应该何时使用?

猫鼬人口例子:

var personSchema = Schema({
  _id     : Number,
  name    : String,
  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = Schema({
  _creator : { type: Number, ref: 'Person' },
  title    : String,
});

Mongoose对象嵌套示例:

var personSchema = Schema({
  _id     : Number,
  name    : String,
  stories : [storySchema]
});

var storySchema = Schema({
  _creator : personSchema,
  title    : String,
});

1 个答案:

答案 0 :(得分:106)

首先要了解猫鼬群体,它不是魔术,而只是一种方便的方法,可以让你自己检索相关信息。

该概念主要用于您决定需要将数据放在单独的集合中而不是嵌入该数据的情况,并且您的主要考虑因素通常应该是文档大小或相关信息经常更新的地方这将使维护嵌入式数据变得笨拙。

&#34>不是魔术"部分是基本上发生在封面上的是当你"引用"另一个来源,populate函数对该"相关的"进行额外的查询/查询。收集,以便"合并"您检索到的父对象的结果。你可以自己做,但方法是为了方便简化任务。显而易见的"表现"考虑的是,没有一次往返数据库(MongoDB实例)以便检索所有信息。总有不止一个。

作为样本,请选择两个集合:

{ 
    "_id": ObjectId("5392fea00ff066b7d533a765"),
    "customerName": "Bill",
    "items": [
        ObjectId("5392fee10ff066b7d533a766"),
        ObjectId("5392fefe0ff066b7d533a767")
    ]
}

项目:

{ "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 }
{ "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }

" best"这可以通过"引用"来完成。模型或使用populate(引擎盖下)是这样的:

var order = db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
order.items = db.items.find({ "_id": { "$in": order.items } ).toArray();

所以很明显"至少"两个查询和操作,以便加入"那个数据。

嵌入概念本质上是MongoDB如何处理不支持" 1 的答案。因此,相反,将数据拆分为规范化集合,您尝试嵌入"相关的"数据直接在使用它的文档中。这里的优点是有一个"读"检索"相关"的操作信息,也是单一的"写"更新" parent"的操作和孩子"条目,虽然经常无法写入"许多"孩子一次没有处理"列表"在客户端或以其他方式接受多个"写操作,最好是"批处理"处理

然后数据看起来像这样(与上面的例子相比):

{ 
    "_id": ObjectId("5392fea00ff066b7d533a765"),
    "customerName": "Bill",
    "items": [
        { "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 },
        { "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }
    ]
}

因此,实际获取数据只是一个问题:

db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });

两者的优缺点总是在很大程度上取决于应用程序的使用模式。但一目了然:

嵌入

  • 包含嵌入数据的文档总大小通常不超过16MB的存储空间(BSON限制)或其他(作为指南)包含500或更多条目的数组。

  • 嵌入的数据通常不需要频繁更改。所以你可以忍受"复制"来自去标准化不会导致需要更新那些"重复"在许多父文档中使用相同的信息只是为了调用更改。

  • 相关数据经常与父母一起使用。这意味着,如果您的"读/写"案件几乎总是需要"读/写"对于父母和孩子,然后将数据嵌入原子操作是有意义的。

引用

  • 相关数据总是超过16MB BSON限制。您始终可以考虑采用混合方法来解决问题,但不能违反主文档的一般硬限制。常见的情况是" post"和"评论"在哪里"评论"活动预计会非常大。

  • 相关数据需要定期更新。或者基本上是你"规范化"因为这些数据是'#34;共享"在许多父母和"相关的"数据经常变化,以至于更新每个父母中的嵌入项目是不切实际的。那儿"孩子"项目发生。更容易的情况是只引用"孩子"并进行一次更改。

  • 读写分离明显。在这种情况下,你可能不会总是要求"相关"阅读"父母"或者不需要经常改变父母"在写给孩子时,可能有充分的理由将模型分开作为参考。此外,如果一般希望更新许多"子文件"在那里,那些"子文件"实际上是对另一个集合的引用,然后通常在数据位于单独的集合中时,实现更有效。

因此实际上有更广泛的讨论"利弊"对于Data Modelling上的MongoDB文档中的任一位置,其中包含各种用例以及使用填充方法支持的嵌入或引用模型的方法。

希望"点数"是有用的,但通常建议考虑应用程序的数据使用模式,并选择最佳的。拥有"选项"嵌入"应该"是你选择MongoDB的原因,但它实际上就是你的应用程序如何使用数据"这使得决定哪种方法适合您的数据建模的哪一部分(因为它不是"全部或全部")是最好的。

  
      
  1. 请注意,由于这是最初编写的,所以MongoDB引入了$lookup运算符,它确实执行了" join"服务器上的集合之间。出于一般性讨论的目的,惠斯特"更好"在大多数情况下,"多个查询"由populate()和"多个查询引起的开销"一般来说,任何$lookup操作都会产生"重大开销"
  2.         

    核心设计原则是"嵌入"意味着"已经存在"而不是"从其他地方取货"。基本上是你口袋里的差异"并且"在货架上"和I / O术语通常更像是"在图书馆市中心的货架上" ,特别是远离基于网络请求。