### 安装在Ubuntu16.04:
```bash
# 创建数据库文件夹,这个是默认存放数据的地方,但MongoDB不会自动创建
mkdir -p /data/db
# 增加权限
chown -R $USER:$USER /data/db
wget -qO - https://www.mongodb.org/static/pgp/server-4.0.asc | sudo apt-key add - # 导入公钥
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list # 为MongoDB创建列表文件
sudo apt-get update # 重新加载本地数据包
sudo apt-get install -y mongodb-org=4.0.12 mongodb-org-server=4.0.12 mongodb-org-shell=4.0.12 mongodb-org-mongos=4.0.12 mongodb-org-tools=4.0.12 # 安装软件包
echo "mongodb-org hold" | sudo dpkg --set-selections
echo "mongodb-org-server hold" | sudo dpkg --set-selections
echo "mongodb-org-shell hold" | sudo dpkg --set-selections
echo "mongodb-org-mongos hold" | sudo dpkg --set-selections
echo "mongodb-org-tools hold" | sudo dpkg --set-selections # 保持目前的版本,防止自动更新
# 启动
sudo service mongodb start #或者
systemctl start mongodb
```
### 卸载
```bash
sudo apt-get purge mongodb-org*
sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongodb
```
### 通过systemctl start mongodb 启动时的错误解决
报错如下:
Failed to start mongodb.service: Unit mongodb.service not found.
解决:
```bash
sudo nano /etc/systemd/system/mongdb.service # 创建服务文件
```
```bash
[Unit]
Description=High-performance, schema-free document-oriented database
After=network.target
[Service]
User=mongodb
ExecStart=/usr/bin/mongod --quiet --config /etc/mongod.conf
# 制定需要执行的命令的位置, # 制定项目运行所需要的配置文件位置
[Install]
WantedBy=multi-user.target
```
- 按ctrl + x 保存退出
- systemctl start mongodb
- 永久有效: systemctl enable mongodb
### 1、文档中值的数据类型
在mongodb中数据是以文档的形式存在的
1、每个文档相当于一条记录
2、多个文档组合在一起,就是集合,一个集合就相当于一张数据表
3、多个集合组合在一起,就是数据库,每个数据库就是一个文件夹
- Object ID: ⽂档ID
- String: 字符串, 最常⽤, 必须是有效的UTF-8
- Boolean: 存储⼀个布尔值, true或false
- Integer: 整数可以是32位或64位, 这取决于服务器
- Double: 存储浮点值
- Arrays: 数组或列表, 多个值存储到⼀个键
- Object: ⽤于嵌⼊式的⽂档, 即⼀个值为⼀个⽂档
- Null: 存储Null值
- Timestamp: 时间戳, 表示从1970-1-1到现在的总秒数
- Date: 存储当前⽇期或时间的UNIX时间格式
### 2、Mongo shell
- 使用mongo命令自动启动shell,并连接数据库服务器,该shell能够解析JavaScript的语法
- 通过shell可以完成一些列的增删改查工作
### 3、文档的基本增删改查
- 增加:使用insert()函数,将要插入的数据放到集合中
- 比如增加一个文档,首先先创建文档变量
post = {"title": "my first mongo"}
- 插入文档到blog集合中
db.blog.insert(post)
- 批量插入:同样适用inset方法,将你需要插入的文档全部存放到一个数组中
- 当我们连接到服务器之后,**db**会自动指向正在使用的数据库,blog为集合名,没有会自动创建
- 查找:使用find()或者findOne()
- find()默认显示20条数据
db.blog.find()
- findOne()只显示一条数据
db.blog.findOne()
- 更新:使用**update()**方法, 该方法接收两个参数,第一个为需要修改的文档,第二个修改过的文档
**update()方法除了接收前两个参数外,还可以接收另外两个参数,并且这两个参数都是布尔值**
**例如:db.blog.update({"title": "My Blog Post"}, {"title": "My Blog Get"}, true, true)**
**第三个参数表示upsert,能够保证操作的原子性,同时在没有查询到制定的集合情况下,会以查询条件和更新文档为基础新建一个文档,第四个参数默认是true,表示更新所有符合条件的集合,若改为false,则只更新第一个。**
- 第一:先更改文档对象的属性,也就是键值对,比如前文使用到的post变量
给他增加一个属性:post.comments = []
- 找到旧文档,替换为新的文档,根具给出的一个参数中的具体信息可以找到旧文档
db.blog.update({"title":"my first mongo"}, post)
- 删除:remove(),在不给出参数的情况下,删除集合中的所有文档,给出参数后,会删除对应的文档
- db.blog.remove({"title":"my first mongo"})
### 4、基于shell的其他命令,利用help查看
```shell
> help
```
| db.help() | help on db methods |
| -------------------------- | --------------------------------------------------------- |
| db.mycoll.help() | help on collection methods |
| sh.help() | sharding helpers |
| rs.help() | replica set helpers |
| help admin | administrative help |
| help keys | key shortcuts |
| help connect | connecting to a db help |
| help misc | misc things to know |
| help mr | mapreduce |
| show dbs | show database names |
| show collections | show collections in current database |
| show users | show users in current database |
| show profile | show most recent system.profile entries with time >= 1ms |
| show logs | show the accessible logger names |
| use <db_name> | set current database |
| db.foo.find() | list objects in collection foo |
| db.foo.find( { a : 1 } ) | list objects in foo where a == 1 |
| it | result of the last line evaluated; use to further iterate |
| DBQuery.shellBatchSize = x | set default number of items to display on shell |
| exit | quit the mongo shell |
### 5、访问集合
1、当集合名与内置的函数名相同时,是没有办法通过**db.集合名**来访问的
2、当集合名名包含**-**时,比如:**six-four**,同样没有办法通过正常的方法访问集合,引文shell对其的解释是两个变量的相减
**最终的解决办法是通过__getCollection()__来访问,**例如:db.getCollection("version"),db.getCollection("six-four")这样才能正常访问
### 6、save shell
save是一个shell函数,可以在文档不存在的时候插入,存在的时候更新,它只有一个参数,即文档本身
save会调用upsert,即实现文档的自动创建和更新
var x = db.foo.findOne()
x.num = 12
db.foo.save(x)
### 7、修改器,以$号开头的特殊字符串
| 修改器 | 用法 | 作用 |
| ---- | :--- | :--- |
| $inc | db.x.update({"a":1}, {$inc: {"b":1}}) |在原有b的基础上加1|
| $set | db.x.update({"a":1}, {$set: {"b":"a"}}) |将原有b的数据修改为a,若b不存在,则创建一个|
| $unset | db.x.update({"a":1}, {$unset: {"b":1}}) |撤销修改,此时的数值1,表示条件为真的意思|
| $push | db.x.update({"a":1}, {$push:{"comments": {"name":"json"}}}) |该修改器只作用于数组,可以向文档中已有数组中添加值,当然数组不存在时,创建一个数组并添加对应内容|
| $pop | db.x.update({"a":1}, {$pop:{"comments": 1}}) db.x.update({"a":1}, {$pop:{"comments": -1}}) |基于数组位置的删除,1表示从数组的尾部删除一个元素,-1表示从数组的头部删除一个元素|
| $addToSet | db.x.update({"a":1}, {$addToSet:{"email": "aa@163.com"}) |该修改器只作用于数组,可以向数组中添加一个值,若数组中已经有了该值,则添加失败|
| $each | db.x.update({"a":1}, {$addToSet:{"email": {$each: ["1", "2", "3"]}}) |可以遍历出数组中的每个值,通常与$addToSet搭配使用,完成多个值得添加,如果存在不添加|
| $pull | db.x.update({"a":1}, {$pull:{"email": "1"}}) |指定数组中的具体元素进行删除,若匹配到多个值,匹配多少删除多少|
| $数组定位符 | db.x.update({"comments.author": "jhon"}, {$set:{"comments.$.author": 'lili'}}) |定位符只匹配第一个匹配到的文档|
### 8、查询
#### 8.1、指定键返回查询
- 查询使用find函数或者findOne函数
- 查询返回指定字段,也就是查询指定键
- db.blog.find({"a":2}, {"ss":1, "wo": 0}),当匹配到包含{"a":2}的文档后,只返回"_id" 和"ss"两个键,"_id"是默认返回的,其中1表示真,0表示假
#### 8.2、条件查询
| 命令 | 用法 | 作用 |
| ------- | ----------------------------------------------------------- | ------------------------------------------------------------ |
| $gt | db.users.find({"age": {"$gt": 20}}) | 查询users集合中年龄大于20的文档 |
| $gte | db.users.find({"age": {"$gte": 20}}) | 查询users集合中年龄大于等于20的文档 |
| $lt | db.users.find({"age": {"$lt": 20}}) | 查询users集合中年龄小于20的文档 |
| $lte | db.users.find({"age": {"$lte": 20}}) | 查询users集合中年龄小于等于20的文档 |
| $ne | db.users.find({"username": {"$ne": "andy"}}) | 查询users集合中用户名不等于andy的文档 |
| $or | db.users.find({"$or": [{"age": 20},{"username": "andy"} ]}) | $or 键对应的值是一个数组,满足数组中任何一个条件的文档都会被查找出来,可作用于不同的键 |
| $in | db.users.find({"age": {"$in": [12 ,10, 22]}}) | $in键对应的值是一个数组,满足数组中任何一个条件的文档都会被查找出来,但是它只作用于同一个键 |
| $nin | db.users.find({"age": {"$nin": [12 ,10, 22]}}) | $nin键对应的值是一个数组,不满足数组中任何一个条件的文档都会被查找出来,它也只作用于同一个键 |
| $mod | db.users.find({"age": {"$mod": [5, 1]}}) | $mod表示取模运算,运算完成后在作为查询条件,它对应的值是一个数组,该数组只有两个值且有顺序,第一个表示除数,第二个表示余数,在这里表达的意思是,找出年龄除以5后余数为1的文档 |
| $not | db.users.find({"$not":{"age": {"$mod": [5, 1]}}}) | $not是元条件查询,即作用其他条件查询之上,例如他与$mod取模运算结合使用,表示取非 |
| $existe | db.users.find({"x":{"$existe": true}}) | $existe判断该键是否真实存在,存在则显示出来 |
| /joy/i | db.users.find({"username": /joy/i}) | 这里是利用正则表达式去匹配所查询文档的某个键对应的值,i表示不区分大小写 |
$where 自定义查询
```js
db.stu.find({
$where:function() {
return this.age>30;} // this 代表当前查询的文档,找出年龄大于30岁的文档
})
```
#### 8.3、数组查询
##### 8.3.1、$all
- 创建如下集合并加入三条文档:
db.fruit.insert({"_id": 1, "fruits": ["banana", "peach", "apple"]})
db.fruit.insert({"_id": 2, "fruits": ["orange", "kumquat", "apple"]})
db.fruit.insert({"_id": 3, "fruits": ["banana", "cherry", "apple"]})
- {"$all": ["banana", "apple"]},作用于数组,
使用db.fruit.find({"fruits": {"$all" : ["banana", "apple"]}}),执行结果是数值fruits中只要包含有"banana"和"apple"元素的文档,全部返回,忽略数组中元素的顺序
- 特殊用法
{"fruits": {"$all" : ["banana"]}}和{"fruits":"banana"},这两条查询效果是一样的
- 基于数组下标的查找
比如:db.fruit.find({"fruits.2":"apple"}),只要数组中指定位置的元素匹配正确,就会返回结果
##### 8.3.2、$size
- 查询固定数组长度的文档
- 用法:db.fruit.find({"fruits": {"$size": 3}})
- 不能配合其他条件命令使用,例如$gt,而实际生产中,总是需要我们去查找数组范围的文档
- 在文档中创建一个size的字段来专门描述数组的长度,变相的通过size的值来范围查找
- 例如上文:增加一个size字段,db.fruit.update({"_id": 1}, {"$inc": {"size": 3}})
- 增加数组的内容时,同时增加size的值
db.fruit.update({"_id": 1}, {"$push" : {"fruits": "pear"}, "$inc" : {"size": 1}})
- 最后就可以进行范围查找了
db.fruit.find({"size": {"$gt": 3}})
##### 8.3.3、$slice
- 对查找到的数组进行切片处理,并返回新的切片后的文档
- 使用$slice必须配合find函数的第二个参数使用,find的第二参数是指定文档中的键返回的,未提及或者对应值为0,将不会被返回,当使用$slice后,即使不提及文档中的其他键同样会返回
```mongo
db.fruit.find({"_id" : 1}, {"fruits": 1})
>{ "_id" : 1, "fruits" : [ "banana", "peach", "apple", "pear"]} # 这里_id默认返回
# 如果将_id的值变为0,则不会被返回
db.fruit.find({"_id" : 1}, {"fruits": 1, "_id": 0}) # 数字代表真假
>{"fruits" : [ "banana", "peach", "apple", "pear"]}
# 当对返回的键使用切片后$slice,文档中所有的键默认都会返回
db.fruit.find({"_id": 1}, {"fruit": {"$slice": 3}})
>{ "_id" : 1, "fruits" : [ "banana", "peach", "apple" ], "size" : 4 }
```
- 对数组正向切割
db.fruit.find({"_id": 1}, {"fruit": {"$slice": 3}}), $slcie对应的值是正数即可,得到数组的前三个元素
- 对数组反向切割
db.fruit.find({"_id": 1}, {"fruit": {"$slice": -3}}), $slcie对应的值是负数即可,得到数组的后三个元素
- 对数组指定范围切片
db.fruit.find({"_id": 1}, {"fruit": {"$slice": [1, 3]}}),$slice对应的值为数组,该数组表示你要操作的数组下标
,得到结果:**{ "_id" : 1, "fruits" : [ "peach", "apple", "pear" ], "size" : 4 }**,包含起始和结束下标
#### 8.4、查询内嵌文档
- 在mongodb中文档的表现形式,像极了javascript中的对象,所以可以通过**对象.属性**的方法,来访问MongoDB中的内嵌文档,但也可以匹配整个内嵌文档
- 方法一:如有文档
{"name": {"first": "Joe", "last": "Schmoe"}, "age": 27},在集合people中
db.people.find({"name": {"first": "Joe", "last": "Schmoe"}}),这里可以找到有这个内嵌文档的文档,但是有一个缺点,就是查询条件必须匹配整个内嵌文档,否则就会匹配失败,同时顺序也不能够错,也就是要求精确匹配
- 方法二:通过**对象.属性**的方法来访问,就不存在限制了
db.people.find({"name.first": "Joe", "name.last": "Schmoe"}),这里就要求精确匹配了
- $elemMatch
将限定条件进行分组,仅当需要对一个内嵌文档的多个键操作时才会用到
db.blog.find({"comments": {"$elemMatch": {"author": "joe", "score": {"$gte": 5}}}})
### 9、排序、分页和跳过
#### 9.1、排序sort
- 使用方法:db.collection.find().sort({"username": 1})
- 返回结果按升序排列,同时可以设置多个键排序
db.collection.find().sort({"username":1, "age": -1})
先以username升序排列,若username相同,则按照age降序排列
#### 9.2、分页limit
- 使用方法:
db.collection.find().limit(20),表示每页返回20条数据
#### 9.3、跳过skip
- db.collection.find().skip(20),跳过前20个数据
**组合实现分页查询**
- db.collection.find().limit(20).sort({"age": -1})
按照年龄从大到小取出前二十条文档
- db.collection.find().limit(20).skip(20).sort({"age": -1})
跳过前20条,取后20条的结果
### 10、创建索引
- 方法:db.users.ensureindex({"username": 1})
### 11、游标对象
使用find函数返回的对象可以赋值给一个变量,这个变量就可以理解为是一个游标对象
**游标对象属性** :可迭代(iterable)
**游标对象方法** :hasNext()判断是否还有下一条数据
next()获取下一条数据
### 12、聚合函数(aggregate)
#### 12.1、count函数
db.stu.count() # 返回fruit集合中的总文档数
现有如下集合mycol:
```javascript
{ "_id" : 1, "name" : "tom", "sex" : "男", "score" : 100, "age" : 34 }
{ "_id" : 2, "name" : "jeke", "sex" : "男", "score" : 90, "age" : 24 }
{ "_id" : 3, "name" : "kite", "sex" : "女", "score" : 40, "age" : 36 }
{ "_id" : 4, "name" : "herry", "sex" : "男", "score" : 90, "age" : 56 }
{ "_id" : 5, "name" : "marry", "sex" : "女", "score" : 70, "age" : 18 }
{ "_id" : 6, "name" : "john", "sex" : "男", "score" : 100, "age" : 31 }
```
#### 12.2、$group,$sum
db.stu.aggregate({$group:{_id: "$sex", Count: {"$sum": 1}}})
#### 12.3、$group,$push
```json
db.stu.aggregate(
{$group:
{
_id:"$gender",
name:{$push:"$name"}
}
}
) 把所有的数据放到一起
$$ROOT, 把整个文档放到一个数组中
db.stu.aggregate(
{$group:
{
_id:null,
name:{$push:"$$ROOT"}
}
}
)
```
#### 12.4、$match`
```json
db.stu.aggregate(
{$match:{age:{$gt:20}}
)
```
#### 12.5、$project
```json
db.stu.aggregate(
{$project:{_id:0,name:1,age:1}}
) //控制显示的文档键
```
#### 12.6、$sort
```json
db.stu.aggregate(
{$group:{_id:"$gender",counter:{$sum:1}}},
{$sort:{counter:-1}}
)
```
#### 12.7、$limit,$skip
```json
db.stu.aggregate(
{$group:{_id:"$gender",counter:{$sum:1}}},
{$sort:{counter:-1}},
{$skip:1},
{$limit:1}
)
```
### 13、数据库命令(runCommand)
#### 13.1、distinct函数
db.runCommand({"distinct": "fruit" , "key": "fruits"})
runCommand指令表示运行命令,括号内的参数的第一个键为具体的指令,指令所对应的的值是要操作的集合,key对应的是要操作的键。
distinct所表达的意思是找出集合中某个键对应多少不同的值,也就是去重。
#### 13.2、group函数
- 对文档进行分组,stocks集合中有以下文档:
db.stock.insert({"day" : "2010/10/03", "time": "10/03/2010 03:57:01 GMT-400", "price": "4.23"})
db.stock.insert({"day" : "2010/10/04", "time": "10/04/2010 11:28:39 GMT-400", "price": "4.27"})
db.stock.insert({"day" : "2010/10/03", "time": "10/03/2010 05:00:22 GMT-400", "price": "4.10"})
db.stock.insert({"day" : "2010/10/06", "time": "10/06/2010 05:27:58 GMT-400", "price": "4.30"})
db.stock.insert({"day" : "2010/10/04", "time": "10/04/2010 08:34:50 GMT-400", "price": "4.01"})
获取到每天最新的股票交易价格
- 使用数据库命令的模式来执行group命令:
```javascript
db.runCommand({"group":{
"ns": "stock",
"key": {"day": true}, // 程序执行到这里之后,就已经对集合分组完毕了
"$keyf": function(X){
return X.toLowerCase() // 将需要排序的键进行转换,比如进行大小写转换
},
"initial":{"time":0}, // 每组文档遍历时候的初始值
"$reduce":function(doc, prev){
if(doc.time>prev.time){ // 判断时间大小,更新要显示的最近时间
prev.time = doc.time;
prev.price= doc.price;
}
},
"condition": {"day": {"$gt": "2010/10/03"}},// 对分组进行条件限制,必须大于条件时间
"finalize":function(prev){ //对返回结果做最后的修改,和限定
...
}
}})
```
**参数解析**
group:对集合执行的操作,即分组
ns:需要操作的数据集
key:以文档中的哪个键进行分组
$keyf: 对需要分组的键进行条件转换
initial: 每组文档的初始化值,也就是最终分完组后显示的字段的初始化值
$reduce: 对分组后的显示文档做最后的操作,在上述案例中,就是找到那一组文档中时间最新的文档,然后把他的时间和股票交易价格显示到我们可以看到的结果
condition: 对分组进行条件限制
finalize: 对返回的结果做最后的修改
#### 13.3、findAndModify
db.runCommand({"findAndModify": "stock", # 操作集合stock
"query": {"day":{"$gt": "2010/09/30"}}, # 查询条件
"sort": {"day": -1}, # 排序键
"remove": true # 是否删除文档true
})
#### 13.4、创建固定大小的集合
db.creatCollection("my_collection",{"capped": true, "size": 100000})
**参数解析**
my_collection:集合名
capped:表示是否限值集合的大小,true表示限值,false表示不限制
size:表示集合大小,这里表示100000个字节
更多技术资讯可关注:gzitcast