mm_mongodb
Version:
MongoDB数据库操作模块,提供简洁易用的API、缓存功能以及完整的Redis兼容接口,支持事件系统、事务和连接池管理
613 lines (440 loc) • 18.9 kB
Markdown
# mm_mongodb
MongoDB数据库操作模块,提供了简洁易用的API、缓存功能以及**完整的Redis兼容接口**。
## 项目信息
- **当前版本**: 1.5.0
- **更新日期**: 2024-12-23
- **Gitee地址**: [https://gitee.com/qiuwenwu91/mm_mongodb](https://gitee.com/qiuwenwu91/mm_mongodb)
- **Node.js 版本要求**: >= 14.0.0(推荐 16.0.0 及以上)
## 功能特点
- ✅ 支持MongoDB连接池管理
- ✅ 自动连接和重连机制
- ✅ 事务支持
- ✅ 索引管理
- ✅ 缓存功能
- ✅ **完整的Redis兼容API**:支持Redis风格的键值操作、列表、集合、有序集合、哈希等数据类型
- ✅ 环境变量配置支持
- ✅ 完善的错误处理和重试机制
- ✅ 查询注入防护
- ✅ 事件系统:支持监听关键操作的事件,包括操作前、操作后和错误事件
## 安装
```bash
npm install mm_mongodb
```
## 配置
### 基本配置
在项目根目录创建 `config.tpl.json` 文件,配置如下:
```json
{
"mongodb": {
"url": "mongodb://username:password@host:port/database",
"poolSize": 20,
"minPoolSize": 5,
"maxPoolSize": 100,
"waitQueueTimeoutMS": 10000,
"connectTimeoutMS": 10000,
"socketTimeoutMS": 45000,
"authSource": "admin",
"authMechanism": "DEFAULT"
}
}
```
### 环境变量配置
支持通过环境变量配置MongoDB连接信息,优先级高于配置文件:
- `MONGO_URL`:MongoDB连接字符串
- `MONGO_HOST`:MongoDB主机地址
- `MONGO_PORT`:MongoDB端口
- `MONGO_USERNAME`:MongoDB用户名
- `MONGO_PASSWORD`:MongoDB密码
- `MONGO_DATABASE`:MongoDB数据库名
- `MONGO_AUTH_SOURCE`:认证源数据库
- `MONGO_AUTH_MECHANISM`:认证机制
## 使用示例
### 基本使用
```javascript
const { MongoDB } = require('mm_mongodb');
// 创建MongoDB实例
const db = new MongoDB();
// 连接数据库
await db.open();
// 创建表
await db.createTable('users');
// 插入数据
await db.insert('users', { name: '张三', age: 25 });
// 查询数据
const result = await db.get('users', { name: '张三' });
// 更新数据
await db.set('users', { name: '张三' }, { age: 26 });
// 删除数据
await db.del('users', { name: '张三' });
// 关闭连接
await db.close();
```
### 使用连接池
```javascript
const { mongodb_admin } = require('mm_mongodb');
// 获取连接池实例
const db = mongodb_admin('default');
// 使用连接池
await db.open();
const data = await db.get('users', {});
```
### 使用事务
```javascript
const { MongoDB } = require('mm_mongodb');
const db = new MongoDB();
await db.open();
// 开始事务
const session = await db.startTransaction();
try {
// 在事务中执行操作
await db.insert('users', { name: '李四', age: 30 }, { session });
await db.insert('orders', { userId: '李四', amount: 100 }, { session });
// 提交事务
await db.commitTransaction(session);
console.log('事务提交成功');
} catch (error) {
// 回滚事务
await db.rollbackTransaction(session);
console.error('事务回滚:', error);
}
```
### 创建索引
```javascript
const db = new MongoDB();
await db.open();
// 创建普通索引
await db.createIndex('users', { name: 1 }, { unique: true });
// 创建TTL索引(数据过期自动删除)
await db.createTTLIndex('logs', { createdAt: 1 }, 3600); // 3600秒后过期
```
## 事件系统
mm_mongodb提供了完整的事件系统,允许您监听各种数据库操作的前后事件,便于进行日志记录、性能监控、数据校验等操作。
### 支持的事件
| 事件名称 | 触发时机 | 事件参数 |
|--------|--------|--------|
| `set_before` | 在执行set操作前 | `{ table, key, value }` |
| `set_after` | 在执行set操作后 | `{ table, key, value, result }` |
| `set_error` | 在set操作出错时 | `{ table, key, value, error }` |
| `del_before` | 在执行del操作前 | `{ table, key }` |
| `del_after` | 在执行del操作后 | `{ table, key, result }` |
| `del_error` | 在del操作出错时 | `{ table, key, error }` |
| `add_before` | 在执行add操作前 | `{ table, key, value }` |
| `add_after` | 在执行add操作后 | `{ table, key, value, result, exists }` |
| `add_error` | 在add操作出错时 | `{ table, key, value, error }` |
### 事件参数说明
- `table`: 操作的表名
- `key`: 操作的键名
- `value`: 操作的值
- `result`: 操作的结果
- `error`: 错误信息(仅在错误事件中)
- `exists`: 键是否已存在(仅在add_after事件中)
### 使用示例
```javascript
const { MongoDB } = require('mm_mongodb');
const db = new MongoDB();
// 连接数据库
await db.open();
// 监听set操作前事件
db.on('set_before', (data) => {
console.log(`准备设置缓存: ${data.key} = ${JSON.stringify(data.value)}`);
// 可以在这里进行数据校验、权限检查等
});
// 监听set操作后事件
db.on('set_after', (data) => {
console.log(`缓存设置成功: ${data.key}, 结果:`, data.result);
// 可以在这里记录日志、触发其他操作等
});
// 监听add操作后事件,特别注意exists标记
db.on('add_after', (data) => {
if (data.exists) {
console.log(`键 ${data.key} 已存在,未添加新值`);
} else {
console.log(`成功添加新键: ${data.key}`);
}
});
// 监听错误事件
db.on('set_error', (data) => {
console.error(`设置缓存失败: ${data.key}, 错误:`, data.error);
});
// 执行操作,触发事件
await db.set('test_key', 'test_value');
// 测试add方法的exists标记
await db.add('test_key', 'another_value'); // 键已存在,exists将为true
```
### 应用场景
1. **数据校验和验证**:在操作前事件中验证数据的有效性
2. **访问控制和权限管理**:检查是否有权限执行特定操作
3. **性能监控**:记录操作执行时间,监控系统性能
4. **审计日志**:记录所有关键操作的详细信息
5. **缓存一致性维护**:在数据变更时更新相关缓存
6. **业务规则执行**:在特定操作前后执行业务逻辑
## Redis兼容API使用指南
mm_mongodb提供了**完整的Redis兼容API**,使您可以使用熟悉的Redis命令与MongoDB进行交互。这对于从Redis迁移到MongoDB或在两者间切换使用非常有用。
### 快速开始
```javascript
const { mongodb_cache_admin } = require('mm_mongodb');
// 获取Redis兼容的缓存实例
const cache = mongodb_cache_admin('default');
// 连接数据库(重要:必须先连接)
await cache.open();
// 基本操作示例
await cache.set('name', 'Redis Compatible');
const value = await cache.get('name');
```
### 核心缓存方法
| 方法名 | 说明 | 示例 |
|-------|------|------|
| `set(key, value, seconds)` | 设置键值,支持过期时间 | `await cache.set('name', 'value', 60);` |
| `get(key)` | 获取键值 | `const value = await cache.get('name');` |
| `del(key)` | 删除键 | `await cache.del('name');` |
| `has(key)` / `exists(key)` | 判断键是否存在 | `const exists = await cache.has('name');` |
| `clear(prefix)` | 清空缓存(可选前缀过滤) | `await cache.clear('temp:*');` |
| `keys(pattern)` | 获取匹配的键名 | `const keys = await cache.keys('user:*');` |
| `ttl(key)` | 获取剩余过期时间 | `const ttl = await cache.ttl('name');` |
| `expire(key, seconds)` | 设置过期时间 | `await cache.expire('name', 60);` |
| `add(key, value)` | 添加缓存(如果存在则不覆盖) | `await cache.add('counter', 1);` |
| `addInt(key, value)` | 增加整数值 | `await cache.addInt('counter', 10);` |
| `addFloat(key, value)` | 增加浮点数值 | `await cache.addFloat('score', 0.5);` |
| `addStr(key, value)` | 追加字符串 | `await cache.addStr('message', ' World');` |
| `sort(prefix, count, type)` | 排序并获取 | `const sorted = await cache.sort('user:*', 10, 'asc');` |
### 哈希表操作
```javascript
// 设置哈希字段
await cache.hset('user:1', 'name', '张三');
// 获取哈希字段
const name = await cache.hget('user:1', 'name');
// 获取哈希所有字段和值
const user = await cache.hgetall('user:1');
// 批量设置哈希字段
await cache.hmset('user:2', { name: '李四', age: 30 });
// 批量获取哈希字段
const values = await cache.hmget('user:2', 'name', 'age');
// 删除哈希字段
await cache.hdel('user:1', 'name');
// 批量删除哈希字段
await cache.hdelMulti('user:2', 'name', 'age');
// 判断哈希字段是否存在
const exists = await cache.hexists('user:1', 'name');
// 获取哈希字段数量
const count = await cache.hlen('user:1');
// 获取哈希所有字段名
const keys = await cache.hkeys('user:1');
// 获取哈希所有字段值
const vals = await cache.hvals('user:1');
```
### 列表操作
```javascript
// 从右侧推入元素
await cache.rpush('messages', '你好');
// 从左侧推入元素
await cache.lpush('messages', 'Hello');
// 获取列表长度
const length = await cache.llen('messages');
// 获取列表指定范围的元素
const range = await cache.lrange('messages', 0, 1); // 获取前两个元素
// 获取列表指定索引的元素
const message = await cache.lindex('messages', 0);
// 从左侧弹出元素
const firstMessage = await cache.lpop('messages');
// 从右侧弹出元素
const lastMessage = await cache.rpop('messages');
// 修改列表指定索引的元素
await cache.lset('messages', 0, 'Updated Message');
// 删除列表中的元素
await cache.lrem('messages', 2, 'value'); // 删除前2个值为'value'的元素
// 裁剪列表
await cache.ltrim('messages', 0, 4); // 保留前5个元素
```
### 集合操作
```javascript
// 添加集合成员
await cache.sadd('users:online', 'user1');
// 批量添加集合成员
await cache.saddMulti('users:online', 'user2', 'user3');
// 获取集合所有成员
const members = await cache.smembers('users:online');
// 判断成员是否在集合中
const isOnline = await cache.sismember('users:online', 'user1');
// 获取集合成员数量
const count = await cache.scard('users:online');
// 移除集合成员
await cache.srem('users:online', 'user1');
// 批量移除集合成员
await cache.sremMulti('users:online', 'user2', 'user3');
// 计算两个集合的差集
const offlineUsers = await cache.sdiff('users:all', 'users:online');
// 计算两个集合的交集
const activeUsers = await cache.sinter('users:online', 'users:paid');
// 计算两个集合的并集
const allUsers = await cache.sunion('users:online', 'users:offline');
```
### 有序集合操作
```javascript
// 添加有序集合成员
await cache.zadd('ranking', 'user1', 100);
// 获取有序集合成员数量
const count = await cache.zcard('ranking');
// 获取有序集合成员分数
const score = await cache.zscore('ranking', 'user1');
// 按分数范围获取有序集合成员
const topUsers = await cache.zrangebyscore('ranking', 0, 100);
// 移除有序集合成员
await cache.zrem('ranking', 'user1');
```
### 批量操作
```javascript
// 批量设置键值
await cache.mset({ 'key1': 'value1', 'key2': 'value2' });
// 批量获取键值
const values = await cache.mget('key1', 'key2', 'key3');
// 批量删除键
await cache.del('key1', 'key2', 'key3');
```
### 数值操作
```javascript
// 原子递增
await cache.incr('counter'); // 自增1
// 原子递减
await cache.decr('counter'); // 自减1
// 增加指定整数
await cache.addInt('counter', 10); // 增加10
// 减少指定整数
await cache.addInt('counter', -5); // 减少5
```
## 事件系统
mm_mongodb的缓存模块实现了事件系统,可以监听各种操作的发生。增删改类方法会触发相应的事件,而查询类方法不触发事件以提高性能。
### 事件命名规范
所有事件严格遵循以下命名格式:
- 操作前事件:`action_before`
- 操作后事件:`action_after`
- 操作错误事件:`action_error`
其中`action`为方法名,例如`set`、`del`、`add`等。
### 事件示例
```javascript
// 监听事件
cache.on('set_before', (data) => {
console.log('设置缓存前:', data);
});
cache.on('set_after', (data) => {
console.log('设置缓存后:', data);
});
cache.on('set_error', (data) => {
console.error('设置缓存错误:', data);
});
```
### 支持事件的方法
以下增删改类方法支持事件触发,每种方法都会触发对应的`action_before`、`action_after`和`action_error`事件:
- 原子操作:incr, decr, addInt, addFloat
- 字符串操作:append, addStr
- 列表操作:rpush, lpush, lset, lrem, ltrim, lpop, rpop
- 集合操作:sadd, saddMulti, srem, sremMulti
- 哈希操作:hset, hmset, hdel, hdelMulti
- 有序集合操作:zadd, zrem
- 键操作:set, del, expire, mset, add
### 事件参数
事件回调函数接收的参数通常包含以下信息:
- 操作涉及的键(key)
- 操作涉及的值(value)
- 操作结果(仅在after事件中)
- 错误信息(仅在error事件中)
具体参数结构取决于触发事件的方法类型。
## 性能优化建议
1. **使用连接池**:优先使用`mongodb_admin`和`mongodb_cache_admin`创建的连接池实例
2. **合理设置索引**:为常用查询字段创建索引
3. **查询优化**:避免全表扫描,使用精确查询
4. **批量操作**:优先使用批量操作方法如`mset`、`mget`等
5. **过期时间**:为临时数据设置合理的过期时间,避免数据膨胀
6. **Redis兼容模式**:使用字符串键名可以简化操作,与Redis保持一致的使用体验
## 从Redis迁移指南
从Redis迁移到使用mm_mongodb的Redis兼容API时,只需以下步骤:
1. 安装mm_mongodb:`npm install mm_mongodb`
2. 修改导入语句:将Redis客户端导入改为`mongodb_cache_admin`
3. 添加连接初始化:调用`await cache.open()`确保连接已建立
4. 保持命令调用不变:大多数Redis命令可以直接使用
```javascript
// Redis客户端示例
// const redis = require('redis');
// const client = redis.createClient();
// await client.connect();
// mm_mongodb替换示例
const { mongodb_cache_admin } = require('mm_mongodb');
const cache = mongodb_cache_admin('default');
await cache.open(); // 确保连接已建立
// 后续命令调用保持不变
await cache.set('key', 'value');
const value = await cache.get('key');
```
## API文档
### MongoDB类
#### 构造函数
```javascript
new MongoDB(scope = 'default', dir = '', config = {})
```
- `scope`:作用域,用于区分不同的连接池
- `dir`:配置文件路径
- `config`:配置对象
#### 连接管理
- `open()`:连接数据库
- `close()`:关闭数据库连接
- `setUrl(url)`:设置连接URL
- `setConfig(config)`:设置配置
#### 表操作
- `createTable(tableName)`:创建表
- `dropTable(tableName)`:删除表
- `existsTable(tableName)`:检查表是否存在
#### 数据操作
- `insert(tableName, data)`:插入数据
- `insertBatch(tableName, datas)`:批量插入数据
- `get(tableName, query, options)`:查询数据
- `getOne(tableName, query, options)`:查询单条数据
- `set(tableName, query, data, options)`:修改数据
- `del(tableName, query, options)`:删除数据
- `count(tableName, query)`:统计数量
#### 事务支持
- `startTransaction()`:开始事务,返回会话对象
- `commitTransaction(session)`:提交事务
- `rollbackTransaction(session)`:回滚事务
#### 索引管理
- `createIndex(tableName, keys, options)`:创建索引
- `createTTLIndex(tableName, keys, expireAfterSeconds)`:创建TTL索引
- `getIndexes(tableName)`:获取索引信息
- `dropIndex(tableName, indexName)`:删除索引
### Redis兼容API
详细的Redis兼容API见上方「Redis兼容API使用指南」章节。支持以下类别的API:
- **核心缓存方法**:set, get, del, has, exists, clear, keys, ttl, expire等
- **哈希表操作**:hset, hget, hgetall, hmset, hmget, hdel, hdelMulti, hexists, hkeys, hvals, hlen等
- **列表操作**:rpush, lpush, lrange, lindex, llen, lpop, rpop, lset, lrem, ltrim等
- **集合操作**:sadd, saddMulti, srem, sremMulti, smembers, scard, sismember, sdiff, sinter, sunion等
- **有序集合操作**:zadd, zrem, zrangebyscore, zscore, zcard等
- **批量操作**:mget, mset等
- **数值操作**:incr, decr, addInt, addFloat等
## 安全注意事项
1. 密码会自动进行URI编码,确保安全传输
2. 支持查询条件注入防护
3. 自动加载环境变量中的敏感信息,避免硬编码
4. 连接错误时会自动重试,提高稳定性
5. 避免使用未经处理的用户输入作为查询条件
## 兼容性表格
查看详细的方法兼容性对比表格,请参考项目中的 [compatibility_table.md](https://github.com/qwwang/mm_mongodb/blob/main/compatibility_table.md) 文件。
## 依赖
- [mm_logs](https://www.npmjs.com/package/mm_logs):日志模块
- [mongodb](https://www.npmjs.com/package/mongodb):MongoDB官方驱动
## 作者
邱文武 - [qww.elins.cn](http://qww.elins.cn)
## 贡献指南
欢迎参与mm_mongodb项目的开发和改进!以下是贡献代码的基本步骤:
1. **Fork 仓库**:在Gitee上Fork项目到您的账户
2. **克隆仓库**:`git clone https://gitee.com/您的用户名/mm_mongodb.git`
3. **创建分支**:`git checkout -b feature/your-feature-name`
4. **提交更改**:`git commit -m "feat: 添加新功能描述"`
5. **推送分支**:`git push origin feature/your-feature-name`
6. **创建PR**:在Gitee上提交Pull Request
## 问题反馈
如果您在使用过程中遇到任何问题或有改进建议,请通过以下方式反馈:
- **Gitee Issues**:[https://gitee.com/qiuwenwu91/mm_mongodb/issues](https://gitee.com/qiuwenwu91/mm_mongodb/issues)
- **NPM 链接**:[https://www.npmjs.com/package/mm_mongodb](https://www.npmjs.com/package/mm_mongodb)
## 鸣谢
感谢所有为mm_mongodb项目做出贡献的开发者和用户!
## 相关链接
- **MongoDB 官方文档**:[https://docs.mongodb.com/](https://docs.mongodb.com/)
- **Node.js MongoDB 驱动**:[https://www.npmjs.com/package/mongodb](https://www.npmjs.com/package/mongodb)