本文是我们学院课程中名为MongoDB –可扩展NoSQL DB的一部分 。
在本课程中,您将被介绍到MongoDB。 您将学习如何安装它以及如何通过它的外壳进行操作。 此外,您还将学习如何通过Java以编程方式访问它以及如何将Map Reduce与其一起使用。 最后,将解释更高级的概念,例如分片和复制。 在这里查看 !
目录
1.简介
最初由Google推广的Map / Reduce范式(对于好奇的读者,这是到原始论文的链接 )如今已经受到了广泛的关注,这主要是由于大数据运动。 许多NoSQL解决方案旨在支持与Map / Reduce框架的集成,但是MongoDB远远超出了此范围,并将其自己的Map / Reduce实现集成到MongoDB服务器中,供所有人使用。
2.映射/缩小一览
Map / Reduce是一个框架,它允许跨许多物理或虚拟服务器并行处理大型和超大型数据集。 典型的Map / Reduce程序包括两个阶段:
- 映射阶段:过滤/转换/转换数据
- 减少阶段:对数据执行聚合
在某种程度上, map和reduce阶段的灵感来自于map和reduce ,这是函数编程领域中广泛使用并广为人知的高级函数 。 就像名称Map / Reduce所暗示的那样, 映射作业始终在缩小作业之前执行。 请注意,现代的数据处理方法比前面介绍的方法更为复杂,但是原理保持不变。
从实现的前景看,大多数Map / Reduce框架都在元组上运行。 映射实现接受一组数据并将其转换为另一组数据,通常为元组 (键/值对)。 因此, reduce实现接受map实现的输出作为其输入,并将那些元组合并(减少)为较小的(聚合的) 元组集,最终成为最终结果。
让我们回顾一下在第3部分中看到的书店示例。MongoDB和Java教程,并尝试对其应用Map / Reduce范例,以获取每个作者发表了多少本书的汇总结果。 map函数只是遍历每个文档的authors属性,并且对于每个作者都发出 (很常见的术语,指代新输出的生成)键/值元组(author,1) 。
function map(document):
for each author in document.authors:
emit( author, 1 )
reduce函数接受键和值的集合(取自元组),对其进行汇总并发出 (再次输出新的)新元组,其中每个作者均拥有其已出版的书的总数。
function reduce(author, values):
sum = 0
for each value in values:
sum += value
emit( author, sum )
它看起来很简单,似乎并没有带来很多价值。 但是这里的简单性是一个关键,它可以将大问题分解为较小的部分,并在数百或数千个服务器(节点)之间分布计算:大规模并行数据处理。
3.在MongoDB中映射/减少
MongoDB提供单个命令mapReduce (以及相应的MongoDB Shell包装器db.<collection>.mapReduce()
),以在整个文档集合中运行Map / Reduce聚合。 该命令支持许多不同的参数,在本节中,我们将逐步介绍所有这些参数。
MapReduce的命令在单输入集合(其可被分片 ),并且可以产生单一的输出集合,其也可以被分片操作。 在将数据输入到map函数之前,该命令允许对输入集合进行任意排序和限制。
MongoDB中的map和reduce函数是JavaScript函数,并在MongoDB服务器进程中运行。 map函数将单个集合的文档作为输入,并应用自定义JavaScript函数以发出新的输出。
当MapReduce的命令在一种方式,它是一个使用分片集合(请参阅运行第4部分的MongoDB拆分指南详细介绍)作为输入,mongos进程将自动调度地图/减少命令每个碎片平行和将等待所有分片上的作业完成。 因此,如果MapReduce的命令的方式,它是使用分片集合作为输出,运行MongoDB的使用_id字段作为片键碎片的输出集合。
数据集
我们将改编第4部分中的书店示例。《 MongoDB分片指南》将说明使用books集合的不同地图/缩小场景。
db.books.insert( {
"title" : "MongoDB: The Definitive Guide",
"published" : "2013-05-23",
"authors": [
{ "firstName" : "Kristina", "lastName" : "Chodorow" }
],
"categories" : [ "Databases", "NoSQL", "Programming" ],
"publisher" : { "name" : "O'Reilly" },
"price" : 32.99
} )
db.books.insert( {
"title" : "MongoDB Applied Design Patterns",
"published" : "2013-03-19",
"authors": [
{ "firstName" : "Rick", "lastName" : "Copeland" }
],
"categories" : [ "Databases", "NoSQL", "Patterns", "Programming" ],
"publisher" : { "name" : "O'Reilly" },
"price" : 32.99
} )
db.books.insert( {
"title" : "MongoDB in Action",
"published" : "2011-12-16",
"authors": [
{ "firstName" : "Kyle", "lastName" : "Banker" }
],
"categories" : [ "Databases", "NoSQL", "Programming" ],
"publisher" : { "name" : "Manning" },
"price" : 30.83
} )
db.books.insert( {
"title" : "NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence",
"published" : "2012-08-18",
"authors": [
{ "firstName" : "Pramod J.", "lastName" : "Sadalage" },
{ "firstName" : "Martin", "lastName" : "Fowler" }
],
"categories" : [ "Databases", "NoSQL" ],
"publisher" : { "name" : "Addison Wesley" },
"price" : 26.36
} )
db.books.insert( {
"title" : "Scaling MongoDB",
"published" : "2011-03-07",
"authors": [
{ "firstName" : "Kristina", "lastName" : "Chodorow" }
],
"categories" : [ "Databases", "NoSQL" ],
"publisher" : { "name" : "O'Reilly" },
"price" : 25.30
} )
db.books.insert( {
"title" : "50 Tips and Tricks for MongoDB Developers",
"published" : "2011-05-06",
"authors": [
{ "firstName" : "Kristina", "lastName" : "Chodorow" }
],
"categories" : [ "Databases", "NoSQL", "Programming" ],
"publisher" : { "name" : "O'Reilly" },
"price" : 25.08
} )
db.books.insert( {
"title" : "MongoDB in Action, 2nd Edition",
"published" : "2014-12-01",
"authors": [
{ "firstName" : "Kyle", "lastName" : "Banker" },
{ "firstName" : "Peter", "lastName" : "Bakkum" },
{ "firstName" : "Tim", "lastName" : "Hawkins" }
],
"categories" : [ "Databases", "NoSQL", "Programming" ],
"publisher" : { "name" : "Manning" },
"price" : 26.66
} )
db.books.insert( {
"title" : "Node.js, MongoDB, and AngularJS Web Development",
"published" : "2014-04-04",
"authors": [
{ "firstName" : "Brad", "lastName" : "Dayley" }
],
"categories" : [ "Databases", "NoSQL", "Programming", "Web" ],
"publisher" : { "name" : "Addison Wesley" },
"price" : 34.35
} )
一旦书籍收藏中充满了这些文档(最好的方法是使用MongoDB shell),我们就可以开始通过示例开始使用map / reduce了。
示例:按作者计数书籍
让我们从最简单的场景开始,运行MongoDB map / reduce命令来完成我们在Map / Reduce概览部分中所看过的示例:按作者计数书籍。
db.runCommand( {
mapReduce: "books",
map: function() {
for (var index = 0; index < this.authors.length; ++index) {
var author = this.authors[ index ];
emit( author.firstName + " " + author.lastName, 1 );
}
},
reduce: function(author, counters) {
count = 0;
for (var index = 0; index < counters.length; ++index) {
count += counters[index];
}
return count;
},
out: { inline: 1 }
} )
如果我们在MongoDB Shell中运行此命令,则map / reduce命令的结果将返回以下文档:
{
"results" : [
{
"_id" : "Brad Dayley",
"value" : 1
},
{
"_id" : "Kristina Chodorow",
"value" : 3
},
{
"_id" : "Kyle Banker",
"value" : 2
},
{
"_id" : "Martin Fowler",
"value" : 1
},
{
"_id" : "Peter Bakkum",
"value" : 1
},
{
"_id" : "Pramod J. Sadalage",
"value" : 1
},
{
"_id" : "Rick Copeland",
"value" : 1
},
{
"_id" : "Tim Hawkins",
"value" : 1
}
],
"timeMillis" : 1,
"counts" : {
"input" : 8,
"emit" : 11,
"reduce" : 2,
"output" : 8
},
"ok" : 1
}
相当清晰的输出,并且我们可以看到每个作者都伴随着输入集合中他的书籍总数。
示例:按出版商计算平均图书价格
我们要看的下一个示例稍微复杂一点,并为map / reduce命令引入了三个新元素: finalize , 范围和具有名称result的 输出到集合 。 我们将使用一种特定的货币(美元)计算每个出版商的平均图书价格。
db.runCommand( {
mapReduce: "books",
scope: { currency: "US" },
map: function() {
emit( this.publisher, { count: 1, price: this.price } );
},
reduce: function(publisher, values) {
var value = { count: 0, price: 0 };
for (var index = 0; index < values.length; ++index) {
value.count += values[index].count;
value.price += values[index].price;
}
return value;
},
finalize: function(publisher, value) {
value.average = currency + ( value.price / value.count ).toFixed(2);
return value;
},
out: {
replace: "results"
}
} )
在此示例中, 范围文档将全局可变货币引入到map / reduce操作的上下文( map , reduce和finalize函数)。 您可能已经知道,只有在处理完所有文档后才能计算平均价格,这就是引入finalize函数的原因:一旦所有map和reduce步骤都结束,就调用它。 映射/归约操作的最终输出将存储在结果集中( 书店数据库)。 如果我们在MongoDB Shell中运行此命令,则将返回以下文档作为结果:
{
"result" : "results",
"timeMillis" : 50,
"counts" : {
"input" : 8,
"emit" : 8,
"reduce" : 3,
"output" : 3
},
"ok" : 1
}
结果集合包含以下文档(可以通过在MongoDB Shell中运行命令包装器db.results.find().pretty()
来检索这些文档):
{
"_id" : {
"name" : "Addison Wesley"
},
"value" : {
"count" : 2,
"price" : 60.71,
"average" : "US30.36"
}
}
{
"_id" : {
"name" : "Manning"
},
"value" : {
"count" : 2,
"price" : 57.489999999999995,
"average" : "US28.74"
}
}
{
"_id" : {
"name" : "O'Reilly"
},
"value" : {
"count" : 4,
"price" : 116.36,
"average" : "US29.09"
}
}
示例:按出版商递增计算平均书价
最后一个示例将演示MongoDB中 map / reduce实现的增量性质。 让我们假设,由于我们已经按出版商计算了平均图书价格,因此有两本新书添加到收藏中。
db.books.insert( {
"title" : "MongoDB and Python: Patterns and processes for the popular document-oriented database",
"published" : "2011-09-30",
"authors": [
{ "firstName" : " Niall", "lastName" : "O'Higgins" }
],
"categories" : [ "Databases", "NoSQL", "Programming" ],
"publisher" : { "name" : "O'Reilly" },
"price" : 18.06
} )
db.books.insert( {
"title" : " Node.js in Action",
"published" : "2013-11-28",
"authors": [
{ "firstName" : " Mike", "lastName" : "Cantelon" }
],
"categories" : [ "Databases", "NoSQL", "Programming", "Web" ],
"publisher" : { "name" : "Manning" },
"price" : 26.09
} )
现在,我们在这里有两个选择,以便获得出版商的更新平均书价:我们可以再次对集合中的所有文档重新运行map / reduce命令,或者仅使用新文档对现有结果进行增量更新。 后者将是本示例的目标。 map , reduce和finalize函数与前面的示例相同。 我们将添加的两个新参数是limit和query ,仅用于过滤出这两本新书,另外,将使用reduce操作( out参数)合并输出集合。
db.runCommand( {
mapReduce: "books",
scope: { currency: "US" },
map: function() {
emit( this.publisher, { count: 1, price: this.price } );
},
reduce: function(publisher, values) {
var value = { count: 0, price: 0 };
for (var index = 0; index < values.length; ++index) {
value.count += values[index].count;
value.price += values[index].price;
}
return value;
},
finalize: function(publisher, value) {
value.average = currency + ( value.price / value.count ).toFixed(2);
return value;
},
query: { "authors.lastName": { $in: [ "Cantelon", "O'Higgins" ] } },
limit: 2,
out: {
reduce: "results"
}
} )
查询过滤器仅包括两名新作者出版的书籍。 出于演示的目的,该限制也设置为2,但是它并不是很有用,因为我们知道只能添加两本新书。 如果我们在MongoDB Shell中运行此命令,则将返回以下文档作为结果:
{
"result" : "results",
"timeMillis" : 4,
"counts" : {
"input" : 2,
"emit" : 2,
"reduce" : 0,
"output" : 3
},
"ok" : 1
}
请注意,根本没有调用reduce函数,因为每个键(发布者)仅包含一个值(有关这些微妙的细节,请参阅MongoDB中的Map / Reduce部分)。
以下是结果集合中的文档(请将它们与“ 示例:按出版商计算平均图书价格”部分中的文档进行比较):
{
"_id" : {
"name" : "Addison Wesley"
},
"value" : {
"count" : 2,
"price" : 60.71,
"average" : "US30.36"
}
}
{
"_id" : {
"name" : "Manning"
},
"value" : {
"count" : 3,
"price" : 83.58,
"average" : "US27.86"
}
}
{
"_id" : {
"name" : "O'Reilly"
},
"value" : {
"count" : 5,
"price" : 134.42,
"average" : "US26.88"
}
}
如我们所见,现有结果集已使用适当的合并算法进行了增量更新。
4.接下来
本教程的这一部分总结了MongoDB文档数据库功能的基本但仍然足够详细的概述。 在本课程的最后部分,我们将深入探讨本章,并希望揭示有关MongoDB内部和高级概念的一些有趣细节。
翻译自: https://www.javacodegeeks.com/2015/09/mongodb-mapreduce-tutorial.html