MongoDB中的物化视图

视图,为查询提供了便利。定义视图时, 可以包含复杂的集合查询逻辑或隐藏敏感信息。构建查询语句时,无需重复构建和维护聚合管道查询中的多个过程。Mongodb查询优化器也可以调整查询管道中的顺序,与视图查询中定义的查询条件一起进行优化。

视图实时返回聚合查询结果,能够获取到最新的数据。但用户或前端应用需要等待数据库实时计算结果。视图返回结果中的部分定义可以预先计算完成,不需要每次进行计算。物化视图可以满足这样的需求。

定义

物化视图,预先将聚合管道查询结果保存到硬盘中,并从硬盘直接读取结果。物化视图是$merge和$out过程的结果典型应用。

两种视图对比

物化视图与普通视图对比,有下面的差异和相同点。

  • 物化视图与普通视图,都返回聚合查询的结果。
  • 普通视图,实时查询数据库并返回结果,不会将数据保存到硬盘
  • 物化视图从硬盘直接读取数据。在聚合查询中,使用$merge和$out命令,将查询结果写入视图。物化视图可能不会实时获得数据,需要考虑定期更新或手动更新。
  • 标准视图不可添加删除索引。查询时查询优化器依赖于视图构建时的集合。物化视图可以添加索引,索引数据保存在硬盘上。
  • 性能上,物化视图直接从硬盘读取数据,效率高于普通视图。随着聚合管道查询复杂性的增加,这种优势会更加明显。

应用

某烘焙店在2019年一月底,产生了下面的数据

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, 要求产生将每个月的销售统计信息放入视图。接受参数开始时间,用户指定开始时间,统计用户指定开始时间前的售卖信息。

var 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: { coll: "monthlybakesales" }, on: "_id",  whenMatched: "replace", whenNotMatched: "insert" }}
        ])
}

$match方法查询出指定时间后的销售数据

$group方法,按照自然月统计销售数量和销售金额信息

$merge方法,将查询结果放到视图monthlybacksales中。当月销售数据存在时,替换掉视图中当月的数据。当月数据不存在时,将统计后当月销售数据插入视图。

运行方法updateMonthlySales, 产生初始统计数据

扫描二维码关注公众号,回复: 17323167 查看本文章

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

查询物化视图

运行updateMonthlySales方法后,数据已经保存到视图monthlybacksales中。

db.monthlybakesales.find().sort("_id")
/* 1 */
{
	"_id" : "2018-12",
	"sales_quantity" : 41,
	"sales_amount" : Decimal128("506")
},

/* 2 */
{
	"_id" : "2019-01",
	"sales_quantity" : 102,
	"sales_amount" : Decimal128("1142")
},

更新物化视图

假设在2019年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") }
] )

为了更新物化视图中的数据,运行方法updateMonthlySales方法

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

查询更新后的结果

db.monthlybakesales.find().sort("_id")
/* 1 */
{
	"_id" : "2018-12",
	"sales_quantity" : 41,
	"sales_amount" : Decimal128("506")
},

/* 2 */
{
	"_id" : "2019-01",
	"sales_quantity" : 102,
	"sales_amount" : Decimal128("1142")
},

/* 3 */
{
	"_id" : "2019-02",
	"sales_quantity" : 15,
	"sales_amount" : Decimal128("284")
}

猜你喜欢

转载自blog.csdn.net/wilsonzane/article/details/135226205