MongoDB
适用场景
- 处理大量低价值的数据而且对性能有很高的要求
- 可以用mongoDB来实现对数据的缓存
- 可以搭建集群环境,实现存储数据的横向扩展
mac安装
- brew tap mongodb/brew
- brew update
- brew install mongodb-community@6.0
常用命令
查看配置信息
- vim /usr/local/etc/mongod.conf
systemLog:
destination: file
path: /usr/local/var/log/mongodb/mongo.log
logAppend: true
storage:
dbPath: /usr/local/var/mongodb
net:
bindIp: 127.0.0.1, ::1
ipv6: true
启动
- 二选一
- mongod --config /usr/local/etc/mongod.conf
- brew services start mongodb/brew/mongodb-community
连接
- mongosh
- 默认名称是test,连接的mongo默认端口是27017
- 连接成功后进入可交互命令窗口,会自带语法高亮,且支持js运行
命令
查询数据库
admin 40.00 KiB
config 60.00 KiB
local 40.00 KiB
使用/切换数据库
查询当前所在数据库
删除库
- 进入库
- db.dropDatabase()
- 会返回 {ok:1,dropped:库名}
查询表
插入数据
local> db.student.insert({name:'cui'})
DeprecationWarning: Collection.insert() is deprecated. Use insertOne, insertMany, or bulkWrite.
{
acknowledged: true,
insertedIds: { '0': ObjectId("62f0f3aaa60c6a176ea71ca5") }
}
查询表数据
- db.表名.find()
- 默认是查询全部
- find({})等同于全部,第一个参数可以进行条件查询
- 第二个参数可以进行字段筛选,0 为显示,1位隐藏, 0和1不能同时存在
db.student.find({name:'cui'},{age:1});
db.student.find({},{name:0, age:1})
db.student.find({$or:[{name:'cui0'},{name:'cui1'}]})
db.student.find({age:{$gt:7, $lt:9}})
db.student.find({age:{$ne:7}})
db.student.find({name:/^cui/})
db.student.find({"address.value":"cc", 'address.num': "1"})
db.student.find({"ary":{$all:[1,2,5]}});
db.student.find({"ary":{$size:3}});
修改
- 想要修改肯定需要先查询,所以update第一个参数与find一致
- 第二个参如果直接写内容的话,会进行覆盖性修改,会把原来的信息干掉
- 如果想增加式修改的话,使用$set
- updateMany是全部,one是单条
db.student.update({"address.value":"cc"}, {$set:{name: "haha"}})
删除
db.student.deleteMany({})
给表创建用户
db.createUser({user:'cui',pwd:'csl',roles:[{role:权限,db:库名}]})
开启权限校验
- 连续两次command+c退出交互窗口
- vim /usr/local/etc/mongod.conf
security:
authorization: enabled
数据库身份
- dbOwner 该数据库的所有者,具有该数据库的所有权限
- dbAdmin 一些数据库对象的管理操作,但没有读写权限
- userAdmin 为当前用户创建、修改用户和觉得。拥有userAdmin权限的用户可以将该数据库的任意权限赋予任意用户
重启
- 修改配置文件需重启
- brew services restart mongodb-community
- 这个时候再通过mongosh链接数据库,发现没权限了,需要登录
登录
- db.auth('用户名')
- 回车,然后输入密码,回车,返回ok代表登录成功
备份数据
- mongodump --db 数据库 --collection 表名 --out backup 备份数据库
还原
导出指定格式
- mongoexport -d 数据库 -c 表 --type 类型(json、csv) -f _id,name -o 导出文件名
可视化工具
- connect-》new Connection 建立一个新的链接
- url就是要访问的数据库
- edit 可编辑信息,如端口号、ip、权限
在node中使用
初体验
connect
- 想要在数据库上增删改查,肯定得先连接
- connect 连接库,两个参数,数据库url,callback
- callback的第一个参数为err,如果err存在,代表连接失败
const mongoose = require("mongoose");
mongoose.connect("mongodb://127.0.0.1:27017/web", function (err) {
if (err) return console.log(`异常信息${err}`);
console.log("连接成功");
});
Schema
- 对内容进行写入的时候,mongo是比较自由的,那么也会导致大量数据的不规范性
- 使用Schema描述一个表结构,在存储的时候,会根据结构进行校验
- 校验不通过,会抛出异常
- 如果插入的数据中有某个项存在,而描述的表结构中不存在,虽然校验会通过,但不会多余内容不会插入到库中
const UserSchema = mongoose.Schema({
name: {
type: String,
require: true,
unique: true,
lowercase: true,
trim: true,
},
pwd: {
type: String,
validate(value) {
return value.length > 0;
},
},
age: {
type: Number,
default: 0,
min: 0,
max: 120,
},
gender: {
type: Number,
enum: [0, 1],
},
address: {
num: Number,
value: String,
},
});
方法拓展,自定义api
- 比如说想根据name查询,但并没有对应方法可以直接查询,那么我们可以将方法封装在Schema基类上
User.findOne({ name: "xxx" });
UserSchema.statics.findName = function (username) {
return this.findOne({ name: username });
};
User.findName("cc")
plugin
- 如果扩展的多的话,可以使用plugin
- 会将this当做第一个参数传递给plugin的callback,第二个参也传过去
function plugin(User, options) {
User.statics.findName = function (username) {
return this.findOne({ name: username });
};
...
}
UserSchema.plugin(plugin, {xxx: 'xxx'})
创建时间、修改时间
const UserSchema = mongoose.Schema({
birtyday: {
type: Date,
default: Date.now,
},
});
const UserSchema = mongoose.Schema(
{
....
},
{
timestamps: {
createAt: true,
updateAt: false,
},
}
);
model
- 创建并连接表
- 三个参数:
- 表名,默认会转小写,且追加+s
- Schema表结构
- 自定义表名,可选参数,强制覆盖第一个参数
- 返回一个表实例,可以对当前连接表进行增持删改查
const User = mongoose.model("haha", UserSchema);
const User = mongoose.model("Haha", UserSchema);
const User = mongoose.model("Haha", UserSchema, "haha");
create
- 创建某个或多个数据
- 支持promise
- 创建成功,then第一个参为入库具体参数
- 失败,catch为异常
(async () => {
let r = await User.create({
name: "cc",
pwd: "sss",
age: 120,
a: 1,
gender: 1,
address: { num: 10, value: "地址" },
});
console.log(r);
})();
(async () => {
let arr = [];
for (let i = 0; i < 10; i++) {
arr.push({
name: "cc" + i,
pwd: "sss",
age: i,
gender: 1,
address: { num: 10, value: "地址" },
});
}
let r = await User.create(arr);
console.log(r);
})();
deleteMany
User.deleteMany({});
find
- 查找,用法与mongo原生语句一致
- 实例上有一个countDocuments方法,可以拿到本次获取数据总数
(async () => {
let r = await User.find();
console.log(r);
let total = await User.find().countDocuments();
console.log(total);
})();
分页
- find的高级应用
- limit设置每页多少条
- skip设置跳过多少条,内部会自动先执行skip
(async () => {
const limit = 3;
const curPage = 2;
let r = await User.find()
.limit(limit)
.skip((curPage - 1) * limit);
console.log(r);
})();
sort
- 排序
- -1为倒序,从大到小
- 1为正序,从小到大
- 如果与skip和limit一同执行,会先执行sort,然后skip,最后limit
let r = await User.find().sort({ age: -1 });
console.log(r);
findById
User.findById("62f121b29ed01ff4f2865057");
创建article
const mongoose = require("mongoose");
const ArticleSchema = mongoose.Schema(
{
title: String,
content: String,
user_id: {
type: mongoose.SchemaTypes.ObjectId,
},
},
{
timestamps: true,
}
);
module.exports = mongoose.model("Article", ArticleSchema, "article");
- 通过new来创建数据,但new一次性只能放一条数据,跟create一样
- 然后调用save方法保存到表中
(async () => {
let user = await User.findById("62f121b29ed01ff4f2865057");
let article = await new Article({
title: "node",
content: "内容2",
user_id: user._id,
}).save();
console.log(article);
})();
联表查询
(async () => {
let article = await Article.findById("62f251b287e45b3c3850931a");
let user = await User.findById(article.user_id);
console.log(user);
})();
populate
- 第一个参为根据某个值查询
- 第二个参等同于find第二个参,作用为筛选出某个字段
- 默认会把查询到的参数塞到第一个参数中返回,默认返回全部
- 步骤
- 在创建Schema的时候加上
ref,对应到User上
- 那么查询的时候,调用populate填写对应字段会自动关联
const ArticleSchema = mongoose.Schema(
{
...
user_id: {
ref: "User",
type: mongoose.SchemaTypes.ObjectId,
},
},
...
);
(async () => {
let article = await Article.findById("62f251b287e45b3c3850931a").populate(
"user_id"
);
console.log(article);
})();
aggregate
- 聚合查询,推荐
- 不用再加ref了
- 还是上述例子
- 多种功能集成到一个方法里
- 查询多个表的话,加多个$lookup就可以了
let article = await Article.aggregate([
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user",
},
},
{
$match: {
_id: mongoose.Types.ObjectId("62f251b287e45b3c3850931a"),
},
},
]);
console.log(article);
示例
- 给article的类型声明添加一个cate_id,让其可以关联类型表
- 创建cate类型表
const ArticleSchema = mongoose.Schema(
{
...
cate_id: {
type: mongoose.SchemaTypes.ObjectId,
}
}
...
);
const mongoose = require("mongoose");
const CateSchema = mongoose.Schema(
{
title: String,
},
{
timestamps: true,
}
);
module.exports = mongoose.model("Cate", CateSchema, "cate");
const Cate = require("./module/cate");
(async () => {
await Cate.create([{ title: "a" }, { title: "b" }]);
})();
(async () => {
let r = await Article.findByIdAndUpdate("62f251b287e45b3c3850931a", {
cate_id: "62f25d0ce1c899f1808a22aa",
});
console.log(r);
})();
(async () => {
let article = await Article.aggregate([
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user",
},
},
{
$lookup: {
from: "cate",
localField: "cate_id",
foreignField: "_id",
as: "cate",
},
},
{
$match: {
_id: mongoose.Types.ObjectId("62f251b287e45b3c3850931a"),
},
},
{
$project: {
user: {
name: 1,
},
cate: {
title: 1,
},
},
},
]);
console.log(article[0].user);
})();
(async () => {
let article = await User.aggregate([
{
$match: {},
},
{
$group: {
_id: "$name",
age: { $sum: "$age" },
},
},
]);
})();
{
$limit: 3,
},
{
$skip: 5,
},