文章目录
简介
数据库概念
|术语 | 解释说明|
|database|数据库|
|coleection|集合,一组数据集合 可以理解为数组|
|document|文档,一条具体数据 可以理解为对象|
|field|字段,文档中属性 可以理解为对象属性|
类似于图的邻接表 数组中每一项是对象
起步
数据库所有操作都是异步
api支持两种异步操作:1.回调函数 2.Promise
nodejs第三方包
nodejs操作MongoDB数据库需要mongoose第三方模块
启动MongoDB:
先启动了才能使用
命令行中 管理员 net start mongodb
(停止 stop)
数据库连接
mongoose提供的connect方法可以连接数据库
传递地址和名字 playground是数据库名字
mongoose.connect返回的Promise对象
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground')
.then(()=>console.log('数据库连接成功'))
.catch((err)=>console.log(err, '数据库连接失败'))
会提示connect有第二个参数选项
{useNewUrlParser: true,useUnifiedTopology: true}
加到connect的第二个参数即可
mongoose.connect('mongodb://localhost/playground', {
useNewUrlParser: true,
useUnifiedTopology: true
})
就不会报错了
创建数据库
MongoDB中不需要显式创建数据库,如果正在使用的数据库不存在,插入数据时 MongoDB会自动创建
增删改查
增
创建集合
创建集合分为两步:
- 对集合设定规则
- 创建集合
创建 Schema构造函数 的实例 即可创建集合
mongoose.Schema({})
(构造函数)创建集合规则,new再创建实例
mongoose.model(集合名称, 集合规则的实例)
返回构造函数
//设定集合规则 值是数据类型
const courseSchema = new mongoose.Schema({
name: String,
author: String,
isPublished: Boolean
})
// 创建集合并应用规则
const Course = mongoose.model('Course', courseSchema); // 第一个名字首字母规定大写 但存在数据库中的名称是 courses (小写再加s)
//model 的返回值(Course)是该集合的构造函数
创建文档(向集合插入文档)
两种方法:
1.创建集合实例
创建文档实际上就是向集合中插入数据
两步:
- 创建集合实例
- 调用实例对象下的save方法将数据保存到数据库中
//创建文档(集合实例)
const course1 = new Course({
name: ' Database',
author: 'bro',
isPublished: true
});
//将数据保存到数据库中
course1.save();
保存后在Compass软件中可以看到刚创建的playground数据库和courses集合
_id ObjectId 是数据库自动生成的字段 是该文档的唯一标识
2.集合构造函数 create方法
Course.create(数据对象, 回调函数)
插入后执行回调函数
Course.create({name:'Database'author: 'bro',isPublished: true}, (err, doc)=>{
//错误对象
console.log(err) // 插入成功该项为空 失败了是对象有错误信息
//当前插入的文档
console.log(doc)
})
Promise改写:
Course.create({name:'Database'author: 'bro',isPublished: true})
.then(doc => console.log(doc))
.catch(err => console.log(err))
mongoDB导入数据
数据库文件目录需要在环境变量里
命令行工具:
mongoimport -d 数据库名称 -c 集合名称 -file 要导入的数据文件
查
find方法返回Promise对象
同时结果都是数组
// 根据条件查找文档(条件为空则查找所有文档) 集合Course下的方法
Course.find().then(result => console.log(result))
//result是个数组 包含多个对象 (一个个文档)
[
{
hobbies: [ '吃饭' ],
_id: 5c09f2b6aeb04b22f846096a,
name: '王二麻子',
age: 32,
email: 'wangermazi@itcast.cn',
password: '123456'
},
{
hobbies: [ '足球', '篮球' ],
_id: 5c09f236aeb04b22f8460967,
name: '李四',
age: 10,
email: 'lisi@itcast.cn',
password: '654321'
}
]
find: 结果是数组(一组)
findOne: 结果是对象(一条)
可以传入一个对象作查找条件
根据条件查找:
//查找所有文档
User.find().then(console.log)
// 通过_id字段查找文档
User.find({_id: '5c09f267aeb04b22f8460968'}).then(console.log)
//通过find查找返回的都是数组(可能一项)
//返回第一条文档 默认返回第一条
User.findOne({name: '李四'}).then(console.log)
// 查询用户集合中年龄字段大于20并且小于40的文档 $gt $lt 大于小于都是开区间
User.find({age: {$gt: 20, $lt: 40}}).then(result => console.log(result))
// 查询用户集合中hobbies字段值包含足球的文档 $in 网站搜索
User.find({hobbies: {$in: ['足球']}}).then(result => console.log(result))
// 选择要查询的字段 返回的结果只有name email 不想查询的内容前面加上-(会默认返回_id 加-就没了)
User.find().select('name email -_id').then(result => console.log(result))
// 根据年龄字段进行升序排列 从小到大
User.find().sort('age').then(result => console.log(result))
// 根据年龄字段进行降序排列 加上-
User.find().sort('-age').then(result => console.log(result))
// 查询文档跳过前两条结果 限制显示3条结果 skip跳过多少条数据 limit查询数量 可以用来做分页
User.find().skip(2).limit(3).then(result => console.log(result))
删
// 查找到一条文档并且删除
// 返回删除的文档
// 如何查询条件匹配了多个文档 那么将会删除第一个匹配的文档
User.findOneAndDelete({_id: '5c09f267aeb04b22f8460968'}).then(result => console.log(result))
// 删除多条文档 此时传空会全部删除
// 返回一个对象{n:4,ok:1} 删除四个成功
User.deleteMany({}).then(result => console.log(result))
参数都是查询条件
改
// 更新集合中的文档(一个 匹配多个默认第一个)
User.updateOne({name: '李四'}, {age: 120, name: '李狗蛋'}).then(result => console.log(result))
// 更新集合文档(多个)
User.updateMany({}, {age: 300}).then(result => console.log(result))
//这个操作是把所有数据的age都修改了
如果文档中没有该字段会自动添加
mongoose验证
创建集合规则时,可以设置字段的验证规则,验证失败就插入失败
- required: true 必传字段
(或required: [true,‘请输入文章标题’]) 数组第二个可以自定义错误信息 - minlength: (字符串最小长度)
- maxlength: (字符串最大长度)
- min: (数值最大)
- max: (数值最小)
- trim: true (去除字符串两边空格)
- default: (默认值)
- enum: [‘a’,‘b’,‘c’] (规定数据的值,传入的值必须是abc中的一个,不是的话会报错) enum: { values: [‘字段’], message: ’ 错误提示 ’ }(自定义错误信息和其他不同)
都可以自定义错误信息 格式一样
minlength: [2,'文章长度不能小于2']
数组第二个值
const testSchema = new mongoose.Schema({
title:{
type: String,
required: true,
// 如果创建的文档没有title会报错(必选字段)
minlength: [2,'文章长度不能小于2'] ,
//字符串的最小长度
maxlength: 5 ,
//字符串的最大长度
trime: true
//去除字符串两边的空格
}
})
const Test = mongoose.model('Test', testSchema);
Test.create({}).then(console.log);
//报错 必须得有title
自定义验证:
- validate: 它是一个对象,里面有 validator(validator是 一个函数类型)属性和 message(自定义错误提示信息)属性
const testSchema = new mongoose.Schema({
name: {
type: String,
validate: {
validator: v => {
// 返回布尔值
// true验证成功
// false 验证失败
// v是要验证的值(输入的)
return v && v.length > 4
},
//自定义错误信息
message: '传入信息不符合验证规则'
}
}
})
处理错误信息
const testSchema = new mongoose.Schema({
title: {
type: String,
required: true,
minlength: [2, '文章长度不能小于2'],
},
age: {
type: Number,
min: [18, '长度不能小于18'],
max: 100
}
})
const Test = mongoose.model('Test', testSchema);
Test.create({
title: 'datacase',
age: 10
})
.then(res => console.log(res))
.catch(err => console.log(err.errors.age.properties.message));
//tittle和age都不符合
/*Error: Test validation failed: age: 长度不能小于18
at ValidationError.inspect (C:\Users\Desktop\node\mongodb数据库\node_modules\mongoose\lib\error\validation.js:47:26)
at internal/per_context/primordials.js:23:32
at formatValue (internal/util/inspect.js:763:19)
at inspect (internal/util/inspect.js:325:10)
at formatWithOptionsInternal (internal/util/inspect.js:1991:40)
at formatWithOptions (internal/util/inspect.js:1875:10)
at Object.value (internal/console/constructor.js:306:14)
at Object.log (internal/console/constructor.js:341:61)
at C:\Users\Desktop\node\mongodb数据库\03.js:34:30
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
errors: {
age:
ValidatorError: 长度不能小于18
at validate (C:\js:1235:13)
at C:\schematype.js:1218:7
at Array.forEach (<anonymous>)
// 各种目录
properties: {
validator: [Function (anonymous)],
message: '长度不能小于18',
type: 'min',
min: 18,
path: 'age',
value: 10
},
kind: 'min',
path: 'age',
value: 10,
reason: undefined,
[Symbol(mongoose:validatorError)]: true
}
},
_message: 'Test validation failed'
}*/
所有错误信息在 errors 中
errors的每一项是某字段的错误
某字段对象的message属性是错误信息
message获取:
err的errors对象中的具体字段对象中的message
Test.create({
title: 'datacase',
age: 10
})
.then(res => console.log(res))
.catch(err => {
let errs = err.errors;
//循环错误信息对象
for(let i in errs){
console.log(errs[i]['message'])
}
});
//拿到了具体的报错信息
集合关联
不同集合的数据之间是有关系的,一个字段没必要完全拷贝另外一个集合,所以用id进行关联,类似链表
_id的数据类型:mongoose.Schema.Types.ObjectI
- 使用id对集合进行关联
- 使用populate方法进行关联集合查询
//需求:在文章集合中存储发布文章的作者
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
});
const postSchema = new mongoose.Schema({
title: {
type: String
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
// 用户集合
const User = mongoose.model('User', userSchema);
// 文章集合
const Post = mongoose.model('Post', postSchema);
// 创建用户
// User.create({
// name: 'dingdinag'
// }).then(result => console.log(result));
// 创建文章
// Post.create({
// title: 'mongo',
// author: '6029f94a835edbdd50e298a1'
// }).then(result => console.log(result))
// .catch(console.log);
// author的具体id在软件中查
//author字段需要分析id
Post.find().populate('author').then(result => console.log(result));
结果:
[
{
_id: 602a811add46b30dfd55d01f,
title: 'mongo',
author: { _id: 6029f94a835edbdd50e298a1, name: 'dingding', __v: 0 },
__v: 0
}
]
---
Post.find().then(console.log)的结果:
[
{
_id: 602a811add46b30dfd55d01f,
title: 'mongo',
author: 6029f94a835edbdd50e298a1,
__v: 0
}
]
用populate方法后查询结果会解析id 显示成另外一个集合文档