MongoDB 实例化视图


什么是实例化视图

  • 视图是本质上是一张虚拟表,不包含任何数据。查看查询的结果不任何地方都存储在磁盘上,每次执行查询时,将重新创建该视图。
  • 实例化视图是实际结构存储在数据库中,并写入到磁盘。他们将基于参数定义它们在创建时进行更新。

从4.2版本开始,MongoDB为聚合管道添加了$merge阶段。此阶段可以将管道结果合并到现有集合,而不是完全替换集合。此功能允许用户创建按需实例化视图,其中每次运行管道时都可以更新输出集合的内容。

示例

假设在 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") },
] );

1 定义按需实例化视图

以下函数定义包含累积每月销售额信息的具体化视图。在此示例中,该函数采用 date 参数来仅更新从特定日期开始的每月销售信息。updateMonthlySalesmonthlybakesales

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

  • 当存在匹配项(即集合中已存在具有相同年月的文档)时,阶段将使用聚合结果中的文档替换现有文档。
  • 当不匹配时,舞台会将文档从聚合结果插入到集合中(不匹配时的默认行为)。

2 执行初始运行

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

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

初始运行后,包含以下文档;即 返回以下内容:monthlybakesalesdb.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") }

3 刷新实例化视图

假设到 2019 年 2 月的第一周,产品系列将使用较新的销售信息进行更新;具体来说,1月和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 月的数据,请再次运行该函数以重新运行聚合管道(从2019-01-01开 始)。monthlybakesalesnew ISODate("2019-01-01")

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

内容已更新,输入最新数据:使用下列语句返回内容:monthlybakesalesbakesalesdb.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阶段:

  • 可以输出到相同或不同数据库中的集合。
  • 如果输出集合尚不存在,则创建新集合。
  • 可以将结果(插入新文档、合并文档、替换文档、保留现有文档、操作失败、使用自定义更新管道处理文档)合并到现有集合中。
  • 可以输出到分片集合。输入集合也可以分片。