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
的内容远远不止本篇文章写的这么一点点范围,有兴趣能够查阅官方文档。