node.js - Mongoose 填充与对象嵌套

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

Mongoose 种群示例:

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,
});

最佳答案

首先要了解 Mongoose 种群,它并不神奇,而只是一种方便的方法,可以让您检索相关信息,而无需您自己动手。

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

“非魔法”部分是,本质上发生在幕后的事情是,当您“引用”另一个来源时,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 }

可以通过“引用”模型或使用 populate(在幕后)完成的“最佳”是这样的:
var order = db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
order.items = db.items.find({ "_id": { "$in": order.items } ).toArray();

因此,为了“加入”该数据,显然“至少”有两个查询和操作。

嵌入概念本质上是 MongoDB 对如何处理不支持“连接”1 的回答。因此,与其将数据拆分为规范化的集合,不如尝试将“相关”数据直接嵌入到使用它的文档中。这里的优点是有一个单一的“读取”操作来检索“相关”信息,还有一个单一的“写入”操作点来更新“父”和“子”条目,尽管通常不可能写入一次“许多”子进程而不在客户端上处理“列表”或以其他方式接受“多个”写入操作,并且最好在“批处理”处理中。

数据则看起来像这样(与上面的示例相比):
{ 
    "_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 限制。您总是可以考虑“bucketing”的混合方法,但不能违反主文档的一般硬限制。常见的情况是“发布”和“评论”,其中“评论”事件预计非常大。
  • 相关数据需要定期更新。或者本质上是您“标准化”的情况,因为该数据在许多父项之间“共享”,并且“相关”数据更改得足够频繁,以至于在出现“子”项的每个“父项”中更新嵌入项是不切实际的.更简单的情况是只引用“ child ”并进行一次更改。
  • 读和写有明确的分离。如果您在阅读“ parent ”时可能不会总是需要“相关”信息,或者在写信给 child 时不需要总是改变“ parent ”,则可能有充分的理由将模型分开如引用。此外,如果普遍希望一次更新许多“子文档”,其中这些“子文档”实际上是对另一个集合的引用,那么当数据位于单独的集合中时,实现通常会更有效收藏。

  • 因此,对于 Data Modelling 上的 MongoDB 文档中任一位置的“优点/缺点”实际上有更广泛的讨论。 ,其中涵盖了各种用例以及使用 populate 方法支持的嵌入或引用模型的方法。

    希望“点点”有用,但一般建议是考虑应用程序的数据使用模式并选择最好的。拥有嵌入“应该”的“选项”是您选择 MongoDB 的原因,但实际上您的应用程序“使用数据”的方式将决定哪种方法适合您的数据建模的哪一部分(因为它不是“全有或全无”)最好的。

    1. Note that since this was originally written MongoDB introduced the $lookup operator which does indeed perform "joins" between collections on the server. For the purposes of the general discussion here, whist "better" in most circumstances that the "multiple query" overhead incurred by populate() and "multiple queries" in general, there still is a "significant overhead" incurred with any $lookup operation.

    The core design principle is "embedded" means "already there" as opposed to "fetching from somewhere else". Essentially the difference between "in your pocket" and "on the shelf", and in I/O terms usually more like "on the shelf in the library downtown", and notably further away for network based requests.

    关于node.js - Mongoose 填充与对象嵌套,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24096546/

    相关文章:

    node.js - 判断预保存回调中是否有记录 "is new"

    mongodb - mongodb insert 命令中是否有 "upsert"选项?

    c# - 支持和反对从 SQL Server 迁移到 MongoDB 的原因

    mongodb - 所以......这个NoSQL的东西

    mongodb - 如何发现 mongodb 数据库结构

    mongodb - 修改和重放 MongoDB oplog

    python - 哪个 Python API 应该与 Mongodb 和 Django 一起使用

    mongodb - 对 MongoDB $in 查询的响应顺序?

    java - MongoDB中不区分大小写的排序

    node.js - MongoDB Node 检查objectid是否有效