1、MongoDB的介绍
MongoDB是一个文档数据库,存放的是BSON,使用非常方便:直接能把Json存进去,并且从数据库中查询出来就是JSON

数据库软件安装地址:https://www.mongodb.com/download-center/community
可视化软件安装地址:https://www.mongodb.com/download-center/compass

2、MongoDB可视化工具
数据库列表:

集合列表,这里相当于mysql的表

集合里的文档,相当于mysql的一条数据

3、MongoDB创建集合、文档
安装mongoose包:`npm install mongoose`
mongoose官方文档地址:https://mongoosejs.com/docs/connections.html
连接数据库,创建集合、文档
const mongoose = require('mongoose')
const schema = mongoose.Schema //Schema用于定义collection的结构
//连接数据库,但是数据库test不存在,会自动创建,但要写内容才会创建
mongoose.connect('mongodb://localhost/test', {
useNewUrlParser: true, //不加这个有警告提示
useUnifiedTopology: true //不加这个有警告提示
}).then(() => {
console.log('连接数据库成功')
}).catch(err => {
console.log(err, '连接数据库失败')
})
//创建集合结构
const userSchema = new schema({
name: String,
city: String,
sex: Number
})
//创建集合,集合名称为users,自动给你加复数
const Model = mongoose.model('user', userSchema)
////创建文档并把文档插入数据库中:方式一
const doc = new Model({
name: 'eric',
city: '深圳',
sex: 2
})
doc.save()
//创建文档并把文档插入数据库中:方式二(推荐)
Model.create({
name: '嘻嘻',
city: '广州',
sex: 1
}, (err, doc) => {
if (err) throw err
console.log(doc)
})
4、MongoDB字段验证
- required 验证字段是否为必须输入,值为boolean
- minlength,maxlength 验证字符的值的最小长度和最大长度
- trim 去除字符串守尾空格,值为boolean
- min、max 验证最小最大数字
- default 默认值
- enum 规定输入的值
- validate 自定义条件验证,通过validator函数处理输入值,message为自定义错误信息
- 通过catch获取errors对象,遍历对象从中获取对应字段自定义报错信息
const mongoose = require('mongoose')
const schema = mongoose.Schema
mongoose.set('useFindAndModify', false)
//连接数据库
mongoose.connect('mongodb://localhost/test', {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log('连接数据库成功')
}).catch(err => {
console.log(err, '连接数据库失败')
})
//创建集合结构
const userSchema = new schema({
name: {
type: String,
required: [true, '该字段为必选字段'],
minlength: [2, '输入值长度小于最小长度'],
maxlength: [6, '输入值长度大于最大长度'],
trim: true
},
age: {
type: Number,
min: 18,
max: 30
},
createTime: {
type: Date,
default: Date.now
},
hobbies: {
type: String,
enum: {
values: ['唱', '跳', 'Rap'],
message: '该值不在设定的值当中'
}
},
score: {
type: Number,
validate: { //自定义验证规则
validator: v => {
//返回布尔值
return v && v > 0 && v < 100
},
message: '不是有效的分数'
}
}
})
//创建集合
const Model = mongoose.model('user', userSchema)
Model.create({hobbies: '篮球', score: -1})
.then(res => console.log(res))
.catch(error => { //如果有多个报错信息,需要循环处理
const errs = error.errors
for (let [field, err] in Object.entries(errs)) {
console.log(field, err.message)
}
})
5、MongoDB导入文件数据
将mongoimport命令加到环境变量:

待导入文件的格式:

导入命令:

