Docs 菜单
Docs 主页
/
MongoDB Manual
/ /

按需物化视图

在此页面上

  • 与标准视图的对比
  • 在 MongoDB Atlas 用户界面中创建物化视图
  • 例子
  • 更多信息

注意

消歧

本页讨论按需物化视图。有关标准视图的讨论,请参阅视图

要了解视图类型之间的差异,请参阅与标准视图的对比

按需物化视图是预先计算的聚合管道结果,它存储在磁盘上并从磁盘读取。按需物化视图通常是 $out 阶段的结果。

MongoDB 提供两种不同的视图类型:标准视图按需物化视图。这两种视图类型都可以返回聚合管道的结果。

  • 标准视图在您读取视图时计算得出,并且不会在磁盘中存储。

  • 按需物化视图存储在磁盘上并从磁盘读取。它们使用$merge$out阶段更新保存的数据。

    注意

    使用$merge时,可以使用变更流来监视物化视图的更改。 使用$out时,您无法监视物化视图上的更改。

标准视图使用底层集合的索引。因此,您无法直接在标准视图上创建、删除或重新构建索引,也无法获取视图上的索引列表。

您可以直接在按需物化视图上创建索引,因为它们存储在磁盘中。

按需物化视图的读取性能要优于标准视图,因为前者是从磁盘中读取,而不是作为查询的一部分通过计算得出。聚合管道越复杂,要聚合的数据量越大,这种性能优势就越明显。

本节中的示例使用示例训练数据集。要了解如何将示例数据集加载到您的 MongoDB Atlas 部署中,请参阅加载示例数据。

要在 MongoDB Atlas 用户界面中创建物化视图,请执行以下步骤:

1
  1. 如果尚未显示,请从导航栏上的 Organizations 菜单中选择包含所需项目的组织。

  2. 如果尚未显示,请从导航栏的 Projects 菜单中选择您的项目。

  3. 如果尚未出现,请单击侧边栏中的 Clusters(集群)。

    会显示集群页面。

2
  1. 对于包含样本数据的集群,单击Browse Collections

  2. 在左侧导航窗格中,选择 sample_training 数据库。

  3. 选择 grades 集合。

3
4
5

聚合阶段转换要另存为视图的数据。要了解有关可用聚合阶段的更多信息,请参阅聚合阶段

对于此示例,使用 $set 阶段添加一个新字段:

  1. Select 下拉菜单中选择 $set

  2. 将以下语法添加到聚合管道编辑器,以针对 grades 集合内 scores 数组中的所有 score 值创建平均分数:

    {
    averageScore: { $avg: "$scores.score" }
    }

    MongoDB Atlas 将 averageScore 值添加到每个文档。

6
7
  1. Select 下拉菜单中选择 $out 阶段。

  2. 在聚合管道中添加以下事务语法,以将管道结果写入 sample_training 数据库中的 myView 集合:

    'myView'
  3. 单击 Save Documents(连接)。

$out 阶段将 aggregation pipeline 的结果写入指定集合,由该集合创建视图。要了解更多信息,请参阅 $out

刷新集合列表以查看 myView 集合。

要了解如何在 MongoDB Atlas 用户界面中查询 myView 集合,请参阅 MongoDB Atlas 文档中的查看、过滤和排序文档

假定临近 2019 年 1 月底,集合 bakesales 包含按商品分类的以下销售信息:

