MongoDB
是一個基於分佈式文件存儲的數據庫。由C++
語言編寫。旨在爲WEB
應用提供可擴展的高性能數據存儲解決方案。javascript
自從先後端分離後就一直有爭辯,先後端分離的界限在哪裏,前端僅僅是寫頁面嗎?須要學習服務器數據庫等後端的知識嗎?前端
我認爲是很是有必要的,假如就籠統以瀏覽器和服務器區分先後端,如何解釋瀏覽器的 indexedDB
的存在。存在就是合理的,瞭解越多能夠走得越遠。java
公司最近有 mongo
聚合的需求,我瞬間有了極大的興趣,這篇文章是 mongo
的學習和解決問題筆記。jquery
環境安裝與本篇文章的關係不大,但提供幾個渠道:mongodb
在城市中有幾所學校(db
),咱們來查看下城市中的學校都有哪些數據庫
而後須要指定學校,做用是能夠操做該學校(mongo
默認指向test
)後端
學校(test
)到了期末考試,成績以下:數組
姓名 | 性別 | 語文 | 數學 | 英語 | 參加考試時間 |
---|---|---|---|---|---|
小一 | 男生 | 90 | 90 | 90 | 2019年6月10號 |
小二 | 女生 | 80 | 80 | 80 | 2019年6月10號 |
小三 | 女生 | 70 | 70 | 70 | 2019年6月10號 |
小四 | 女生 | 60 | 60 | 60 | 2019年6月12號 |
成績出來後老師把分數錄入成績系統。瀏覽器
// 插入的方法:insertOne插入一條數據
db.student.insertOne({
name: "小一",
sex: '男生',
chinese: '90',
math: '90',
english: '90',
createdAt: new Date('2016-06-09T16:00:00Z')
})
複製代碼
注意:
new Date('2016-06-09T16:00:00Z')
是世界標準時間,mongo
記錄的也是世界標準時間。中國在東八區,時間加8小時,換算過來恰好是2019年6月10號。服務器
老師以爲一個個添加太煩了,因此一次性添加:
// // 插入的方法:insertMany 插入一條數據
db.student.insertMany([
{ name: "小二", sex: '女生', chinese: '80', math: '80', english: '80', createdAt: new Date('2016-06-09T16:00:00Z')},
{ name: "小三", sex: '女生', chinese: '70', math: '70', english: '70', createdAt: new Date('2016-06-09T16:00:00Z')},
{ name: "小四", sex: '女生', chinese: '60', math: '60', english: '60', createdAt: new Date('2016-06-11T16:00:00Z')}
])
複製代碼
- 學校(
db
):數據庫,mongo
能夠容納多個數據庫,默認使用test
;- 學生集合(
student
):集合,能夠理解爲集合全部學生的班級;- 學生信息(
data
):文檔,能夠理解爲單個學生信息。
老師忽然發現,小三實際上是女裝大佬,老師被騙惱羞成怒,因而修改其性別。
// 更新方法:updateOne
db.student.updateOne(
{ name: '小三' },
{ $set: { sex: '女裝大佬' }}
)
複製代碼
其中
$set
是更新操做符,告訴mongo
須要對符合的數據進行什麼操做。用法差很少,列舉部分以下(更多更新操做符):
- $currentDate:設置時間;
- $inc:按指定的數量增長字段的值;
- $min:僅當指定的值小於現有字段值時才更新字段;
- $max:僅當指定的值大於現有字段值時才更新字段;
- $mul:將字段的值乘以指定的量;
- $rename:重命名字段;
- $set:設置文檔中字段的值;
- $setOnInsert:若是更新致使文檔插入,則設置字段的值。對修改現有文檔的更新操做沒有影響;
- $unset:從文檔中刪除指定的字段。
三個學生都努力學習,通過期末考試完後,小一自信能夠拿第一,因而小一去查本身的成績。
// 查詢方法:find
// pretty方法可讓數據更好看
db.student.find({name: '小一'}).pretty()
複製代碼
而後再查小二和小三的成績。他忽然發現小三是女裝大佬的事實,震驚!
// 查詢方法:find
db.student.find({$or: [{name: '小二'}, {name: '小三'}]}).pretty()
複製代碼
其中
$or
是查詢操做符,告訴mongo
須要對符合的數據進行什麼操做。用法差很少,列舉部分以下(更多查詢操做符):
小一心裏糾結,最後把這事告訴小三。小三以爲無顏面對同窗和老師,決定退學。老師把小三的信息從教務系統刪除。
// 刪除方法:deleteOne
db.student.deleteOne({name: '小三'})
複製代碼
期末老師作工做總結,發現成績錄入成字符串,應該是數字類型纔對,因此操做循環更改。
// 遍歷方法:forEach
db.student.find().forEach(function(doc) {
doc.chinese = NumberInt(doc.chinese);
doc.math = NumberInt(doc.math);
doc.english = NumberInt(doc.english);
db.student.save(doc);
})
複製代碼
數字類型:
NumberInt
強制轉化爲數字類型。
期末老師須要把班級的語文、數學、英語的平均分計算出來。
// 聚合方法:aggregate
// 而$chinese、$math和$english只是表明文檔字段。
db.student.aggregate([
{
$group: {
_id: null,
sumChinese: { $sum: '$chinese' },
avgChinese: { $avg: '$chinese' },
sumMath: { $sum: '$math' },
avgMath: { $avg: '$math' },
sumEnglish: { $sum: '$english' },
avgEnglish: { $avg: '$english' },
}
}
]).pretty()
複製代碼
$group
是分組操做符,根據_id
來進行分組,若是傳null
就是不分組,選擇全部的數據。 其中$sum
和$avg
是聚合操做符,告訴mongo
須要對符合的數據進行什麼操做。用法差很少,列舉部分以下(更多聚合操做符):
老師發現上面的計算方式是計算全部的學生的成績,而此次只須要計算2019年6月10號考試成績,修改以下:
db.student.aggregate([
{
$match: {
createdAt: {
$gte: new Date('2016-06-09T16:00:00Z'),
$lte: new Date('2016-06-10T16:00:00Z')
}
},
},
{
$project: {
name: 1,
sex: 1,
chinese: 1,
math: 1,
english: 1,
time: {$add: ['$createdAt', 8 * 60 * 60 * 1000]}
}
},
{
$group: {
_id: {
year: {'$year': '$time'},
month: {'$month': '$time'},
day: {'$dayOfMonth': '$time'},
},
sumChinese: { $sum: '$chinese' },
avgChinese: { $avg: '$chinese' },
sumMath: { $sum: '$math' },
avgMath: { $avg: '$math' },
sumEnglish: { $sum: '$english' },
avgEnglish: { $avg: '$english' },
}
}
]).pretty()
複製代碼
time
字段用來顯示中國真實時間,它的值是 createedAt
的值再加8個小時。由於 mongo
默認存儲世界標準時間,中國在東八區,須要加8個小時才能正確顯示中國時間。
$match
:篩選操做符,顧名思義只有知足條件的數據才繼續傳遞下去;$project
:修改返回數據結構的操做符,1表示顯示,不寫或者0則不顯示。(aggregate
聚合方法接受數組參數,由於有管道概念,簡單說前面命令執行完的結果做爲後面命令的參數,像jquery
的點鏈接符。)
除了使用聚合方法,還可使用 MapReduce
方法來計算。
Map-Reduce
是一種計算模型,簡單的說就是將大批量的工做(數據)分解(MAP)執行,而後再將結果合併成最終結果(REDUCE)。MongoDB提供的Map-Reduce很是靈活,對於大規模數據分析也至關實用。
db.student.mapReduce(
function(){
emit(null, this);
},
function(key, values){
var reducedVal = {
sumChinese: 0,
avgChinese: 0,
sumMath: 0,
avgMath: 0,
sumEnglish: 0,
avgEnglish: 0
};
for (var i = 0; i < values.length; i++) {
reducedVal.sumChinese += values[i].chinese;
reducedVal.sumMath += values[i].math;
reducedVal.sumEnglish += values[i].english;
}
reducedVal.avgChinese = reducedVal.sumChinese / values.length;
reducedVal.avgMath = reducedVal.sumMath / values.length;
reducedVal.avgEnglish = reducedVal.sumEnglish / values.length;
return reducedVal;
},
{
query: {createdAt: {$gte: new Date('2016-06-09T16:00:00Z'), $lte: new Date('2016-06-10T16:00:00Z')}},
out: 'sum'
}
).find().pretty()
複製代碼
上面
mongo
語句的意思是:
query
篩選出合適的數據(至關於聚合 aggregate
的 $match
),交給 map
函數(mapReduce
接受的第一個參數);map
函數調用 emit
函數遍歷全部篩選數據,傳遞的第一個參數表示分組,咱們不須要分組就傳遞 null
,第二個參數是對應的數據,咱們把完整數據傳遞下去給給 reduce
函數(mapReduce
接受的第二個參數);reduce
函數接收 map
傳遞的 key
和 values
值(就是對應上面的 emit(null, this)
),咱們只須要按本身的需求遍歷組裝,返回組裝好的數據就能夠了。mongo
很好玩,本篇文章只是記錄功能開發中運用 mongo
的一些場景,並且 mongo
的內容遠遠不止本篇文章寫的這麼一點點範圍,有興趣能夠查閱官方文檔。