留神
本页的内容探讨了按需物化视图。无关视图的探讨,请参阅视图。
从 4.2 版本开始,MongoDB 为 aggregation pipeline 增加了 $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") },
] );
定义按需物化视图
上面的 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"} }
] );
};
$match 阶段过滤数据以仅解决那些销售额大于或等于 startDate
阶段按年 - 月对销售信息进行分组。此阶段输入的文档具备以下模式:
{“_id” : “<YYYY-mm>”, “sales_quantity” : <num>, “sales_amount” : <NumberDecimal>}
$merge 阶段将输入写入到 monthlybakesales 汇合
基于 on_id 字段(未分片输入汇合的默认值),此阶段会查看聚合后果中的文档是否 匹配 汇合中的现有文档:
当匹配时(即同年月的文档曾经存在于汇合中),此阶段会应用来自聚合后果的文档替换现有文档;
当不匹配时,此阶段将聚合后果中的文档插入到汇合中(不匹配时的默认行为)。
执行初始运行
对于初始运行,你能够传入一个日期 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") }
刷新物化视图
假如到了 2019 年 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 月的 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 和可用选项的更多信息
示例:按需物化视图:初始创立
示例:按需物化视图:更新 / 替换数据
示例:仅插入新数据
原文链接:https://docs.mongodb.com/manu…
译者:李正洋
DBA 一枚,善于 oracle/mongodb//tidb 等多种数据库。
现阶段对开源分布式数据库、云计算等畛域有很大趣味;平时喜爱打羽毛球、看电影等。