db.bakesales.insertMany( [
{ date: new ISODate("2018-12-01"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
{ date: new ISODate("2018-12-02"), item: "Cake - Peanut Butter", quantity: 5, amount: new NumberDecimal("90") },
{ date: new ISODate("2018-12-02"), item: "Cake - Red Velvet", quantity: 10, amount: new NumberDecimal("200") },
{ date: new ISODate("2018-12-04"), item: "Cookies - Chocolate Chip", quantity: 20, amount: new NumberDecimal("80") },
{ date: new ISODate("2018-12-04"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
{ date: new ISODate("2018-12-05"), item: "Pie - Key Lime", quantity: 3, amount: new NumberDecimal("60") },
{ date: new ISODate("2019-01-25"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
{ date: new ISODate("2019-01-25"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
{ date: new ISODate("2019-01-26"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
{ date: new ISODate("2019-01-26"), item: "Cookies - Chocolate Chip", quantity: 12, amount: new NumberDecimal("48") },
{ date: new ISODate("2019-01-26"), item: "Cake - Carrot", quantity: 2, amount: new NumberDecimal("36") },
{ date: new ISODate("2019-01-26"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
{ date: new ISODate("2019-01-27"), item: "Pie - Chocolate Cream", quantity: 1, amount: new NumberDecimal("20") },
{ date: new ISODate("2019-01-27"), item: "Cake - Peanut Butter", quantity: 5, amount: new NumberDecimal("80") },
{ date: new ISODate("2019-01-27"), item: "Tarts - Apple", quantity: 3, amount: new NumberDecimal("12") },
{ date: new ISODate("2019-01-27"), item: "Cookies - Chocolate Chip", quantity: 12, amount: new NumberDecimal("48") },
{ date: new ISODate("2019-01-27"), item: "Cake - Carrot", quantity: 5, amount: new NumberDecimal("36") },
{ date: new ISODate("2019-01-27"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
{ date: new ISODate("2019-01-28"), item: "Cookies - Chocolate Chip", quantity: 20, amount: new NumberDecimal("80") },
{ date: new ISODate("2019-01-28"), item: "Pie - Key Lime", quantity: 3, amount: new NumberDecimal("60") },
{ date: new ISODate("2019-01-28"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
] );

以下 updateMonthlySales 函数定义一个 monthlybakesales 物化视图,其中包含累计每月销售信息。在该示例中,该函数采用日期参数来仅更新从特定日期开始的每月销售信息。

updateMonthlySales = function(startDate) {
db.bakesales.aggregate( [
{ $match: { date: { $gte: startDate } } },
{ $group: { _id: { $dateToString: { format: "%Y-%m", date: "$date" } }, sales_quantity: { $sum: "$quantity"}, sales_amount: { $sum: "$amount" } } },
{ $merge: { into: "monthlybakesales", whenMatched: "replace" } }
] );
};
  • $match 阶段对数据进行筛选,以便仅处理日期晚于或等于 startDate 的销售额。

  • $group 阶段按年月对销售信息进行分组。此阶段输出的文档具有以下形式:

    { "_id" : "<YYYY-mm>", "sales_quantity" : <num>, "sales_amount" : <NumberDecimal> }
  • $merge 阶段会将输出写入 monthlybakesales 集合。

    _id 字段(未分片输出集合的默认值),该阶段检查聚合结果中的文档是否与集合中的现有文档匹配

对于初始运行,您可以传入值为 new ISODate("1970-01-01") 的日期:

updateMonthlySales(new ISODate("1970-01-01"));

初次运行后,monthlybakesales 包含以下文档;即 db.monthlybakesales.find().sort( { _id: 1 } ) 返回以下文档:

{ "_id" : "2018-12", "sales_quantity" : 41, "sales_amount" : NumberDecimal("506") }
{ "_id" : "2019-01", "sales_quantity" : 86, "sales_amount" : NumberDecimal("896") }

假设到 2019 年 2 月的第一周,bakesales 集合用更新的销售信息进行更新,具体来说就是增加一月和二月的销售额。

db.bakesales.insertMany( [
{ date: new ISODate("2019-01-28"), item: "Cake - Chocolate", quantity: 3, amount: new NumberDecimal("90") },
{ date: new ISODate("2019-01-28"), item: "Cake - Peanut Butter", quantity: 2, amount: new NumberDecimal("32") },
{ date: new ISODate("2019-01-30"), item: "Cake - Red Velvet", quantity: 1, amount: new NumberDecimal("20") },
{ date: new ISODate("2019-01-30"), item: "Cookies - Chocolate Chip", quantity: 6, amount: new NumberDecimal("24") },
{ date: new ISODate("2019-01-31"), item: "Pie - Key Lime", quantity: 2, amount: new NumberDecimal("40") },
{ date: new ISODate("2019-01-31"), item: "Pie - Banana Cream", quantity: 2, amount: new NumberDecimal("40") },
{ date: new ISODate("2019-02-01"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") },
{ date: new ISODate("2019-02-01"), item: "Tarts - Apple", quantity: 2, amount: new NumberDecimal("8") },
{ date: new ISODate("2019-02-02"), item: "Cake - Chocolate", quantity: 2, amount: new NumberDecimal("60") },
{ date: new ISODate("2019-02-02"), item: "Cake - Peanut Butter", quantity: 1, amount: new NumberDecimal("16") },
{ date: new ISODate("2019-02-03"), item: "Cake - Red Velvet", quantity: 5, amount: new NumberDecimal("100") }
] )

要刷新 1 月和 2 月的 monthlybakesales 数据,请再次运行该函数以重新运行聚合管道,并从 new ISODate("2019-01-01") 开始。

updateMonthlySales(new ISODate("2019-01-01"));

monthlybakesales 的内容已更新,以反映 bakesales 集合中的最新数据,即 db.monthlybakesales.find().sort( { _id: 1 } ) 返回以下内容:

{ "_id" : "2018-12", "sales_quantity" : 41, "sales_amount" : NumberDecimal("506") }
{ "_id" : "2019-01", "sales_quantity" : 102, "sales_amount" : NumberDecimal("1142") }
{ "_id" : "2019-02", "sales_quantity" : 15, "sales_amount" : NumberDecimal("284") }

$merge 阶段:

  • 可以输出到相同或不同数据库中的集合。

  • 如果输出集合不存在,则创建一个新集合。

  • 可将结果(插入新文档、合并文档、替换文档、保留现有文档、操作失败、使用自定义更新管道处理文档)并入现有集合。

  • 可输出到分片集合。输入集合也可以是分片的。

请参阅 $merge 以了解:

后退

支持的操作