Nodejs 操作 MongoDB
-
使用官方的
MongoDB
包:https://github.com/mongodb/node-mongodb-native
-
使用第三方的
mongoose
来操作MongoDB
数据库mongoose
是基于MongoDB
官方的mongodb
包再一次做了封装-
mongoose 官网:https://mongoosejs.com/
安装命令npm i mongoose
-
连接 MongoDB 数据库
mongoose.connect(uri[, option][, callback]):该方法用于与 MongoDB 数据库建立连接
mongoose
导入的 mongoose 模块
参数
rui
连接 MongoDB 数据库的地址options
该参数是一个对象,用于设置一些选项,这些选项会被传到底层 MongoDB 驱动callback
创建集合
mongoose 的一切都始于 Schema, 通过 mongoose 导出的 Schema 构造函数可以创建集合,设定集合的结构(规则);mongoose 中的集合就相当于关系型数据库中的表结构,可以设置集合中字段的类型,默认值、约束等。
Schema(definition[,options]):构造函数,通过该函数可以设计集合的结构,然后通过 new 创建实例对象;使用该构造函数时要通过 mongoose.Schema 获取,比如:new mongoose.Schema()
参数:
definition
该参数是一个对象,通过该参数定义集合中的字段并对字段设置相关约束options
应用规则
mongoose.model(name[,schema]):将文档结构发布为模型(应用规则);返回值为模型构造函数。
参数
name
字符串,首字母大写的名词的单数形式,用来表示文档(模型)的名称; mongoose 会自动将该单词生成小写复数的集合名称schema
该参数为设计的集合结构 (规则)
创建文档1
所谓的创建文档就是向集合中插入数据,该过程分为两步:创建集合实例,然后调用实例对象下的 save 方法将数据保存到数据库中
创建集合实例:
获取 model() 方法的返回的构造函数,该构造函数接收一个对象为参数,通过这个对象传入具体的数据,然后通过 new 关键字来创建实例对象
保存数据:
Model.prototype.save([options][,options.safe][,options.validateBeforeSave][,fn] ):该方法为 Model 原型上的方法,用于保存数据;有多个参数常用的为 fn
参数
fn
在数据保存完成后执行的回调函数,该函数有两个参数:错误对象 err,没有错误时值为 null;另一个参数为执行结果 doc(就是保存的数据)
创建文档2
Model.create(doc[,callback]) 将一个或多个文档保存到数据库的快捷方式;该方法的返回值为 promise 对象。
Model
调用 model() 方法后的返回值
参数
doc
需要插入集合中的文档,该参数可以是对象或数组callback
数据插入完成后执行的回调函数;该函数有两个参数:错误参数对象 err 和保存的数据 doc
因为该方法的返回值为 promise 对象,所以可以省略回调函数,直接使用链式编程的方式。
下面举个例子:
// 引包
const mongoose = require('mongoose')
// 连接 MongoDB 数据库,数据库不存在时会自动创建
mongoose
.connect('mongodb://localhost/userInfo', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('数据库连接成功'))
.catch(err => console.log('数据库连接失败', err))
// 设计集合结构(类似于表结构)
const userSchema = new mongoose.Schema({
username: {
type: String, // 设置字段类型
required: true // 约束不能为空
},
age: Number,
gender: Boolean,
hobby: Array
})
// 应用集合
let User = mongoose.model('User', userSchema)
/* 创建文档1 */
// 拿到模型构造函数,开始为所欲为(插入数据)
let admin = new User({
username: 'admin',
age: 19,
gender: false,
hobby: ['book', 'music', 'shopping']
})
// 数据持久化(保存数据)
admin.save((err, res) => {
if (err) console.log('保存失败!', err)
else console.log('保存成功!', res)
})
/* 创建文档2 */
// 通过该方法不需要去实例化集合
User.create(
{
username: 'VIP',
age: 18,
gender: true,
hobby: ['running', 'basketball', 'anime']
},
(err, doc) => {
if (err) console.log('保存失败!', err)
else console.log('保存成功', doc)
}
)
/* 创建文档2 可写成如下形式*/
// User.create({
// username: 'VIP',
// age: 18,
// gender: true,
// hobby: ['running', 'basketball', 'anime']
// })
// .then(doc => console.log('保存成功', doc))
// .catch(err => console.log('保存失败!', err))
查询数据
Model.find(conditions[,callback]):用于查询集合中的文档(数据)
参数
conditions
查询条件,其值为一个对象,条件为多个时用逗号隔开;如果省略不写将获取集合中的所有文档callback
查询完成后执行的回调函数,有两个参数err
错误信息,成功时值为 nulldocs
一个包含文档的数组,未查询到时为一个空数组
Model.findOne(conditions[,callback]):用于查询集合中符合条件的第一条文档(数据);参数同上,不同的是 callback 的 doc 为查询到的一条文档,未查询到时值为 null。
举个栗子:
// 引包
const mongoose = require('mongoose')
// 连接 MongoDB 数据库,数据库不存在时会自动创建
mongoose
.connect('mongodb://localhost/userInfo', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('数据库连接成功'))
.catch(err => console.log('数据库连接失败', err))
// 设计集合结构(类似于表结构)
const userSchema = new mongoose.Schema({
username: {
type: String, // 设置字段类型
required: true // 约束不能为空
},
age: Number,
gender: Boolean,
hobby: Array
})
// 应用集合
let User = mongoose.model('User', userSchema)
// 通过指定条件查询文档
User.find({ _id: '5dfc35f15057e028f49f4d39' }, (err, docs) => {
if (err) console.log(err)
else console.log(docs)
})
常见查询条件
$lt:小于;$gt:大于
// 获取 User 集合中 age 大于 16 小于 23 的文档
User.find({ age: {$gt: 16, $lt: 23} }).then(docs => console.log(docs))
$in:包含
// 获取 User 集合中 hobby 包含 'music' 的文档
User.find({ hobby: {$in: ['music']} }).then(docs => console.log(docs))
select(arg ):指定查询到的文档包含或排除哪些字段进行显示;需要注意的是包含和排除不能同时使用。
// 排除 username 和 age 字段,不进行显示
User.find()
.select('-username -age')
.then(docs => console.log(docs))
arg
该参数可以是一个字符串或对象;为字符串时多个字段之间用空格隔开,通过在字段前加-
前缀可以进行排除
sort(arg):对查询到的文档进行排序
arg
一个字段,默认升序排列,降序排列时在字段前加一个-
前缀即可
// 按照 age 升序排列
User.find()
.sort('age')
.then(docs => console.log(docs))
skip(val):跳过指定条数的文档
val
数量
// 跳过 2 条数据
User.find()
.skip(2)
.then(docs => console.log(docs))
limit(val):限制显示文档的最大条数
// 显示 10 条数据
User.find()
.limit(10)
.then(docs => console.log(docs))
删除数据
findOneAndDelete([conditions][,callback]):删除第一条匹配的文档;findOneAndRemove() 的功能与此方法相同
参数:
conditions
该参数是一个对象用于指定查询条件callback
删除完成后执行的回调函数;有两个参数,错误信息 err 和 被删除的文档 doc
// 删除第一条 username 为 'VIP' 的文档
User.findOneAndDelete({ username: 'VIP' }, (err, doc) => {
if (err) console.log(err)
else console.log(doc)
})
deleteMany([filter][,callback]):删除符合条件的文档(多条);参数同上,不同的是回调函数的第二个参数为删除结果。
// 删除所有匹配的文档
User.deleteMany({ username: 'VIP' }, (err, docs) => {
if (err) console.log(err)
else console.log(docs)
})
更新数据
updateOne([criteria][,doc][,callback]):更新匹配到的第一条文档;fandOneAndUpdate() 的功能与此方法相同
参数:
criteria
查询条件doc
需要更新的文档字段callback
更新完成后执行的回调函数;有两个参数:err
错误信息writeOpResult
执行成功后的结果
// 更新匹配到的第一条文档的数据
User.updateOne({ username: 'www' }, { age: 666 }, (err, res) => {
if (err) console.log(err)
else console.log(res)
})
updateMany():更新匹配到的所有文档;参数同上。
// 更新匹配的所有文档
User.updateMany({ username: 'www' }, { age: 123 }, (err, res) => {
if (err) console.log(err)
else console.log(res)
})
mongoose 验证
required
设置是否为必传字段,其值为布尔值 true 表示必传;当值为数组时可追加错误时的提示信息
required: [ true, '该字段为必填字段']
unique
设置字段具有唯一性;其值为布尔值 true 表示不可重复
unique: true
minlength
设置当前字段的最小长度
minlength: [6, '该字段最小长度为 6']
maxlength
设置当前字段的最大长度
min
设置最小数值
max
设置最大数值
enum
枚举,设置当前字段可拥有的值值为数组
gender: {
type: String,
enum: ['男', '女', '妖精', '保密']
}
trim
是否祛除字符串首尾的空格,为 true 时表示祛除
validate
自定义验证器;其值为一个对象,该对象的 validator 属性的属性值为一个函数,通过该函数可以自定义对字段的验证(函数的返回值为布尔值,为 true 时表示验证成功,否则验证失败)
intro: {
type: String,
// 自定义验证
validate: {
// val 为要验证的值
validator: val => {
return val && val >= 5
},
// 自定义验证信息
message: '最少5个字符'
}
}
default
设置默认值
举个例子:
// 引包
const mongoose = require('mongoose')
// 连接 MongoDB 数据库,数据库不存在时会自动创建
mongoose
.connect('mongodb://localhost/userInfo', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('数据库连接成功'))
.catch(err => console.log('数据库连接失败', err))
// 设计集合结构(类似于表结构)
const userSchema = new mongoose.Schema({
username: {
// 设置字段类型
type: String,
// 约束不能为空
required: [true, '用户名不能为空'],
// 最小长度
minlength: [6, '用户名最小长度为 6'],
// 最大长度
maxlenght: [12, '用户名最大长度为 12'],
// 祛除首尾空格
trim: true
},
age: {
// 类型为 数字
type: Number,
// 最小值
min: 16,
// 最大值
max: 23
},
regDate: {
// 日期类型
type: Date,
// 默认值;Date.now 当前时间
default: Date.now
},
gender: {
type: String,
// 枚举
enum: ['男', '女', '妖精', '保密']
},
intro: {
type: String,
// 自定义验证
validate: {
// val 为要验证的值
validator: val => {
return val && val.length >= 5
},
// 自定义验证信息
message: '最少5个字符'
}
}
})
// 应用集合
let User = mongoose.model('userInfo', userSchema)
User.create(
{
username: 'VIP',
age: 20,
gender: '',
intro: '超级VIP会员!'
},
(err, doc) => {
if (err) {
// 获取错误对象
let error = err.errors
// 打印 message 信息
for (let attr in error) {
console.log(error[attr].message)
}
} else console.log('保存成功', doc)
}
)
关联集合
集合之间默认是没有关联的,通常一些不同集合之间的数据之间有着某种关系,比如文章信息和文章作者的信息存储在不同集合中,但是文章是某个用户发表的,要查询文章的作者信息,就需要用到关联集合。
集合之间一般是使用 id 进行关联关联,然后通过 populate() 方法进行关联集合查询。
示例:
// 引入 mongoose 操作数据库
const mongoose = require('mongoose')
// 连接数据库
mongoose
.connect('mongodb://localhost/userInfo', {
useNewUrlParser: true,
useUnifiedTopology: true
})
// 连接成功
.then(() => console.log('数据库连接成功'))
// 连接失败
.catch(err => console.log(err, '数据库连接失败'))
// 设计用户集合结构
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
})
// 设计文章集合结构
const postSchema = new mongoose.Schema({
title: {
type: String
},
author: {
// 通过 id 与用户集合进行关联
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
})
// 应用用户集合
const User = mongoose.model('User', userSchema)
// 应用文章集合
const Post = mongoose.model('Post', postSchema)
// 创建用户文档
User.create({ name: 'boy' }).then(result => console.log(result))
// 创建文章文档
Post.create({ titile: 'www', author: '5dfcb365bf9f50355c0e299a' }).then(
result => console.log(result)
)
// 关联查询
Post.find()
.populate('author')
.then(result => console.log(result))