什么是按需的物化视图
这个功能从MongoDB 4.2开始伴随着聚合管道的新stage特性$merge
引入的.$merge
stage能将聚合管道的结果合并到一个现有的collection中,借助$merge
这个特性用户能创建按需物化视图(on-demand materialized view)
按需物化视图的原理: 每次运行聚合管道,就会将结果输出到collection中
Starting in version 4.2, MongoDB adds the $merge stage for the aggregation pipeline. This stage can merge the pipeline results to an existing collection instead of completely replacing the collection. This functionality allows users to create on-demand materialized views, where the content of the output collection can be updated each time the pipeline is run.
用途
- 数据统计操作: 对某个维度的信息做处理,比如每天对销售清单表里面的数据做一次汇总,算出每天的销售额
样例
我们按照官网上的样例做解读: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,它可以统计从startDate开始的月度销售情况,比如销售的数量和金额;将结果保存到monthlybakesales,当monthlybakesales中已经有了旧的统计信息,那么就用最新的信息更新旧的
think_db> 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" } }
] );
};
[Function: updateMonthlySales]
复制代码
执行第一次结算,从1970-01-01
开始
# 执行
think_db> updateMonthlySales(new ISODate("1970-01-01"));
# 查询
think_db> db.monthlybakesales.find()
[
{
_id: '2018-12',
sales_quantity: 41,
sales_amount: Decimal128("506")
},
{
_id: '2019-01',
sales_quantity: 86,
sales_amount: Decimal128("896")
}
]
think_db>
复制代码
再次插入销售数据,模拟新的交易产生了
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") }
] )
复制代码
重新执行物化视图,我们可以看到已经进行了结果的更新
# 执行
think_db> updateMonthlySales(new ISODate("1970-01-01"));
# 查询
think_db> db.monthlybakesales.find()
[
{
_id: '2018-12',
sales_quantity: 41,
sales_amount: Decimal128("506")
},
{
_id: '2019-01',
sales_quantity: 102,
sales_amount: Decimal128("1142")
},
{
_id: '2019-02',
sales_quantity: 15,
sales_amount: Decimal128("284")
}
]
复制代码
总结:
- 按需物化视图首先是按需计算的,每次需要最新的数据都需要执行一次计算,这样结果才能更新,数据是由计算产生了,按需物化视图并不拥有实际的数据
- 定义按需物化视图本质上是定义一个函数,函数逻辑由聚合管道组成