6、MongoDB查询文档
const mongoose = require('mongoose')
const schema = mongoose.Schema
//连接数据库
mongoose.connect('mongodb://localhost/test', {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log('连接数据库成功')
}).catch(err => {
console.log(err, '连接数据库失败')
})
//创建集合结构
const userSchema = new schema({
name: String,
city: String,
sex: Number,
age: Number
})
//创建集合
const Model = mongoose.model('user', userSchema)
//查询多条:查询所有文档,res为数组
Model.find().then(res => {
console.log(res)
})
//查询多条:查询name='6'的文档,res为数组
Model.find({name: '6'}).then(res => {
console.log(res)
})
//查询多条:查询name='6'&& city='上海'的文档,res为数组
Model.find({name: '6', city: '上海'}).then(res => {
console.log(res)
})
//查询单条:查询name='2'的文档,返回第一条,res为对象
Model.findOne({name: '2'}).then(res => {
console.log(res)
})
//区间查询: 18<=age<=38
Model.find({age: {$gte: 18, $lte: 38}}).then(res => {
console.log(res)
})
//模糊查询: 正则匹配
Model.find({city: /上/}).then(res => {
console.log(res)
})
//限制返回字段:只返回name和city字段,以及_id字段
Model.find().select('name city').then(res => {
console.log(res)
})
//限制返回字段:只返回name和city字段
Model.find().select('name city -_id').then(res => {
console.log(res)
})
//限制返回字段:除了name其他都返回
Model.find().select('-name').then(res => {
console.log(res)
})
//字段排序:加减号是降序,不加是升序
Model.find().sort('-age').then(res => {
console.log(res)
})
//跳过以及限制:查全部数据,跳过前两条,只显示第3,4,5条
Model.find().skip(2).limit(3).then(res => {
console.log(res)
})
7、MongoDB更新文档
const mongoose = require('mongoose')
const schema = mongoose.Schema
mongoose.set('useFindAndModify', false) //不写这个会有警告信息
//连接数据库
mongoose.connect('mongodb://localhost/test', {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log('连接数据库成功')
}).catch(err => {
console.log(err, '连接数据库失败')
})
//创建集合结构
const userSchema = new schema({
name: String,
city: String,
sex: Number,
age: Number
})
//创建集合
const Model = mongoose.model('user', userSchema)
//更新单个文档:
// 找到⼀个⽂档并更新,如果查询多个⽂档,则更新第⼀个匹配⽂档 返回值为更新后的⽂档
Model.findOneAndUpdate({name: '2'}, {city: '深圳'}).then(res => {
console.log(res)
})
//更新单个文档:
//作用跟上面一样,更新指定条件⽂档,如果查询多个⽂档,则更新第⼀个匹配⽂档,
// 区别是返回值不同 {n:1,nModified:1,ok:1}
Model.updateOne({name: '2'}, {city: '上海'}).then(res => {
console.log(res)
})
//更新多个文档:如果条件为空,则更新全部文档
Model.updateMany({}, {city: "深圳"}).then(res => {
console.log(res)
})
//更新多个文档:更新所有name='2'的所有文档
Model.updateMany({name: '2'}, {city: "深圳"}).then(res => {
console.log(res)
})
8、MongoDB删除文档
const mongoose = require('mongoose')
const schema = mongoose.Schema
mongoose.set('useFindAndModify', false)
//连接数据库
mongoose.connect('mongodb://localhost/test', {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log('连接数据库成功')
}).catch(err => {
console.log(err, '连接数据库失败')
})
//创建集合结构
const userSchema = new schema({
name: String,
city: String,
sex: Number,
age: Number
})
//创建集合
const Model = mongoose.model('user', userSchema)
//删除单个文档:
//找到⼀个⽂档并删除,如果查询多个⽂档,则删除第⼀个匹配⽂档 返回值为该⽂档
Model.findOneAndDelete({name: 'xx'}).then(res => {
console.log(res)
})
//删除单个文档:
//跟上面一样,只是返回值不一样 {n:1,ok:1,deleteCount:1}
Model.deleteOne({name: '2'}).then(res => {
console.log(res)
})
//删除多个⽂档:如果条件为空,就删除全部文档
Model.deleteMany().then(res => {
console.log(res)
})
9、示例
数据结构:

// 插入一条数据 function addPoints(cardId: string, statusId: string) { return models.cardStamp.updateOne({_id: cardId}, {$push: {'stampHistories': {statusId: statusId, dates: points}}}) } //更新cardId=?且statusId=?的dates数据 function updatePoints(cardId: string, statusId: string) { return models.cardStamp.updateOne( { _id: cardId, stampHistories: { $elemMatch: { statusId: statusId } } }, {$set: {'stampHistories.$.dates': points}}) } //更新第一条数据 function updatePointsFirst(cardId: string) { return models.cardStamp.updateOne( { _id: cardId, stampHistories: { $elemMatch: { index: 0 } } }, {$set: {'stampHistories.$.dates': points}}) }

