视图,为查询提供了便利。定义视图时, 可以包含复杂的集合查询逻辑或隐藏敏感信息。构建查询语句时,无需重复构建和维护聚合管道查询中的多个过程。Mongodb查询优化器也可以调整查询管道中的顺序,与视图查询中定义的查询条件一起进行优化。
视图实时返回聚合查询结果,能够获取到最新的数据。但用户或前端应用需要等待数据库实时计算结果。视图返回结果中的部分定义可以预先计算完成,不需要每次进行计算。物化视图可以满足这样的需求。
物化视图,预先将聚合管道查询结果保存到硬盘中,并从硬盘直接读取结果。物化视图是$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, 产生初始统计数据
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")
}