## 前景
随着大数据的不断发展,非关系型数据库已经变得越来越重要,相关的产品也都得到了飞速发展。而其中 MongoDB 更是佼佼者,作为高性能开源文档数据库,MongoDB 以敏捷、可扩展和对企业应用友好而著称,因其操作简单、完全免费、源码公开等特点,受到了 IT 从业人员的青睐,并被广泛部署于实际的生产环境中。
使用 MongoDB 的公司包括:BAT、360、Foursquare、Craiglist、迪士尼、SAP、Intuit、EA 等。
## 定义
MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。MongoDB 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
## 优点
MongoDB 的特点是高性能、易部署、易使用,存储数据非常方便。主要功能特性有:
* 面向集合存储,易存储对象类型的数据。
* 模式自由。
* 支持动态查询。
* 支持完全索引,包含内部对象。
* 支持查询。
* 支持复制和故障恢复。
* 使用高效的二进制数据存储,包括大型对象(如视频等)。
* 自动处理碎片,以支持云计算层次的扩展性
* 支持 JavaScript,RUBY,PYTHON,JAVA,C++,PHP 等多种语言。
* 文件存储格式为 BSON(一种 JSON 的扩展)
## BSON
BSON 是一种类似 JSON 的二进制形式的存储格式,简称 Binary JSON,它和 JSON 一样,支持内嵌的文档对象和数组对象,但是 BSON 有 JSON 没有的一些数据类型,如 Date 和 BinData 类型。
后面存在 MongoDB 数据库里面的全部都是 BSON 格式的数据。
# MongoDB 使用
对 MongoDB 数据库操作需要其提供的内置命令来操作的。
## 基本命令
* show dbs :查看有数据的库
* use 库名 :若没有则创建库并切换,若有只切换库
* db :显示当前操作的库
* db.runCommand({"dropDatabase": 1}):删除当前数据库,注意此处的 1 没加双引号
## 集合操作
* show collections :显示库中的集合
* db.createCollection("集合名字") : 创建集合
* db.集合名.drop() 或 db.runCommand({"drop":"集合名"}) :删除集合
## 文档操作
### 增加文档
给某集合里面增加文档,注意:
* 若集合不存在,会自动创建集合。
* 保存成功之后,系统会自动给文档增加一个 \_id 主键字段,主键是每个文档的唯一标识,其值不能重复,就像身份证是每个人编号是唯一的。
语法格式如下:
* db.集合名.insert(BSON 格式数据),若新增的文档主键已经存在,insert 会不做操作并提示错误。
* db.集合名.save(BSON 格式数据),若新增的文档主键已经存在,save 则更改原来的内容为新内容。
~~~
db.students.insert({"name":"xx","age":18})
db.students.insert({"_id":ObjectId("5c457f78034a8e64b22cd683"),"name":"xx","age":19}) // 报错 duplicate field error collection
db.students.save({"_id":ObjectId("5c457f78034a8e64b22cd683"),"name":"xx","age":19}) // 修改
~~~
### 查询文档
查询集合中的文档,语法格式如下:
* db.集合名.find() :查询集合中所有文档
* db.集合名.find().pretty() :格式化查询到的文档
* db.集合名.findOne() :查询集合中的第一个文档,即插入最早那个
~~~
db.students.find()
db.students.find().pretty()
db.students.findOne()
~~~
### 修改文档
修改集合中的文档,语法格式如下:
* db.集合名.update(BSON 格式数据):注意:第一个参数查询的条件,第二个参数是修改的内容,但主键是不能修改。
~~~
db.students.update({"name":"xx"}, {"age":88}) // 注意原来的数据只剩下 {"age":88}
db.students.update({"name":"xx"}, {$set:{"age":88}}) // 这里就只改原来文档的 age 的值。
db.students.update({"name":"xx"}, {$set:{"age":88}}, {multi:true}) // 默认只改先找的一条,若想改多条得设置 multi 参数为 true
~~~
### 删除文档
删除集合中的文旦,语法格式如下:
* db.集合名.remove() :删除集合中的文档,可以指定条件
~~~
db.集合名.remove({}) // 删除集合中所有的文档
db.集合名.remove({"name":"yy"}) // 删除指定条件 name="yy" 的文档
~~~
## 高级查询
* 查询 field = value 的文档语法:db.集合名.find({ "field" : value })
~~~
// 查询女歌星,即查询 sex = "女"的歌星
db.singers.find({"sex":"女"})
~~~
* 查询 field > value 的文档语法:db.集合名.find({ "field" : { $gt: value } })
~~~
// 查询年龄大于 53 的歌星
db.singers.find({"age": { $gt: 53 }})
~~~
* 查询 field < value 的文档语法:db.集合名.find({ "field" : { $lt: value } })
~~~
// 查询年龄小于 35 岁的歌星
db.singers.find({"age": { $lt: 35 }})
~~~
* 查询 field >= value 的文档语法:db.集合名.find({ "field" : { $gte: value } })
~~~
// 查询成绩大于等于 95 的歌星
db.singers.find({"score": { $gte: 95 }})
~~~
* 查询 field <= value 的文档语法:db.集合名.find({ "field" : { $lte: value } })
~~~
// 查询年龄在小于等于 32 岁的歌星。
db.singers.find({"age": { $lte: 32 }})
~~~
* 查询 min < field < max 的文档语法:db.集合名.find({ "field" : { lt: max } })
~~~
// 查询年龄在 (30, 40) 岁之间的歌星
db.singers.find({"age": { $gt: 30 , $lt: 40 }})
~~~
* 查询 field != value 的文档语法:db.集合名.find({ "field" : { $ne: value } })
~~~
// 查询外国歌手
db.singers.find({"country": { $ne: "中国" }})
~~~
* 查询 field % divisor == remainder 的文档语法:db.集合名.find({ "field" : { $mod : \[ divisor, remainder \] } })
~~~
// 查询成绩为 5,15,25...95 的歌星。
db.singers.find({"score" : { $mod : [10, 5] }})
~~~
* 查询 field = value1 OR field = value2 ... OR field = valueN OR 的文档语法:db.集合名.{ field: { $in: \[value1, value2, ... valueN \] } }
~~~
// 查询序号(num)为 3 或者 6 或者 9 的歌星
db.singers.find({ "num" : { $in: ["3", "6", "9"] } })
~~~
* 查询 field value2 ... AND field <> valueN 的文档语法:db.集合名.{ field: { $nin: \[value1, value2, ... valueN \] } }
~~~
// 查询国籍不为美国和韩国的歌手
db.singers.find({"country" : { $nin: ["美国", "韩国"] }})
~~~
* 查询文档字段数量的语法:db.集合名.find({ "field" : { $size: num } }),注意 field 的值是数组。
~~~
// 查询有 3 个代表作品的歌手
db.singers.find({"works" : { $size: 3 }})
~~~
* 查询存在或不存在某个字段的的文档语法:db.集合名.find({ "field" : { $exists : true|false } })
~~~
// 查询包含 name 字段的歌手
db.singers.find({"name" : { $exists : true }})
~~~
* 查询多个条件是或的关系的文档语法:db.集合名.find({ $or : \[expression1, expression2, ..., expressionN\] })
~~~
// 查询名词是刘德华的歌手或者是女歌手
db.singers.find({$or : [{"name":"刘德华"}, {"sex":"女"}]})
~~~
* 查询多个条件是且的关系的文档语法:db.集合名.find({ $and : \[expression1, expression2, ..., expressionN\] })
~~~
// 查询名字是刘德华且是女的歌手
db.singers.find({$and : [{"name":"刘德华"}, {"sex":"女"}]})
~~~
* 若字段的值是对象的查询
~~~
db.students.insert({"name":"zs", "score":{"yw":80, "sx":91}})
db.students.insert({"name":"ls", "score":{"yw":77, "sx":95}})
// 查询语文成绩为 80 的同学
db.students.find({"score.yw": 80})
~~~
* 排序查询文档,语法格式:db.集合名.find().sort({ "field" : -1 | 1}),注意 1 代表升序,-1 代表降序
~~~
// 对所有歌星安年龄排序
db.singers.find().sort({"age": 1})
~~~
* 限制查询的个数,语法格式:db.集合名.find().limit(n)
~~~
// 实现输出 5 条
db.singers.find().limit(5)
~~~
* 跳过多少条再查询,语法格式:db.集合名.find().skip(n)
~~~
// 跳过 5 条查询
db.list.find().skip(5)
~~~
* 分页查询:语法格式:db.集合名.find().skip(n).limit(n)
```
// 假如 101 条数据,每页显示 10 条,一共分 11 页,那么要查询第 2 页的文档
// var pageSize = 10;
// var start = (2 - 1) * pageSize;
db.list.find().skip(start).limit(pagesize)
```