export default { local: "mongodb://localhost/test", st: "mongodb://kanban4st:Kanban%254st@kbmongo4st-0:27017,kbmongo4st-1:27017,kbmongo4st-2:27017/kanban?replicaSet=kbmongo4st&maxpoolsize=300&maxidletimems=180000&connecttimeoutms=10000&sockettimeoutms=60000&readpreference=secondaryPreferred", uat: "mongodb://kanban4uat:Kb#uat%251P#f@kanban4uat-0:27017,kanban4uat-1:27017,kanban4uat-2:27017/kanban?replicaSet=kanban4uat&maxpoolsize=300&maxidletimems=180000&connecttimeoutms=10000&sockettimeoutms=60000&readpreference=secondaryPreferred" }
import mongoose from "mongoose"; mongoose.connection.once('open', () => { console.log('连接数据库成功') }) mongoose.connection.once('close', () => { console.log('连接断开') }) export default { cardStamp: mongoose.model('cardStamp', new mongoose.Schema({ _id: String, troopId: String, kanbanType: String, archived: Boolean, stampHistories: Array({ _id: false, // 建表不带_id字段 statusId: String, dates: Array(Date), projectKey: String }) }, {versionKey: false} // 建表不带__v版本字段 ), 'cardStamp' // 表名不加s 第三个参数可以重命名这个集合 ) }
/// <reference path="./types.d.ts" /> import mongoose from "mongoose"; const axios = require("axios") import model from "./model" /** * 获取最近几天的日期 * * @param n * 往前推的天数 * getLatestDays(7) = [ * '2022-11-17T16:00:00.000+0000', * '2022-11-16T16:00:00.000+0000', * '2022-11-15T16:00:00.000+0000', * '2022-11-14T16:00:00.000+0000', * '2022-11-13T16:00:00.000+0000', * '2022-11-12T16:00:00.000+0000', * '2022-11-11T16:00:00.000+0000'] * */ function getLatestDays(n: number) { const currDate = new Date() return Array.from({length: n}, (v, k) => { const preDate = new Date(currDate.getTime() - (k + 1) * 24 * 60 * 60 * 1000) return `${preDate.getFullYear()}-${(preDate.getMonth() + 1).toString().padStart(2, '0')}-${preDate.getDate().toString().padStart(2, '0')}T16:00:00.000+0000` }); } /** * 连接 Mongodb * @param uri */ async function connectMongodb(uri: string) { return await mongoose.connect(uri) } /** * 断开连接 Mongodb */ function disconnectMongodb() { mongoose.connection.close() } /** 根据cardId 获取 statusId ,用法:getCardStatusId('FEATURE002-747') * @param cardId */ async function getCardPoint(cardId: string): Promise<CardStampType> { const res = await axios.get(`http://cmbkb-service-kanban-st.paasst.cmbchina.cn/tracking/cards/${cardId}`) const result: CardStampType = { _id: res.data.id, troopId: res.data.troop.id, kanbanType: res.data.troopKanbanType, archived: false, stampHistories: [ { statusId: res.data.cardStatus.id, dates: getLatestDays(20), } ] } if (res.data.sprintId) { result.stampHistories[0].sprintId = res.data.sprintId } if (res.data.vpProject.id.indexOf("@default") == -1) { // 不是默认泳道 result.stampHistories[0].projectKey = res.data.vpProject.id } return result } async function handleCardPoint(cardId: string) { const cardDetail = await getCardPoint(cardId) return new Promise((resolve, reject) => { model.cardStamp.deleteOne({_id: cardId}).then(res => { model.cardStamp.create(cardDetail, (err, doc) => { if (err) { resolve(`${cardId}:失败`) } resolve(`${cardId}:ok`) }) }) }) } export default {connectMongodb, disconnectMongodb, handleCardPoint, getLatestDays}
interface StampHistory { statusId: string, dates: Array<string>, projectKey?: string, sprintId?: string } interface CardStampType { _id: string, troopId: string, kanbanType: string, archived: boolean, stampHistories: Array<StampHistory> }
import service from "./service/service" import uri from "./service/conf" const jobs = [ service.handleCardPoint('Z990083-576') ] service.connectMongodb(uri.st).then(() => { Promise.all(jobs).then(res => { console.log(res) service.disconnectMongodb() }) })
浙公网安备 33010602011771号