按需物化视图
按需物化视图是预先计算的聚合管道结果,存储在磁盘上并从磁盘读取。按需物化视图通常是 $merge
或 $out
阶段的结果。
与标准视图的比较
MongoDB提供两种不同的视图类型:标准视图 和 按需物化视图。这两种视图类型都返回聚合管道的结果。
索引
标准视图使用底层集合的索引。因此,您无法直接在标准视图中创建、删除或重建索引,也无法获取视图上的索引列表。
由于它们存储在磁盘上,因此可以直接在按需物化视图中创建索引。
性能
按需物化视图比标准视图提供更好的读取性能,因为它们是从磁盘读取的,而不是作为查询的一部分计算。这种性能优势随着管道的复杂性和聚合数据的量级而增加。
在MongoDB Atlas UI中创建物化视图
本节中的示例使用的是示例电影数据集。要了解如何将示例数据集加载到您的MongoDB Atlas部署中,请参阅加载示例数据。
要在MongoDB Atlas UI中创建物化视图,请按照以下步骤操作
页面显示集群。
对于包含示例数据的集群,点击浏览集合。
在左侧导航窗格中,选择sample_training数据库。
选择grades集合。
添加 $out
阶段
从 选择 下拉菜单中选择
$out
阶段。将以下语法添加到聚合管道中,以将管道的结果写入
sample_training
数据库中的myView
集合。'myView' 点击 保存文档。
$out
阶段将聚合管道的结果写入指定的集合,从而创建视图。有关更多信息,请参阅 $out
。
刷新集合列表以查看 myView
集合。
有关如何在 MongoDB Atlas UI 中查询 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") }, ] );
1. 定义按需物化视图
以下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" } } ] ); };
2. 执行初始运行
对于初始运行,您可以传递一个日期为 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") }
3. 刷新物化视图
假设到2019年2月的第一周,bakesales
集合已更新为包含最新的销售信息;具体来说,增加了1月和2月的销售信息。
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
有关
$merge
及其可用选项的更多信息示例: 按需物化视图:初始创建
示例: 按需物化视图:更新/替换数据
示例: 仅插入新数据