一、 连接数据库
// 开启服务
mongod --dbpath 数据库目录/data/db
mongo
// 清屏
cls
// 查看所有数据库列表
show dbs
二、 创建数据库、查看、删除数据库
1. 使用数据库、创建数据库
use itying
如果真的想把这个数据库创建成功,那么必须插入一个数据。 数据库中不能直接插入数据,只能往集合(collections)中插入数据。下面命令表示给 itying 数 据库的 user 表中插入数据。
db.user.insert({“name”:”xiaoming”});
2. 查看数据库
show dbs
3. 显示当前的数据集合(mysql 中叫表)
show collections
4. 删除集合,删除指定的集合 删除表
删除集合 db.COLLECTION_NAME.drop()
db.user.drop()
5. 删除数据库,删除当前所在的数据库
db.dropDatabase();
三、 插入(增加)数据
插入数据,随着数据的插入,数据库创建成功了,集合也创建成功了。
db.表名.insert({"name":"zhangsan","age":20});
四、 查找数据
1. 查询所有记录
db.user.find();
// 相当于:select* from user;
2. 查询去掉后的当前聚集集合中的某列的重复数据
db.user.distinct("name");
// 会过滤掉 name 中的相同数据
// 相当于:select distict name from user;
3. 查询 age = 22 的记录
db.user.find({"age": 22});
// 相当于: select * from user where age = 22;
4. 查询 age > 22 的记录
db.user.find({age: {$gt: 22}});
// 相当于:select * from user where age >22;
5. 查询 age < 22 的记录
db.user.find({age: {$lt: 22}});
// 相当于:select * from user where age <22;
6. 查询 age >= 25 的记录
db.user.find({age: {$gte: 25}});
// 相当于:select * from user where age >= 25;
7. 查询 age <= 25 的记录
db.user.find({age: {$lte: 25}});
8. 查询 age >= 23 并且 age <= 26 注意书写格式
db.user.find({age: {$gte: 23, $lte: 26}});
9. 查询 name 中包含 mongo 的数据 模糊查询用于搜索
db.user.find({name: /mongo/});
// 相当于%%
// select * from user where name like ‘%mongo%’;
10. 查询 name 中以 mongo 开头的
db.user.find({name: /^mongo/});
// 相当于:select * from user where name like ‘mongo%’;
11. 查询指定列 name、age 数据
db.user.find({}, {name: 1, age: 1});
// 相当于:select name, age from user;
// 当然 name 也可以用 true 或 false,当用 ture 的情况下河 name:1 效果一样,如果用 false 就是排除 name,显示 name 以外的列信息。
12、查询指定列 name、age 数据, age > 25
db.user.find({age: {$gt: 25}}, {name: 1, age: 1});
// 相当于:select name, age from user where age >25;
13、按照年龄排序 1 升序 -1 降序
升序:
db.user.find().sort({age: 1});
降序:
db.user.find().sort({age: -1});
14、查询 name = zhangsan, age = 22 的数据
db.user.find({name: 'zhangsan', age: 22});
// 相当于:select * from user where name = ‘zhangsan’ and age = ‘22’;
15、查询前 5 条数据
db.user.find().limit(5);
// 相当于:selecttop 5 * from user;
16、查询 10 条以后的数据
db.user.find().skip(10);
17、查询在 5-10 之间的数据
db.user.find().limit(10).skip(5);
// 可用于分页,limit 是 pageSize,skip 是(page-1)*pageSize
18、or 与 查询
db.user.find({$or: [{age: 22}, {age: 25}]});
// 相当于:select * from user where age = 22 or age = 25;
19、findOne 查询第一条数据
db.user.findOne();
// 相当于:selecttop 1 * from user;
// db.user.find().limit(1);
20、查询某个结果集的记录条数 统计数量
db.user.find({age: {$gte: 25}}).count();
// 相当于:select count(*) from user where age >= 20;
// 如果要返回限制之后的记录数量,要使用 count(true)或者 count(非 0)
// db.users.find().skip(10).limit(5).count(true);
四、修改数据
修改里面还有查询条件。你要改谁,要告诉 mongo。
- 查找名字叫做小明的,把年龄更改为 16 岁:
db.student.update({"name":"小明"},{$set:{"age":16}});
- 查找数学成绩是 70,把年龄更改为 33 岁:
db.student.update({"score.shuxue":70},{$set:{"age":33}});
- 更改所有匹配项目:"
By default, the update() method updates a single document. To update multiple documents, use the multi option in the update() method.
默认情况下,update()方法更新单个文档。要更新多个文档,请在update()方法中使用multi选项。
db.student.update({"sex":"男"},{$set:{"age":33}},{multi: true});
- 完整替换,不出现$set 关键字了: 注意
db.student.update({"name":"小明"},{"name":"大明","age":16});
db.users.update({name: 'Lisi'}, {$inc: {age: 50}}, false, true);
// 相当于:update users set age = age + 50 where name = ‘Lisi’;
db.users.update({name: 'Lisi'}, {$inc: {age: 50}, $set: {name: 'hoho'}}, false, true);
// 相当于:update users set age = age + 50, name = ‘hoho’ where name = ‘Lisi’;
五、 删除数据
db.collectionsNames.remove( { "borough": "Manhattan" } )
// db.users.remove({age: 132});
By default, the remove() method removes all documents that match the remove condition. Use the justOne option to limit the remove operation to only one of the matching documents.
默认情况下,remove()方法会删除符合移除条件的所有文档。使用justOne选项将删除操作限制为仅对一个匹配的文档执行。
db.restaurants.remove( { "borough": "Queens" }, { justOne: true } )
六、 索引
1. 索引基础
索引是对数据库表中一列或多列的值进行排序的一种结构,可以让我们查询数据库变得更快。MongoDB 的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的查询优化技巧。
- 下面是创建索引的命令,1(升序),-1(降序):
db.user.ensureIndex({"userame":1})
- 获取当前集合的索引:
db.user.getIndexes()
- 删除索引的命令是:
db.user.dropIndex({"username":1})
在 MongoDB 中,我们同样可以创建复合索引,如:
- 数字 1 表示 username 键的索引按升序存储,-1 表示 age 键的索引按照降序方式存储。
db.user.ensureIndex({"username":1, "age":-1})
该索引被创建后,基于 username 和 age 的查询将会用到该索引,或者是基于 username的查询也会用到该索引,但是只是基于 age 的查询将不会用到该复合索引。因此可以说,如果想用到复合索引,必须在查询条件中包含复合索引中的前 N 个索引列。然而如果查询条件中的键值顺序和复合索引中的创建顺序不一致的话,MongoDB 可以智能的帮助我们调整该顺序,以便使复合索引可以为查询所用。如:
db.user.find({"age": 30, "username": "stephen"})
对于上面示例中的查询条件,MongoDB 在检索之前将会动态的调整查询条件文档的顺序,以使该查询可以用到刚刚创建的复合索引。
对于上面创建的索引,MongoDB 都会根据索引的 keyname 和索引方向为新创建的索引自动分配一个索引名,下面的命令可以在创建索引时为其指定索引名,如:
db.user.ensureIndex({"username":1},{"name":"userindex"})
随着集合的增长,需要针对查询中大量的排序做索引。如果对没有索引的键调用 sort,MongoDB 需要将所有数据提取到内存并排序。因此在做无索引排序时,如果数据量过大以致无法在内存中进行排序,此时 MongoDB 将会报错。
2. 强制使用索引
hint 命令可以强制使用某个索引。
db.t5.find({age:{$lt:30}}).hint({name:1, age:1})
3. 唯一索引
- 在缺省情况下创建的索引均不是唯一索引。下面的示例将创建唯一索引,如:
db.user.ensureIndex({"userid":1},{"unique":true})
- 如果再次插入 userid 重复的文档时,MongoDB 将报错,以提示插入重复键,如:
db.user.insert({"userid":5})
db.user.insert({"userid":5})
> E11000 duplicate key error index: user.user.$userid_1 dup key: { : 5.0 }
- 如果插入的文档中不包含 userid 键,那么该文档中该键的值为 null,如果多次插入类似的文档,MongoDB 将会报出同样的错误,如:
db.user.insert({"abc":5})
db.user.insert({"abc":5})
> E11000 duplicate key error index: user.user.$userid_1 dup key: { : null }
【3.0+已经废弃】如果在创建唯一索引时已经存在了重复项,我们可以通过下面的命令帮助我们在创建唯一索引时消除重复文档,仅保留发现的第一个文档
- 先删除刚刚创建的唯一索引。
db.user.dropIndex({"userid":1})
- 插入测试数据,以保证集合中有重复键存在。
db.user.remove()
db.user.insert({"userid":5})
db.user.insert({"userid":5})
- 重新创建唯一索引
db.user.ensureIndex({"userid":1},{"unique":true,dropDups:true })
- 我们同样可以创建复合唯一索引,即保证复合键值唯一即可。如:
db.user.ensureIndex({"userid":1,"age":1},{"unique":true,dropDups:true})
4. 索引的一些参数
| parameter | type | Description |
|---|---|---|
| background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台的方式创建索引,即增加background可选参数,默认值:false |
| unique | Boolean | 建立的索引是否唯一, 默认值:false |
| name | string | 索引名。如果未指定,MongoDB通过拼接索引字段名和排序顺序生成一个索引名称 |
| dropDups(3.0+废弃) | Boolean | 在建立唯一索引时是否删除重复记录,指定true创建唯一索引。默认值:false |
如果在为已有数据的文档创建索引时,可以执行下面的命令,以使 MongoDB 在后台创建索引,这样的创建时就不会阻塞其他操作。但是相比而言,以阻塞方式创建索引,会使整个创建过程效率更高,但是在创建时 MongoDB 将无法接收其他的操作。
db.user.ensureIndex({"username":1},{"background":true})
5. 使用 explain
explain 是非常有用的工具,会帮助你获得查询方面诸多有用的信息。只要对游标调用该方法,就可以得到查询细节。explain 会返回一个文档,而不是游标本身。如:
explain 会返回查询使用的索引情况,耗时和扫描文档数的统计信息。
6. explain executionStats 查询具体的执行时间
db.tablename.find().explain( "executionStats" )
关注输出的如下数值:explain.executionStats.executionTimeMillis
七、 账户权限配置
1. 创建超级管理用户
use admin
db.createUser({
user:'admin', pwd:'123456', roles:[{role:'root',db:'admin'}]
})
2. 修改 Mongodb 数据库配置文件
路径:C:\Program Files\MongoDB\Server\4.0\bin\mongod.cfg
配置:
security:
authorization: enabled
3. 重启 mongodb 服务
在任务管理器中选择【服务】,在服务列表中找到MongoDB Server右键重新启动
此时可以发现使用
mongo 连接数据库获取不到信息
4. 用超级管理员账户连接数据库
mongo admin -u 用户名 -p 密码
mongo 192.168.1.200:27017/test -u user -p password
5. 给 user 数据库创建一个用户 只能访问 user 不能访问其他数据库
use user
db.createUser(
{
user: "useradmin", pwd: "123456", roles: [ { role: "dbOwner", db: "user" } ]
}
)
// dbOwner 数据库管理角色
6. Mongodb 账户权限配置中常用的命令
- 查看当前库下的用户
show users;
- 删除用户
db.dropUser("eggadmin")
- 修改用户密码
db.updateUser( "admin",{pwd:"password"});
- 密码认证
db.auth("admin","password");
7. Mongodb 数据库角色
- 数据库用户角色:
read、readWrite - 数据库管理角色:
dbAdmin、dbOwner、userAdmin - 集群管理角色:
clusterAdmin、clusterManager、clusterMonitor、hostManager - 备份恢复角色:
backup、restore - 所有数据库角色:
readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase - 超级用户角色:
root
8. 连接数据库的时候需要配置账户密码
const url = 'mongodb://admin:123456@localhost:27017/';
八、 表与表的关系
1. 一对一的关系
一个人对应一个唯一的身份证号,一个身份证对应一个人,即为一对一的关系。
2. 一对多的关系
一个班级对应多名学生,一个学生只能属于一个班级,即为一对多关系
3. 多对多的关系
九、 聚合管道(Aggregation Pipeline)
1. 管道概念
POSIX可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX )多线程的使用方式中, 有一种很重要的方式-----流水线(亦称为“管道”)方式,“数据元素”流串行地被一组线程按顺序执行。
以面向对象的思想去理解,整个流水线,可以理解为一个数据传输的管道;该管道中的每一个工作线程,可以理解为一个整个流水线的一个工作阶段stage,这些工作线程之间的合作是一环扣一环的。靠输入口越近的工作线程,是时序较早的工作阶段stage,它的工作成果会影响下一个工作线程阶段(stage)的工作结果,即下个阶段依赖于上一个阶段的输出,上一个阶段的输出成为本阶段的输入。这也是pipeline的一个共有特点!
使用聚合管道可以对集合中的文档进行变换和组合。
实际项目:表关联查询、数据的统计。
MongoDB 中使用db.COLLECTION_NAME.aggregate([{<stage>},...]) 方法
来构建和使用聚合管道。先看下官网给的实例,感受一下聚合管道的用法。
解释:
- 第一阶段(stage)在orders表中匹配(筛选)出所有
status:'A'的数据。 - 第二阶段对上一阶段的数据按照
cust_id进行分组,并且对组内数据字段amount进行求和,结果赋值给total。
2. 管道操作符
| 操作符 | 简述 |
|---|---|
| $project | 投射操作符,用于重构每一个文档的字段,可以提取字段,重命名字段,甚至可以对原有字段进行操作后新增字段 |
| $match | 匹配操作符,用于对文档集合进行筛选 |
| $group | 分组操作符,用于对文档集合进行分组 |
| $unwind | 拆分操作符,用于将数组中的每一个值拆分为单独的文档 |
| $sort | 排序操作符,用于根据一个或多个字段对文档进行排序 |
| $limit | 限制操作符,用于限制返回文档的数量 |
| $skip | 跳过操作符,用于跳过指定数量的文档 |
| $lookup | 连接操作符,用于连接同一个数据库中另一个集合,并获取指定的文档,类似于populate |
| $count | 统计操作符,用于统计文档的数量 |
db.collection.aggregate([])是聚合管道查询使用的方法,参数是数组,每个数组元素就是一个stage,stage中运用操作符对数据进行处理后再交由下一个stage,直到没有下个stage,就输出最终的结果
3. 管道表达式
管道操作符作为“键”,所对应的“值”叫做管道表达式。
例如{$match:{status:"A"}},$match 称为管道操作符,而status:"A"称为管道表达式,是管道操作符的操作数(Operand)。
每个管道表达式是一个文档结构,它是由字段名、字段值、和一些表达式操作符组成的。
| 常用表达式操作符 | Description |
|---|---|
| $addToSet | 将文档指定字段的值去重 |
| $max | 文档指定字段的最大值 |
| $min | 文档指定字段的最小值 |
| $sum | 文档指定字段求和 |
| $avg | 文档指定字段求平均 |
| $gt | 大于给定值 |
| $lt | 小于给定值 |
| $eq | 等于给定值 |
4. 基础使用示例
演示案例使用以下数据
1. $project
要求:查找 order 只返回文档中 trade_no 和 all_price 字段
db.order.aggregate([
{
$project:{ trade_no:1, all_price:1 }
}
])
-->
{ "_id" : ObjectId("5fc1f01319245fa314447af7"), "trade_no" : "111", "all_price" : 100 }
{ "_id" : ObjectId("5fc1f05319245fa314447af8"), "trade_no" : "222", "all_price" : 90 }
{ "_id" : ObjectId("5fc1f05d19245fa314447af9"), "trade_no" : "333", "all_price" : 20 }
2. $match
用于过滤文档。用法类似于 find() 方法中的参数。
db.order.aggregate([
{
$project:{ trade_no:1, all_price:1 }
},
{
$match:{"all_price":{$gte:90}}
}
])
-->
{ "_id" : ObjectId("5fc1f01319245fa314447af7"), "trade_no" : "111", "all_price" : 100 }
{ "_id" : ObjectId("5fc1f05319245fa314447af8"), "trade_no" : "222", "all_price" : 90 }
3. $group
将集合中的文档进行分组,可用于统计结果。
统计每个订单的订单数量,按照订单号分组
db.order_item.aggregate(
[
{
$group: {_id: "$order_id", total: {$sum: "$num"}}
}
]
)
-->
{ "_id" : "2", "total" : 2 }
{ "_id" : "1", "total" : 3 }
{ "_id" : "3", "total" : 6 }
4. $sort
将集合中的文档进行排序。
db.order.aggregate([
{
$project:{ trade_no:1, all_price:1 }
},
{
$match:{"all_price":{$gte:90}}
},
{
$sort:{"all_price":-1}
}
])
-->
{ "_id" : ObjectId("5fc1f01319245fa314447af7"), "trade_no" : "111", "all_price" : 100 }
{ "_id" : ObjectId("5fc1f05319245fa314447af8"), "trade_no" : "222", "all_price" : 90 }
4. $limit
只取一条
db.order.aggregate([
{
$project:{ trade_no:1, all_price:1 }
},
{
$match:{"all_price":{$gte:90}}
},
{
$sort:{"all_price":-1}
},
{
$limit:1
}
])
-->
{ "_id" : ObjectId("5fc1f01319245fa314447af7"), "trade_no" : "111", "all_price" : 100 }
5. $skip
从第一条开始
db.order.aggregate([
{
$project:{ trade_no:1, all_price:1 }
},
{
$match:{"all_price":{$gte:90}}
},
{
$sort:{"all_price":-1}
},
{
$skip:1
}
])
-->
{ "_id" : ObjectId("5fc1f05319245fa314447af8"), "trade_no" : "222", "all_price" : 90 }
6. $lookup 表关联
| 语法值 | 解释说明 |
|---|---|
| from | 同一个数据库下等待被Join的集合。 |
| localField | 源集合中的match值,如果输入的集合中,某文档没有 localField这个Key(Field),在处理的过程中,会默认为此文档含有 localField:null的键值对。 |
| foreignField | 待Join的集合的match值,如果待Join的集合中,文档没有foreignField值,在处理的过程中,会默认为此文档含有 foreignField:null的键值对。 |
| as | 为输出文档的新增值命名。如果输入的集合中已存在该值,则会覆盖掉 |
db.order.aggregate([
{
$lookup: {
from: "order_item",
localField: "order_id", foreignField: "order_id", as: "items"
}
}
])
-->
{
"_id": ObjectId("5fc1f01319245fa314447af7"),
"order_id": "1",
"uid": 10,
"trade_no": "111",
"all_price": 100,
"all_num": 2,
"items": [{
"_id": ObjectId("5fc1f06e19245fa314447afa"),
"order_id": "1",
"title": "商品鼠标 1",
"price": 50,
"num": 1
}, {
"_id": ObjectId("5fc1f06e19245fa314447afb"),
"order_id": "1",
"title": "商品键盘 2",
"price": 50,
"num": 1
}, {
"_id": ObjectId("5fc1f08219245fa314447afc"),
"order_id": "1",
"title": "商品键盘 3",
"price": 0,
"num": 1
}]
}
{
"_id": ObjectId("5fc1f05319245fa314447af8"),
"order_id": "2",
"uid": 7,
"trade_no": "222",
"all_price": 90,
"all_num": 2,
"items": [{
"_id": ObjectId("5fc1f08619245fa314447afd"),
"order_id": "2",
"title": "牛奶",
"price": 50,
"num": 1
}, {
"_id": ObjectId("5fc1f08619245fa314447afe"),
"order_id": "2",
"title": "酸奶",
"price": 40,
"num": 1
}]
}
{
"_id": ObjectId("5fc1f05d19245fa314447af9"),
"order_id": "3",
"uid": 9,
"trade_no": "333",
"all_price": 20,
"all_num": 6,
"items": [{
"_id": ObjectId("5fc1f08619245fa314447aff"),
"order_id": "3",
"title": "矿泉水",
"price": 2,
"num": 5
}, {
"_id": ObjectId("5fc1f08819245fa314447b00"),
"order_id": "3",
"title": "毛巾",
"price": 10,
"num": 1
}]
}
十、MongoDB 备份(mongodump)与恢复(mongorestore)
数据库迁移需要使用备份(mongodump)与恢复(mongorestore)语法,如果数据库没有开启权限验证,则直接使用命令即可
$ mongodump -h dbhost -d dbname -o dbdirectory
$ mongorestore -h <hostname><:port> -d dbname <path>
如果开启了验证登录,是无法直接备份和恢复数据库的,报如下错误:
Failed: koa.user: error reading database: command listCollections requires authentication
即使我们加上了 -u <username> -p=<password>参数进行验证,也可能报错:
Failed: error connecting to db server: server returned error on SASL authentication step: Authentication failed
解决方案
添加:-u <username> -p=<password>
添加:--authenticationDatabase admin
mongorestore -h 127.0.0.1:27017 -d koa -u admin -p=123456 -o C:\Users\yq612\Desktop\koa --authenticationDatabase admin
!! 注意,密码p后面要加=
- 备份
mongodump -h 127.0.0.1 -d newsDB -u admin -p=123456 -o D:\MongoDB\bf\newsDB --authenticationDatabase admin
- 还原
mongorestore -h 127.0.0.1 -d newsDB2 -u admin -p=123456 D:\MongoDB\bf\newsDB --authenticationDatabase admin
十一、 Nodejs 中使用 Mongodb
-
- 创建项目文件夹
- 创建项目文件夹
-
- 引入
mongodb
- 引入
npm init --yes
cnpm install mongodb --save
【文档】https://github.com/mongodb/node-mongodb-native
- 3. 连接
新建index.js
//index.js
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
// const url = 'mongodb://admin:123456@localhost:27017/'; //有密码连接方式
const dbName = 'ybs';
const client = new MongoClient(url, { useUnifiedTopology: true });
client.connect(function (err) {
if (err) {
console.log(err);
return;
}
console.log("连接成功");
/*
数据操作
...
*/
// 完成数据库操作要关闭连接
client.close();
});
// 运行
node index.js
-
- 查找示例
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'itying';
const client = new MongoClient(url, { useUnifiedTopology: true });
client.connect(function (err) {
if (err) {
console.log(err);
return;
}
const db = client.db(dbName); //获取 db 对象
db.collection("user").find({}).toArray(function (err, data) { //查找
if (err) {
console.log(err);
return;
}
console.log(data);
client.close();
})
});
-
- 增加数据
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'itying';
const client = new MongoClient(url, { useUnifiedTopology: true });
client.connect(function (err) {
if (err) {
console.log(err);
return;
}
const db = client.db(dbName); //获取 db 对象
// 增加数据
db.collection("user").insertOne({ "name": "nodejs", "age": 10 }, (err, result) => {
if (err) {
console.log(err);
return;
}
console.log(result);
client.close();
})
});
-
- 修改数据
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'itying';
const client = new MongoClient(url, { useUnifiedTopology: true });
client.connect(function (err) {
if (err) {
console.log(err);
return;
}
const db = client.db(dbName); //获取 db 对象
// 修改数据
db.collection("user").updateOne({ "name": "zhangsan12" }, { $set: { "age": 50 } }, (err, result) => {
if (err) {
console.log(err);
return;
}
console.log(result);
client.close();
})
});
-
- 删除数据
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'itying';
const client = new MongoClient(url, { useUnifiedTopology: true });
client.connect(function (err) {
if (err) {
console.log(err);
return;
}
const db = client.db(dbName); //获取 db 对象
// 删除数据
db.collection("user").deleteOne({ "name": "zhangsan12" }, function (err, result) {
if (err) {
console.log(err);
return;
}
console.log(result);
client.close();
});
});