前端必学的mongoose 入门

1,178 阅读7分钟

mongoose 介绍

Mongoose 是在 node.js 异步环境下对 mongodb 进行便捷操作的对象模型工具。Mongoose

NodeJS 的驱动,不能作为其他语言的驱动。

Mongoose 有两个特点

  1. 通过关系型数据库的思想来设计非关系型数据库
  2. 基于 mongodb 驱动,简化操作

mongoose 的安装以及使用

官网

  1. 安装

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 预定义模式修饰符

lowercaseuppercasetrim

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 对比:

SQLNO 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})

截屏2022-08-29 21.14.37.png

截屏2022-08-29 21.15.22.png

$project

修改文档的结构,可以用来重命名、增加或删除文档中的字段。要求查找 order 只返回文档中 trade_noall_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)
})