摘要:在第行中,我們會從集合取得結果并顯示它。的邏輯在中,我們要以性別作為,然后以作為。年齡是用來做計算用的,而名字只是用來顯示給人看的。我們要檢查所有和性別相關的年齡,找到年齡最大和最小的用戶。
在這篇文章里面,我們會演示如何在 MongoDB 中使用 MapReduce 操作。
我們會用 dummy-json 這個包來生成一些虛假的數據,然后用 Mongojs
如果想要快速看到結果,可以到 這里 里看看。
什么是 MongoDB ?MongoDB 是一個 NoSQL 數據庫,不像 MySQL 、MSSQL 和 Oracle DB 那樣,MongoDB 使用集合(collections) 來代替表(tables)。同時,它用集合中的文檔(documents)來代替表中的行(rows)。還有最好的一點是,所有文檔都保存成 JSON 格式!你可以到這里學更多關于 MongoDB 的知識。
你可以從 這里 下載安裝 MongoDB。
如果以前沒用過 MongoDB,那么你可以記住下面這些命令:
Command | Result |
---|---|
mongod | 啟動 MongoDB 服務 |
mongo | 進入 MongoDB Shell |
show dbs | 顯示所有數據庫列表 |
use |
進入指定的數據庫 |
show collections | 進入數據庫之后,顯示該數據庫中所有的集合 |
db.collectionName.find() | 顯示該集合中所有文檔 |
db.collectionName.findOne() | 顯示該集合中第一個文檔 |
db.collectionName.find().pretty() | 顯示漂亮的 JSON 格式 |
db.collectionName.insert({key: value}) | 插入一條新的記錄 |
db.collectionName.update({ condition: value}, {$set: {key: value}}, {upsert: true}) | 會更新指定的文檔,設置指定的值。如果 upsert 為 true,當沒有找到匹配的文檔時,會創建一條新的記錄 |
db.collectionName.remove({}) | 移除集合中的所有文檔 |
db.collectionName.remove({key: value}) | 移除集合中匹配到的文檔 |
弄清楚 MapReduce 是如何運作的是非常重要的,如果對 MapReduce 過程不了解的話,你在運行 MapReduce 時很可能得不到你想要的結果。
從 mongodb.org 上的解析:
Map-reduce 是一種數據處理范例,用于將大量的數據變成有用的聚合結果。 對于 map-reduce 操作,MongoDB 提供了 mapReduce 的數據庫命令。
在這非常簡單的術語里面,mapReduce 命令接受兩個基本的輸入:mapper 函數和 reducer 函數。
Mapper 是一個匹配數據的過程,它會在集合中查詢我們想要處理的字段,然后根據我們指定的 key 去分組,再把這些 key-value 對交給 reducer 函數,由它來處理這些匹配到的數據。
我們來看看下面這些數據:
[ { name: foo, price: 9 }, { name: foo, price: 12 }, { name: bar, price: 8 }, { name: baz, price: 3 }, { name: baz, price: 5 } ]
我們想要計算出相同名字下的所需要的價錢。我們將會用這個數據通過 Mapper 和 Reducer 去獲得結果。
當我們讓 Mapper 去處理上面的數據時,會生成如下的結果:
Key | Value |
---|---|
foo | [9,12] |
bar | [8] |
baz | [3,5] |
看到了嗎?它用相同的 key 去分組數據。在我們的例子中,是用 name 分組。這些結果會發送到 Reducer 中。
現在,在 reducer 中,我們會得到上面表格中的第一行數據,然后迭代這些數據然后把它們加起來,這就是第一行數據的總和。然后 reducer 會對第二行數據做同樣的事情,直到所有行被處理完。
最終的輸出結果如下:
Name | Total |
---|---|
foo | 21 |
bar | 8 |
baz | 8 |
現在你明白為什么 Mapper 會叫 Mapper 了吧 ! (因為它會創建一份數據的映射)
也明白了為什么 Reducer 會叫 Reducer 了吧 ! (因為它會把 Mapper 生成的數據歸納成一個簡單的形式)
如果你運行一些例子,你就會知道它是怎么工作的拉。你也可以從官方文檔 中了解更多細節。
創建一個項目正如上文所說,我們可以在 mongo shell 中直接查詢和看到輸出結果。但是,為了讓教程更加豐富,我們會構建一個 Nodejs 項目,在里面運行我們之前的任務。
Mongojs我們會用 mongojs 去實現我們的 MapReduce。你可以用同樣的代碼跑在 mongo shell 里面,會看到同樣的結果。
Dummy-json我們會用 dummy-json 去創建一些虛假的數據。你可以在 這里 找到更多的信息。然后我們會在這些虛假數據上面運行 MapReduce 命令,生成一些有意義的結果。
我們開始吧!
首先,你要安裝 Nodejs,你可以看看 這里。然后你要創建一個叫 mongoDBMapReduce 的目錄。我們將會創建 package.json 文件來保存項目的詳細信息。
運行 npm init 然后填入你喜歡的東西,創建完 package.json 后,我們要添加項目的依賴。
運行 npm i mongojs dummy-json --save-dev ,然后等幾分鐘之后,我們項目的依賴就安裝好了。
下一步,我們要用 dummy-json 模塊來生成虛假數據。
在項目的根目錄創建一個名叫 dataGen.js 的文件,我們會把數據生成的邏輯保存到一個獨立的文件里面。如果以后需要添加更多的數據,你可以運行這個文件。
把下面的內容復制到 dataGen.js 里面:
var mongojs = require("mongojs"); var db = mongojs("mapReduceDB", ["sourceData"]); var fs = require("fs"); var dummyjson = require("dummy-json"); var helpers = { gender: function() { return ""+ Math.random() > 0.5 ? "male" : "female"; }, dob : function() { var start = new Date(1900, 0, 1), end = new Date(); return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); }, hobbies : function () { var hobbysList = []; hobbysList[0] = []; hobbysList[0][0] = ["Acrobatics", "Meditation", "Music"]; hobbysList[0][1] = ["Acrobatics", "Photography", "Papier-Mache"]; hobbysList[0][2] = [ "Papier-Mache"]; return hobbysList[0][Math.floor(Math.random() * hobbysList[0].length)]; } }; console.log("Begin Parsing >>"); var template = fs.readFileSync("schema.hbs", {encoding: "utf8"}); var result = dummyjson.parse(template, {helpers: helpers}); console.log("Begin Database Insert >>"); db.sourceData.remove(function (argument) { console.log("DB Cleanup Completd"); }); db.sourceData.insert(JSON.parse(result), function (err, docs) { console.log("DB Insert Completed"); });
第1-4行,我們引入了所有依賴。
第2行,我們創建了一個叫 mapReduceDB 的數據庫。在數據庫里面,創建了一個叫 sourceData 的集合。
第6-23行,是 Handlebar 的 helper。你可以到 dummy-json 中了解更多信息。
第27-28行,我們讀取了 schema.hbs 文件 (我們接著會創建這個文件),然后把它解析成 JSON。
第32行,在插入新數據之前,我們要先把舊數據清除掉。如果你想保留舊數據,把這部分注釋掉就好了。
第36行,把生成的數據插入數據庫。
接著,我們要在項目根目錄創建一個叫 schema.hbs 的文件。這里面會包括 JSON 文檔的結構。把下面的內容復制到文件里面:
[ {{#repeat 9999}} { "id": {{index}}, "name": "{{firstName}} {{lastName}}", "email": "{{email}}", "work": "{{company}}", "dob" : "{{dob}}", "age": {{number 1 99}}, "gender" : "{{gender}}", "salary" : {{number 999 99999}}, "hobbies" : "{{hobbies}}" } {{/repeat}} ]
注意 第2行,我們會生成 9999 個文檔。
打開一個新的終端,運行 mongod,啟動 MongoDB 服務。然后回到原來的終端,運行 node dataGen.js。
如果一切正常,會顯示如下結果:
$ node dataGen.js Begin Parsing >> Begin Database Insert >> DB Cleanup Completed DB Insert Completed
然后按 ctrl + c 殺掉 Node 程序。要驗證是否插入成功,我們可以打開一個新的終端,運行 mongo 命令進入 mongo shell。
> use mapReduceDB > db.sourceData.findOne() { "id": 0, "name": "Leanne Flinn", "email": "leanne.flinn@unilogic.com", "work": "Unilogic", "dob": "Sun Mar 14 1909 12:45:53 GTM+0530 (LST)", "age": 27, "gender": "male", "salary": 16660, "hobbies": "Acrobatics,Photography,Papier-Mache", "_id": Object("57579f702fa6c7651e504fe2") } > db.sourceData.count() 9999有意義的數據
現在我們有 9999 個虛假用戶的數據,讓我們試著把數據變得有意義
例子1:計算男女數量首先,在項目根目錄創建一個 example1.js 的文件,我們要進行 MapReduce 操作,去計算男女的數量。
Mapper 的邏輯我們只需要讓 Mapper 以性別作為 key,把值作為 1。因為一個用戶不是男就是女。所以,Mapper 的輸出會是下面這樣:
Key | Value |
---|---|
Male | [1,1,1...] |
Female | [1,1,1,1,1...] |
在 Reducer 中,我們會獲得上面兩行數據,我們要做的是把每一行中的值求和,表示該性別的總數。最終的輸出結果如下:
Key | Value |
---|---|
Male | 5031 |
Female | 4968 |
好了,現在我們可以寫代碼去實現了。在 example1.js 中,我們要先引入所需要的依賴。
var mongojs = require("mongojs"); var db = mongojs("mapReduceDB", ["sourceData", "example1_results"]);
注意 第2行,第一個參數是數據庫的名字,第二個參數表示集合的數組。example1_results 集合用來保存結果。
接下來,我們加上 mapper 和 reducer 函數:
var mapper = function () { emit(this.gender, 1); }; var reducer = function(gender, count){ return Array.sum(count); };
在第2行中, this 表示當前的文檔,因此 this.gender 會作為 mapper 的 key,它的值要么是 male,要么是 female。而 emit() 將會把數據發送到一個臨時保存數據的地方,作為 mapper 的結果。
在第5行中,我們簡單地把每個性別的所有值加起來。
最后,加上執行邏輯:
db.sourceData.mapReduce( mapper, reducer, { out : "example1_results" } ); db.example1_results.find(function (err, docs) { if(err) console.log(err); console.log(docs); });
在第5行中,我們設置了輸出的集合名。
在第9行中,我們會從 example1_results 集合取得結果并顯示它。
我們可以在終端運行試試:
$ node example1.js [ { _id: "female", value: 4968 }, { _id: "male": value: 5031 } ]
我的數量可能和你的不一樣,但男女總數應該是 9999 !
Mongo Shell 代碼如果你想在 mongo shell 中運行上面的例子,你可以粘貼下面這些代碼到終端里面:
mapper = function () { emit(this.gender, 1); }; reducer = function(gender, count){ return Array.sum(count); }; db.sourceData.mapReduce( mapper, reducer, { out : "example1_results" } ); db.example1_results.find()
然后你就會看到一樣的結果,很簡單吧!
例子2:獲取每個性別中最老和最年輕的人在項目根目錄創建一個 example2.js 的文件。在這里,我們要把所有用戶根據性別分組,然后分別找每個性別中最老和最年輕的用戶。這個例子比前面的稍微復雜一點。
Mapper 的邏輯在 mapper 中,我們要以性別作為 key,然后以 object 作為 value。這個 object 要包含用戶的年齡和名字。年齡是用來做計算用的,而名字只是用來顯示給人看的。
Key | Value |
---|---|
Male | [{age: 9, name: "John"}, ...] |
Female | [{age: 19, name: "Rita"}, ...] |
我們的 reducer 會比前一個例子要復雜一點。我們要檢查所有和性別相關的年齡,找到年齡最大和最小的用戶。最終的輸出結果是這樣的:
Key | Value |
---|---|
Male | {min: {name: "harry", age: 1}, max: {name: "Alex", age: 99} } |
Female | {min: {name: "Loli", age: 10}, max: {name: "Mary", age: 98} } |
現在打開 example2.js,粘貼下面的內容進去:
var mongojs = require("mongojs"); var db = mongojs("mapReduceDB", ["sourceData", "example2_results"]); var mapper = function () { var x = {age : this.age, name : this.name}; emit(this.gender, {min : x , max : x}); }; var reducer = function(key, values){ var res = values[0]; for (var i = 1; i < values.length; i++) { if(values[i].min.age < res.min.age) res.min = {name : values[i].min.name, age : values[i].min.age}; if (values[i].max.age > res.max.age) res.max = {name : values[i].max.name, age : values[i].max.age}; }; return res; }; db.sourceData.mapReduce( mapper, reducer, { out : "example2_results" } ); db.example2_results.find(function (err, docs) { if(err) console.log(err); console.log(JSON.stringify(docs)); });
在第6行,我們構建了一個 object,把它作為 value 發送。
在第13-18行,我們迭代了所有 object,檢查當前的 object 的年齡是否大于或小于前一個 object 的年齡,如果是,就會更新 res.max 或者 res.min。
在第第27行,我們把結果輸出到 example2_results 中。
我們可以運行一下這個例子:
$ node example2.js [ { _id: "female", value: { min: [Object], max: [Object] } }, { _id: "male", value: { min: [Object], max: [Object] } } ]例子3:計算每種興趣愛好的人數
在我們最后的例子中,我們會看看有多少用戶有相同的興趣愛好。我們在項目根目錄創建一個叫 example3.js 的文件。用戶數據長這樣子:
{ "id": 0, "name": "Leanne Flinn", "email": "leanne.flinn@unilogic.com", "work": "Unilogic", "dob": "Sun Mar 14 1909 12:45:53 GTM+0530 (LST)", "age": 27, "gender": "male", "salary": 16660, "hobbies": "Acrobatics,Photography,Papier-Mache", "_id": Object("57579f702fa6c7651e504fe2") }
如你所見,每個用戶的興趣愛好列表都用逗號分隔。我們會找出有多少用戶有表演雜技的愛好等等。
Mapper 的邏輯在這個場景下,我們的 mapper 會復雜一點。我們要為每個用戶的興趣愛好發送一個新的 key-value 對。這樣,每個用戶的每個興趣愛好都會觸發一次計算。最終我們會得到如下的結果:
Key | Value |
---|---|
Acrobatics | [1,1,1,1,1,1,….] |
Meditation | [1,1,1,1,1,1,….] |
Music | [1,1,1,1,1,1,….] |
Photography | [1,1,1,1,1,1,….] |
Papier-Mache | [1,1,1,1,1,1,….] |
在這里,我們只要簡單地為每種興趣愛好求和就好了。最終我們會得到下面的結果:
Key | Value |
---|---|
Acrobatics | 6641 |
Meditation | 3338 |
Music | 3338 |
Photography | 3303 |
Papier-Mache | 6661 |
var mongojs = require("mongojs"); var db = mongojs("mapReduceDB", ["sourceData", "example3_results"]); var mapper = function () { var hobbys = this.hobbies.split(","); for (i in hobbys) { emit(hobbys[i], 1); } }; var reducer = function (key, values) { var count = 0; for (index in values) { count += values[index]; } return count; }; db.sourceData.mapReduce( mapper, reducer, { out : "example3_results" } ); db.example3_results.find(function (err, docs) { if(err) console.log(err); console.log(docs); });
注意第7-9行,我們迭代了每個興趣愛好,然后發送了一次記數。
第13-18行可以用 Array.sum(values) 來代替,這樣是另外一種做相同事情的方式。最終我們得到的結果:
$ node example3.js [ { _id: "Acrobatics", value: 6641 }, { _id: "Meditation", value: 3338 }, { _id: "Music", value: 3338 }, { _id: "Photography", value: 6661 }, { _id: "Papier-Mache", value: 3303 } ]
這就是 MongoDB 中運行 MapReduce 的方法了。但要記住,有時候一個簡單的查詢就能完成你想要的事情的。
出處http://scarletsky.github.io/2016/06/12/mapreduce-in-mongodb/
參考資料MapReduce in MongoDB
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/18854.html
摘要:操作花費的時間,單位是毫秒。處理完成后,會自動將臨時集合的名字更改為你指定的集合名,這個重命名的過程是原子性的。作用域在這些函數內部是不變的。上一篇文章指南聚合下一篇文章指南聚合命令 上一篇文章:MongoDB指南---16、聚合下一篇文章:MongoDB指南---18、聚合命令 MapReduce是聚合工具中的明星,它非常強大、非常靈活。有些問題過于復雜,無法使用聚合框架的查詢語言...
摘要:操作花費的時間,單位是毫秒。處理完成后,會自動將臨時集合的名字更改為你指定的集合名,這個重命名的過程是原子性的。作用域在這些函數內部是不變的。上一篇文章指南聚合下一篇文章指南聚合命令 上一篇文章:MongoDB指南---16、聚合下一篇文章:MongoDB指南---18、聚合命令 MapReduce是聚合工具中的明星,它非常強大、非常靈活。有些問題過于復雜,無法使用聚合框架的查詢語言...
摘要:簡述從字面上來理解就是兩個過程映射以及化簡。在映射化簡的過程都是每臺服務器自己的在運算,大量的服務器同時來進行運算工作,這就是大數據基本理念。映射操作輸出了鍵值對結果。在中,所有的映射化簡函數都是使用編寫,并且運行在進程中。 簡述 mapReduce從字面上來理解就是兩個過程:map映射以及reduce化簡。是一種比較先進的大數據處理方法,其難度不高,從性能上來說屬于比較暴力的(通過N...
閱讀 3712·2023-04-25 17:45
閱讀 3426·2021-09-04 16:40
閱讀 999·2019-08-30 13:54
閱讀 2126·2019-08-29 12:59
閱讀 1396·2019-08-26 12:11
閱讀 3273·2019-08-23 15:17
閱讀 1516·2019-08-23 12:07
閱讀 3878·2019-08-22 18:00