描述:
migrate-mongo是一个js模块,通过migrate-mongo可以实现远程操作mongodb数据库。其中执行脚本的版本控制是通过配置文件进行远程MongoDB数据库连接配置,执行时在远程数据库下创建一个默认changelog集合,将执行文件记录存储下来。
环境准备
服务端:
- MongoDB副本集 - 为migrate-mongo提供MongoDB数据库支持,【副本集】提供migrate-mongo事务支持
客户端:
- node环境 - 为migrate-mongo提供执行环境
基本用法
这里粗略写了一下,具体可以见最后详情链接
- 初始化新项目
# 创建工作目录,并在目录下执行初始化
$ mkdir albums-migrations
$ cd albums-migrations
$ migrate-mongo init
上面的初始化命令做了两件事:
- 创建了一个migrate-mongo-config.js文件 (该文件用于连接MongoDB数据库相关配置)
- 创建了一个migrations文件夹 (该文件夹用于存储执行脚本文件)
- 链接配置及配置信息加密
migrate-mongo-config.js文件配置详情
migrate-mongo-config.js文件为配置远程mongodb数据库的连接配置文件。编辑migrate-mongo-config.js文件,确保url及databaseName正确配置。
// 在这个文件中,您可以配置migrate-mongo
module.exports = {
mongodb: {
// MongoDB地址:
url: "mongodb://localhost:27017",
// MongoDB数据库名称:
databaseName: "YOURDATABASENAME",
options: {
useNewUrlParser: true // 连接时删除弃用警告
// connectTimeoutMS: 3600000, // 将连接超时时间设置为1小时
// socketTimeoutMS: 3600000, // 将套接字超时时间增加到1小时
}
},
// 迁移目录可以是相对路径或绝对路径。仅在确实需要时编辑此内容。
migrationsDir: "migrations",
// 存储应用的更改的mongodb集合。仅在确实需要时编辑此内容。
changelogCollectionName: "changelog",
// 在迁移目录中创建迁移和搜索的文件扩展名
migrationFileExtension: ".js",
// 启用算法以创建文件内容的校验和,并在比较中使用该校验和来确定
// useFileHash设置为false,migrations下执行文件被执行后,变更文件内容执行将不发生变化。为true,执行将更新
useFileHash: false
};
配置信息加密实现
实际使用过程中我们一般会对敏感属性不能直接暴露出来,需要做加密处理。migrate-mongo目前自身未提供对配置文件加密功能。实现方案: 通过crypto-js依赖实现对url配置信息进行加密,重写migrate-mongo,在加载属性时进行解密。
加密url示例:
首先下载用于加密的依赖:npm install crypto-js
然后创建一个js文件,填充以下内容:
var CryptoJS = require("crypto-js");
// Encrypt
var ciphertext = CryptoJS.AES.encrypt("mongodb://user:passwd@101.32.14.131:8080/mongo", 'secret key 123').toString();
console.log(ciphertext);
执行node xx.js即可在屏幕上获取到输出的加密内容
重写migrate-mongo属性加载部分代码 database.js:
$ vi /usr/local/lib/node_modules/migrate-mongo/lib/env/database.js
const { MongoClient } = require("mongodb");
const _ = require("lodash");
const config = require("./config");
var CryptoJS = require("crypto-js"); // crypto-js依赖引入
module.exports = {
async connect() {
const configContent = await config.read();
//const url = _.get(configContent, "mongodb.url");
let url = _.get(configContent, "mongodb.url"); // 对加密url进行解密处理
var bytes = CryptoJS.AES.decrypt(url, 'secret key 123'); // 对加密url进行解密处理
url = bytes.toString(CryptoJS.enc.Utf8); // 对加密url进行解密处理
const databaseName = _.get(configContent, "mongodb.databaseName");
const options = _.get(configContent, "mongodb.options");
- 执行脚本创建及编排
执行脚本创建
$ cd albums-migrations
$ migrate-mongo create [description]
For example:
$ migrate-mongo create blacklist_the_beatles
创建: migrations/20221010111912-blacklist_the_beatles.js
执行脚本编辑
脚本详情
up部分写入执行脚本,down部分写入回退脚本
module.exports = { async up(db, client) {
// TODO write your migration here.
// See https://github.com/seppevs/migrate-mongo/#creating-a-new-migration-script
// Example:
// return db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: true}});
// await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: true}});
},
async down(db, client) {
// TODO write the statements to rollback your migration (if possible)
// Example:
// await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: false}});
}
};
- 执行、回退操作
执行
执行migrations目录下所有未被执行的.js脚本文件
$ migrate-mongo up
回退
每次回退只会向上回退一次记录(最近一个.js脚本文件)
$ migrate-mongo down
- 事务实现
官方文档里面没有加session,执行发现不能实现事务提交。看了官方文档,发现加session,试了一下成了。
module.exports = {
async up(db, client) {
const session = client.startSession();
try {
await session.withTransaction(async () => {
await db.collection('mongo').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: true}}, { session });
await db.collection('mongo').updateOne1({artist: 'The Doors'}, {$set: {stars: 5}}, { session });
});
} finally {
await session.endSession();
}
},
async down(db, client) {
const session = client.startSession();
try {
await session.withTransaction(async () => {
await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: false}}, { session });
await db.collection('albums').updateOne({artist: 'The Doors'}, {$set: {stars: 0}}, { session });
});
} finally {
await session.endSession();
}
},
};