mongoose 介绍
Mongoose 是在 node.js 异步环境下对 mongodb 进行便捷操作的对象模型工具。Mongoose
是 NodeJS 的驱动,不能作为其他语言的驱动。
Mongoose 有两个特点
- 通过关系型数据库的思想来设计非关系型数据库
- 基于 mongodb 驱动,简化操作
mongoose 的安装以及使用
- 安装
npm i mongoose --save
2、引入 mongoose 并连接数据库
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
如果有账户密码需要采用下面的连接方式:
mongoose.connect('mongodb://eggadmin:123456@localhost:27017/eggcms');
3、定义 Schema
数据库中的 Schema,为数据库对象的集合。schema 是 mongoose 里会用到的一种数据模式, 可以理解为表结构的定义;每个 schema 会映射到 mongodb 中的一个 collection,它不具备操作数据库的能力
var UserSchema=mongoose.Schema({
name: String,
age:Number,
status:'number'
})
4、创建数据模型
定义好了 Schema,接下就是生成 Model。model 是由 schema 生成的模型,可以对数据库的
操作。
注意:
mongoose.model里面可以传入两个参数也可以传入三个参数 mongoose.model(参数 1:模型名称(首字母大写),参数 2:Schema)mongoose.model(参数 1:模型名称(首字母大写),参数 2:Schema,参数 3:数据库集合名称)
如果传入 2 个参数的话:这个模型会和模型名称相同的复数的数据库建立连接:如通过下面方法创建模型,那么这个模型将会操作 users 这个集合。
如果传入 3 个参数的话:模型默认操作第三个参数定义的集合名称
var User=mongoose.model('User', UserSchema);
查找数据
User.find({}, function (err, docs) {
if (err) {
console.log(err)
return
}
console.log(docs)
})
增加数据
var u = new User({
//实例化模型 传入增加的数据
name: 'lisi2222333',
age: 20,
status: true,
})
u.save()
修改数据
User.updateOne({ name: 'lisi2222' }, { name: '哈哈哈' }, function (err, res) {
if (err) {
console.log(err)
return
}
console.log('成功')
})
删除数据
User.deleteOne({ _id: '5b72ada84e284f0acc8d318a' }, function (err) {
if (err) {
console.log(err)
return
}
// deleted at most one tank document
console.log('成功')
})
保存成功查找
var u = new User({
name: 'lisi2222333',
age: 20,
status: true, //类型转换
})
u.save(function (err, docs) {
if (err) {
console.log(err)
return
}
//console.log(docs);
User.find({}, function (err, docs) {
if (err) {
console.log(err)
return
}
console.log(docs)
})
})
mongoose 预定义模式修饰符
lowercase、uppercase 、trim
mongoose 提供的预定义模式修饰符,可以对我们增加的数据进行一些格式化。
var UserSchema = mongoose.Schema({
name: { type: String, trim: true },
age: Number,
status: { type: Number, default: 1 },
})
Mongoose Getters 与 Setters 自定义修饰符
除了 mongoose 内置的修饰符以外,我们还可以通过 set(建议使用) 修饰符在增加数据的时候对数据进行格式化。也可以通过 get(不建议使用)在实例获取数据的时候对数据进行格式化。
var NewsSchema = mongoose.Schema({
title: 'string',
author: String,
pic: String,
redirect: {
type: String,
set(url) {
if (!url) return url
if (url.indexOf('http://') != 0 && url.indexOf('https://') != 0) {
url = 'http://' + url
}
return url
},
},
content: String,
status: { type: Number, default: 1 },
})
var NewsSchema = mongoose.Schema({
title: 'string',
author: String,
pic: String,
redirect: {
type: String,
set(url) {
if (!url) return url
if (url.indexOf('http://') != 0 && url.indexOf('https://') != 0) {
url = 'http://' + url
}
return url
},
get: function (url) {
if (!url) return url
if (url.indexOf('http://') != 0 && url.indexOf('https://') != 0) {
url = 'http://' + url
}
return url
},
},
content: String,
status: { type: Number, default: 1 },
})
Mongoose 索引
索引是对数据库表中一列或多列的值进行排序的一种结构,可以让我们查询数据库变得更快。MongoDB 的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的查询优化技巧。
mongoose 中除了以前创建索引的方式,我们也可以在定义 Schema 的时候指定创建索引。
var DeviceSchema = new mongoose.Schema({
sn: {
type: Number,
// 唯一索引
unique: true,
},
name: {
type: String,
// 普通索引
index: true,
},
})
Mongoose 内置 CURD
Model.deleteMany()
Model.deleteOne()
Model.find()
Model.findById()
Model.findByIdAndDelete()
Model.findByIdAndRemove()
Model.findByIdAndUpdate()
Model.findOne()
Model.findOneAndDelete()
Model.findOneAndRemove()
Model.findOneAndUpdate()
Model.replaceOne()
Model.updateMany()
Model.updateOne()
扩展 Mongoose CURD 方法
var mongoose = require('./db.js')
var UserSchema = mongoose.Schema({
name: {
type: String,
},
age: Number,
status: {
type: Number,
default: 1,
},
})
// 静态方法
UserSchema.statics.findByUid = function (uid, cb) {
this.find({ _id: uid }, function (err, docs) {
cb(err, docs)
})
}
// 实例方法
UserSchema.methods.print = function () {
console.log('这是一个实例方法')
console.log(this)
}
module.exports = mongoose.model('User', UserSchema, 'user')
Mongoose 校验参数
| 参数 | |
|---|---|
| required | 表示这个数据必须传入 |
| max | 用于 Number 类型数据,最大值 |
| min | 用于 Number 类型数据,最小值 |
| enum | 枚举类型,要求数据必须满足枚举值 enum: ['0', '1', '2'] |
| match | 增加的数据必须符合 match(正则)的规则 |
| maxlength | 最大值 |
| minlength | 最小值 |
var UserSchema = new mongoose.Schema({
name: { type: String, required: true },
age: {
type: Number, // 是否必须的校验器
required: true,
// 数字类型的最大值校验器
max: 120,
// 数字类型的最小值校验器
min: 0,
},
status: {
type: String,
// 设置字符串的可选值
enum: ['0', '1', '2'],
},
phone: { type: Number, match: /^\d{11}$/ },
desc: { type: String, maxlength: 20, minlength: 10 },
})
Mongoose 自定义的验证器
在缺省情况下创建的索引均不是唯一索引。下面的示例将创建唯一索引,如:
var UserSchema = new mongoose.Schema({
name: { type: String, required: true },
age: {
type: Number, // 是否必须的校验器 required: true, // 数字类型的最大值校验器
max: 120, // 数字类型的最小值校验器
min: 0,
},
status: {
type: String, // 设置字符串的可选值
enum: ['0', '1', '2'],
},
phone: { type: Number, match: /^\d{11}$/ },
desc: {
type: String, // 自定义的验证器,如果通过验证返回 true,没有通过则返回 false
validate: function (desc) {
return desc.length >= 10
},
},
})
MongoDB 聚合管道(Aggregation Pipeline)
使用聚合管道可以对集合中的文档进行变换和组合。
实际项目:表关联查询、数据的统计。
MongoDB 中使用 db.COLLECTION_NAME.aggregate([{<stage>},...]) 方法
来构建和使用聚合管道。先看下官网给的实例,感受一下聚合管道的用法。
MongoDB Aggregation 管道操作符与表达式
| 管道操作符 | Description |
|---|---|
| $project | 增加、删除、重命名字段 |
| $match | 条件匹配。只满足条件的文档才能进入下一阶段 |
| $limit | 限制结果的数量 |
| $skip | 跳过文档的数量 |
| $sort | 条件排序。 |
| $group | 条件组合结果 统计 |
| $lookup | 操作符 用以引入其它集合的数 |
据 (表关联查询)SQL 和 NOSQL 对比:
| SQL | NO SQL |
|---|---|
| WHERE | $match |
| GROUP BY | $group |
| HAVING | $match |
| SELECT | $project |
| ORDER BY | $sort |
| LIMIT | $limit |
| SUM() | $sum |
| COUNT() | $sum |
| join | $lookup |
管道表达式:
管道操作符作为“键”,所对应的“值”叫做管道表达式。
例如{$match:{status:"A"}},$match 称为管道操作符,而 status:"A"称为管道表达式,
是管道操作符的操作数(Operand)。
每个管道表达式是一个文档结构,它是由字段名、字段值、和一些表达式操作符组成的。
| 常用表达式操作符 | Description |
|---|---|
| $addToSet | 将文档指定字段的值去重 |
| $max | 文档指定字段的最大值 |
| $min | 文档指定字段的最小值 |
| $sum | 文档指定字段求和 |
| $avg | 文档指定字段求平均 |
| $gt | 大于给定值 |
| $lt | 小于给定值 |
| $eq | 等于给定值 |
数据模拟
db.order.insert({"order_id":"1","uid":10,"trade_no":"111","all_price":100,"all_num":2})
db.order.insert({"order_id":"2","uid":7,"trade_no":"222","all_price":90,"all_num":2})
db.order.insert({"order_id":"3","uid":9,"trade_no":"333","all_price":20,"all_num":6})
db.order_item.insert({"order_id":"1","title":"商品鼠标 1","price":50,num:1})
db.order_item.insert({"order_id":"1","title":"商品键盘 2","price":50,num:1})
db.order_item.insert({"order_id":"1","title":"商品键盘 3","price":0,num:1})
db.order_item.insert({"order_id":"2","title":"牛奶","price":50,num:1})
db.order_item.insert({"order_id":"2","title":"酸奶","price":40,num:1})
db.order_item.insert({"order_id":"3","title":"矿泉水","price":2,num:5})
db.order_item.insert({"order_id":"3","title":"毛巾","price":10,num:1})
$project
修改文档的结构,可以用来重命名、增加或删除文档中的字段。要求查找 order 只返回文档中 trade_no 和 all_price 字段
db.order.aggregate([
{
$project: { trade_no: 1, all_price: 1 },
},
])
$match
作用
用于过滤文档。用法类似于 find() 方法中的参数。
db.order.aggregate([
{
$project: { trade_no: 1, all_price: 1 },
},
{
$match: { all_price: { $gte: 90 } },
},
])
$group
将集合中的文档进行分组,可用于统计结果。
统计每个订单的订单数量,按照订单号分组
db.order_item.aggregate([
{
$group: { _id: '$order_id', total: { $sum: '$num' } },
},
])
$sort
将集合中的文档进行排序。
db.order.aggregate([
{
$project: { trade_no: 1, all_price: 1 },
},
{
$match: { all_price: { $gte: 90 } },
},
{
$sort: { all_price: -1 },
},
])
$limit
db.order.aggregate([
{
$project: { trade_no: 1, all_price: 1 },
},
{
$match: { all_price: { $gte: 90 } },
},
{
$sort: { all_price: -1 },
},
{
$limit: 1,
},
])
$skip
db.order.aggregate([
{
$project: { trade_no: 1, all_price: 1 },
},
{
$match: { all_price: { $gte: 90 } },
},
{
$sort: { all_price: -1 },
},
{
$skip: 1,
},
])
$lookup 表关联
db.order.aggregate([
{
$lookup: {
from: 'order_item',
localField: 'order_id',
foreignField: 'order_id',
as: 'items',
},
},
])
{
"_id": ObjectId("5b743d8c2c327f8d1b360540"),
"order_id": "1",
"uid": 10,
"trade_no": "111",
"all_price": 100,
"all_num": 2,
"items": [
{
"_id": ObjectId("5b743d9c2c327f8d1b360543"),
"order_id": "1",
"title": "商品鼠标 1",
"price": 50,
"num": 1
},
{
"_id": ObjectId("5b743da12c327f8d1b360544"),
"order_id": "1",
"title": "商品键盘 2",
"price": 50,
"num": 1
},
{
"_id": ObjectId("5b74f457089f78dc8f0a4f3b"),
"order_id": "1",
"title": "商品键盘 3",
"price": 0,
"num": 1
}
]
} {
"_id": ObjectId("5b743d902c327f8d1b360541"),
"order_id": "2",
"uid": 7,
"trade_no": "222",
"all_price": 90,
"all_num": 2,
"items": [
{
"_id": ObjectId("5b743da52c327f8d1b360545"),
"order_id": "2",
"title": "牛奶",
"price": 50,
"num": 1
},
{
"_id": ObjectId("5b743da92c327f8d1b360546"),
"order_id": "2",
"title": "酸奶",
"price": 40,
"num": 1
}
]
} {
"_id": ObjectId("5b743d962c327f8d1b360542"),
"order_id": "3",
"uid": 9,
"trade_no": "333",
"all_price": 20,
"all_num": 6,
"items": [
{
"_id": ObjectId("5b743dad2c327f8d1b360547"),
"order_id": "3",
"title": "矿泉水",
"price": 2,
"num": 5
},
{
"_id": ObjectId("5b743dff2c327f8d1b360548"),
"order_id": "3",
"title": "毛巾",
"price": 10,
"num": 1
}
]
}
多管道查询
db.order.aggregate([
{
$lookup: {
from: 'order_item',
localField: 'order_id',
foreignField: 'order_id',
as: 'items',
},
},
{ $project: { trade_no: 1, all_price: 1, items: 1 } },
{ $match: { all_price: { $gte: 90 } } },
{ $sort: { all_price: -1 } },
])
{
"_id": ObjectId("5b743d8c2c327f8d1b360540"),
"trade_no": "111",
"all_price": 100,
"items": [{
"_id": ObjectId("5b743d9c2c327f8d1b360543"),
"order_id": "1",
"title": "商品鼠标 1",
"price": 50,
"num": 1
}, {
"_id": ObjectId("5b743da12c327f8d1b360544"),
"order_id": "1",
"title": "商品键盘 2",
"price": 50,
"num": 1
}, {
"_id": ObjectId("5b74f457089f78dc8f0a4f3b"),
"order_id": "1",
"title": "商 品键盘 3",
"price": 0,
"num": 1
}]
} {
"_id": ObjectId("5b743d902c327f8d1b360541"),
"trade_no": "222",
"all_price": 90,
"items": [{
"_id": ObjectId("5b743da52c327f8d1b360545"),
"order_id": "2",
"title": "牛奶",
"price": 50,
"num": 1
}, {
"_id": ObjectId("5b743da92c327f8d1b360546"),
"order_id": "2",
"title": "酸奶",
"price": 40,
"num": 1}]
}
Mongoose aggregate 多表关联查询
var mongoose = require('./db.js')
var OrderSchema = mongoose.Schema({
order_id: String,
uid: Number,
trade_no: String,
all_price: Number,
all_num: Number,
})
var OrderModel = mongoose.model('Order', OrderSchema, 'order')
OrderModel.aggregate(
[
{
$lookup: {
from: 'order_item',
localField: 'order_id',
foreignField: 'order_id',
as: 'item',
},
},
],
function (err, docs) {
console.log(docs)
}
)
Mongoose populate 关联查询
定义 ref
var ArticleSchema = new Schema({
title:{
type: String, unique: true},
cid : {
type: Schema.Types.ObjectId,
ref:'ArticleCate' //model 的名称
}, /*分类 id*/
author_id:{
type: Schema.Types.ObjectId,
ref:'User'
}, /*用户的 id*/
author_name:{
type:String
},
descripton:String,
content : String
});
关联查询
ArticleModel.find({}).populate('cid').populate('author_id').exec(function(err,docs){
console.log(docs)
})