mm_mongodb 1.5.7 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +392 -261
- package/index.js +2839 -3978
- package/package.json +4 -4
- package/test.js +807 -285
package/README.md
CHANGED
|
@@ -1,96 +1,90 @@
|
|
|
1
1
|
# mm_mongodb
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一个基于 MongoDB 的数据库操作模块,提供与 **mm_redis / mm_cachebase 完全对齐** 的异步缓存操作接口,同时保留 MongoDB 原生的 CRUD、事务与索引管理能力,支持连接池、TTL 自动过期和冒号风格的事件通知系统。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](https://github.com/qwwang/mm_mongodb/blob/main/LICENSE)
|
|
7
|
-
[](https://nodejs.org/)
|
|
5
|
+
## 项目信息
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
- **当前版本**: 2.0.0
|
|
8
|
+
- **更新日期**: 2026-08-04
|
|
9
|
+
- **Gitee地址**: [https://gitee.com/qiuwenwu91/mm_mongodb](https://gitee.com/qiuwenwu91/mm_mongodb)
|
|
10
|
+
- **Node.js 版本要求**: >= 14.0.0(推荐 16.0.0 及以上)
|
|
11
|
+
- **MongoDB 版本要求**: 4.0+
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
- ✅ **Connection Pool Management** - Efficient connection pooling with automatic reconnection
|
|
13
|
-
- ✅ **Transaction Support** - Full ACID transactions with session management
|
|
14
|
-
- ✅ **Event System** - Comprehensive event system for monitoring operations
|
|
15
|
-
- ✅ **Index Management** - Easy index creation and management
|
|
16
|
-
- ✅ **TTL Support** - Automatic data expiration with TTL indexes
|
|
17
|
-
- ✅ **Environment Variable Configuration** - Flexible configuration via environment variables
|
|
18
|
-
- ✅ **Query Injection Protection** - Built-in security against NoSQL injection
|
|
19
|
-
- ✅ **Performance Optimized** - Optimized for high-performance database operations
|
|
13
|
+
## 特点
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
- **接口与 mm_redis / mm_cachebase 完全对齐**:全异步 API、冒号风格事件、统一方法签名与返回值,三模块可无缝切换
|
|
16
|
+
- **全异步设计**:所有公共方法均为 `async`,返回 `Promise`
|
|
17
|
+
- **冒号风格事件**:`set:before` / `set:after` / `set:error`,与 mm_redis / mm_cachebase 一致
|
|
18
|
+
- **完整的数据类型支持**:字符串、哈希表、列表、集合、有序集合、发布订阅
|
|
19
|
+
- **MongoDB 原生能力**:保留核心 CRUD、事务(ACID)、索引与 TTL 索引管理
|
|
20
|
+
- **连接池管理**:内置连接池,支持自动重连与重试
|
|
21
|
+
- **TTL 自动过期**:通过 `expire_at` 字段与 TTL 索引实现自动过期清理
|
|
22
|
+
- **查询注入防护**:内置 `_sanitizeQuery` 防止 NoSQL 注入
|
|
23
|
+
|
|
24
|
+
## 安装
|
|
22
25
|
|
|
23
26
|
```bash
|
|
24
27
|
npm install mm_mongodb
|
|
25
28
|
```
|
|
26
29
|
|
|
27
|
-
##
|
|
30
|
+
## 基本使用
|
|
28
31
|
|
|
29
|
-
###
|
|
32
|
+
### Redis 兼容 API(与 mm_redis / mm_cachebase 对齐)
|
|
30
33
|
|
|
31
34
|
```javascript
|
|
32
|
-
const { MongoDB } = require('mm_mongodb');
|
|
35
|
+
const { MongoDB, mongodbAdmin } = require('mm_mongodb');
|
|
33
36
|
|
|
34
|
-
//
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
// Connect to database
|
|
38
|
-
await db.open();
|
|
37
|
+
// 通过连接池管理器获取实例(推荐)
|
|
38
|
+
const cache = mongodbAdmin('myapp');
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
async function example() {
|
|
41
|
+
// 连接数据库
|
|
42
|
+
await cache.open();
|
|
42
43
|
|
|
43
|
-
//
|
|
44
|
-
await
|
|
44
|
+
// 设置缓存,10 秒过期
|
|
45
|
+
await cache.set("name", "张三", 10);
|
|
45
46
|
|
|
46
|
-
//
|
|
47
|
-
const
|
|
47
|
+
// 获取缓存
|
|
48
|
+
const value = await cache.get("name");
|
|
49
|
+
$.log.debug(value); // 输出: 张三
|
|
48
50
|
|
|
49
|
-
//
|
|
50
|
-
await
|
|
51
|
+
// 删除缓存(返回删除数量)
|
|
52
|
+
const deleted = await cache.del("name");
|
|
53
|
+
$.log.debug(deleted); // 输出: 1
|
|
51
54
|
|
|
52
|
-
//
|
|
53
|
-
await
|
|
54
|
-
|
|
55
|
-
// Close connection
|
|
56
|
-
await db.close();
|
|
55
|
+
// 关闭连接
|
|
56
|
+
await cache.close();
|
|
57
|
+
}
|
|
57
58
|
```
|
|
58
59
|
|
|
59
|
-
###
|
|
60
|
+
### MongoDB 原生 API
|
|
60
61
|
|
|
61
62
|
```javascript
|
|
62
|
-
const {
|
|
63
|
+
const { MongoDB } = require('mm_mongodb');
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const db = new MongoDB({ database: 'mydb' });
|
|
66
|
+
await db.open();
|
|
66
67
|
|
|
67
|
-
//
|
|
68
|
-
await
|
|
68
|
+
// 插入文档
|
|
69
|
+
await db.addObj({ key: 'user:1', name: 'John', age: 25 });
|
|
69
70
|
|
|
70
|
-
//
|
|
71
|
-
await
|
|
72
|
-
const value = await cache.get('name');
|
|
73
|
-
await cache.del('name');
|
|
71
|
+
// 查询文档
|
|
72
|
+
const user = await db.findOne({ key: 'user:1' });
|
|
74
73
|
|
|
75
|
-
//
|
|
76
|
-
await
|
|
77
|
-
const name = await cache.hget('user:1', 'name');
|
|
74
|
+
// 更新文档
|
|
75
|
+
await db.setting({ key: 'user:1' }, { age: 26 });
|
|
78
76
|
|
|
79
|
-
//
|
|
80
|
-
await
|
|
81
|
-
await cache.rpush('messages', 'World');
|
|
82
|
-
const messages = await cache.lrange('messages', 0, -1);
|
|
77
|
+
// 删除文档
|
|
78
|
+
await db.delete({ key: 'user:1' });
|
|
83
79
|
|
|
84
|
-
|
|
85
|
-
await cache.sadd('users:online', 'user1', 'user2');
|
|
86
|
-
const members = await cache.smembers('users:online');
|
|
80
|
+
await db.close();
|
|
87
81
|
```
|
|
88
82
|
|
|
89
|
-
##
|
|
83
|
+
## 配置
|
|
90
84
|
|
|
91
|
-
###
|
|
85
|
+
### 环境变量
|
|
92
86
|
|
|
93
|
-
|
|
87
|
+
可通过环境变量配置 MongoDB 连接:
|
|
94
88
|
|
|
95
89
|
```bash
|
|
96
90
|
MONGO_URL=mongodb://username:password@host:port/database
|
|
@@ -102,252 +96,389 @@ MONGO_DATABASE=database
|
|
|
102
96
|
MONGO_AUTH_SOURCE=admin
|
|
103
97
|
```
|
|
104
98
|
|
|
105
|
-
###
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
"authMechanism": "DEFAULT"
|
|
121
|
-
}
|
|
122
|
-
}
|
|
99
|
+
### 构造函数配置
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
const db = new MongoDB({
|
|
103
|
+
host: 'localhost',
|
|
104
|
+
port: 27017,
|
|
105
|
+
database: 'mm',
|
|
106
|
+
user: '',
|
|
107
|
+
password: '',
|
|
108
|
+
min_pool_size: 10,
|
|
109
|
+
max_pool_size: 100,
|
|
110
|
+
connect_timeout_ms: 10000,
|
|
111
|
+
server_selection_timeout_ms: 5000,
|
|
112
|
+
retry_writes: true
|
|
113
|
+
});
|
|
123
114
|
```
|
|
124
115
|
|
|
125
|
-
##
|
|
116
|
+
## 连接管理
|
|
117
|
+
|
|
118
|
+
与 mm_redis / mm_cachebase 一致的连接管理接口:
|
|
126
119
|
|
|
127
|
-
|
|
120
|
+
```javascript
|
|
121
|
+
const cache = mongodbAdmin('myapp');
|
|
122
|
+
await cache.open(); // 打开连接
|
|
123
|
+
const connected = cache.isConnected(); // 检查连接状态,返回 Boolean
|
|
124
|
+
await cache.close(); // 关闭连接,清理发布订阅频道
|
|
125
|
+
```
|
|
128
126
|
|
|
129
|
-
|
|
127
|
+
连接特性:
|
|
128
|
+
- 内置连接 Promise 缓存(`connectPromise`),避免重复连接
|
|
129
|
+
- 最大重试次数 `maxConnectAttempts = 3`,自动重连
|
|
130
|
+
- 连接成功后自动创建 `key` 唯一索引与 `expire_at` TTL 索引
|
|
131
|
+
- 监听连接 `error` / `close` 事件,自动更新连接状态
|
|
132
|
+
|
|
133
|
+
## API 说明
|
|
134
|
+
|
|
135
|
+
所有方法均为 `async`,返回 `Promise`。事件名采用冒号风格(`xxx:before` / `xxx:after` / `xxx:error`)。
|
|
136
|
+
|
|
137
|
+
### 键值操作
|
|
138
|
+
|
|
139
|
+
| 方法 | 签名 | 返回值 |
|
|
140
|
+
|------|------|--------|
|
|
141
|
+
| set | `set(key, value, seconds=0)` | `Promise<Boolean>` |
|
|
142
|
+
| get | `get(key)` | `Promise<Object\|null>` |
|
|
143
|
+
| del | `del(key)` | `Promise<Number>` 删除数量,支持键名数组 |
|
|
144
|
+
| exists | `exists(key)` | `Promise<Number>` 0/1(单键)或计数(多键) |
|
|
145
|
+
| has | `has(key)` | `Promise<Boolean>` |
|
|
146
|
+
| getset | `getset(key, value, seconds=0)` | `Promise<Object\|null>` 旧值 |
|
|
147
|
+
| add | `add(key, value, seconds=0)` | `Promise<Boolean>` SETNX 语义 |
|
|
148
|
+
|
|
149
|
+
### 批量操作
|
|
150
|
+
|
|
151
|
+
| 方法 | 签名 | 返回值 |
|
|
152
|
+
|------|------|--------|
|
|
153
|
+
| mset | `mset(obj, seconds=0)` | `Promise<Boolean>` |
|
|
154
|
+
| mget | `mget(keys)` | `Promise<Array>` 不存在的键返回 null |
|
|
155
|
+
|
|
156
|
+
### 过期时间管理
|
|
157
|
+
|
|
158
|
+
| 方法 | 签名 | 返回值 |
|
|
159
|
+
|------|------|--------|
|
|
160
|
+
| expire | `expire(key, seconds)` | `Promise<Boolean>` |
|
|
161
|
+
| pexpire | `pexpire(key, milliseconds)` | `Promise<Boolean>` |
|
|
162
|
+
| persist | `persist(key)` | `Promise<Boolean>` |
|
|
163
|
+
| ttl | `ttl(key, [seconds])` | `Promise<Number\|Boolean>` 不传 seconds 返回剩余秒数(-1 无过期,-2 已过期) |
|
|
164
|
+
|
|
165
|
+
> 过期通过 `expire_at` 字段配合 TTL 索引(`expireAfterSeconds: 0`)由 MongoDB 自动清理。
|
|
166
|
+
|
|
167
|
+
### 哈希表操作
|
|
168
|
+
|
|
169
|
+
| 方法 | 签名 | 返回值 |
|
|
170
|
+
|------|------|--------|
|
|
171
|
+
| hset | `hset(key, field, value)` | `Promise<Boolean>` |
|
|
172
|
+
| hget | `hget(key, field)` | `Promise<Object\|null>` |
|
|
173
|
+
| hgetall | `hgetall(key)` | `Promise<Object>` |
|
|
174
|
+
| hdel | `hdel(key, fields)` | `Promise<Number>` 删除字段数,支持字段数组 |
|
|
175
|
+
| hmset | `hmset(key, obj)` | `Promise<Boolean>` |
|
|
176
|
+
| hmget | `hmget(key, fields)` | `Promise<Array>` 不存在的字段返回 null |
|
|
177
|
+
| hlen | `hlen(key)` | `Promise<Number>` |
|
|
178
|
+
| hexists | `hexists(key, field)` | `Promise<Boolean>` |
|
|
179
|
+
| hkeys | `hkeys(key)` | `Promise<Array>` 字段名数组 |
|
|
180
|
+
| hvals | `hvals(key)` | `Promise<Array>` 值数组 |
|
|
181
|
+
|
|
182
|
+
### 数值操作
|
|
183
|
+
|
|
184
|
+
| 方法 | 签名 | 返回值 |
|
|
185
|
+
|------|------|--------|
|
|
186
|
+
| incr | `incr(key, seconds=0)` | `Promise<Number>` 错误返回 null |
|
|
187
|
+
| decr | `decr(key, seconds=0)` | `Promise<Number>` 错误返回 null |
|
|
188
|
+
| incrby | `incrby(key, increment, seconds=0)` | `Promise<Number>` 错误返回 null |
|
|
189
|
+
| decrby | `decrby(key, decrement, seconds=0)` | `Promise<Number>` 错误返回 null |
|
|
190
|
+
| incrbyfloat | `incrbyfloat(key, increment, seconds=0)` | `Promise<Number>` 错误返回 null |
|
|
191
|
+
| addInt | `addInt(key, value, seconds=0)` | `Promise<Number>` 兼容方法 |
|
|
192
|
+
| addFloat | `addFloat(key, num, seconds=0)` | `Promise<Number>` 兼容方法 |
|
|
193
|
+
|
|
194
|
+
### 字符串操作
|
|
195
|
+
|
|
196
|
+
| 方法 | 签名 | 返回值 |
|
|
197
|
+
|------|------|--------|
|
|
198
|
+
| setrange | `setrange(key, value, seconds=0)` | `Promise<Number>` 追加后长度 |
|
|
199
|
+
| append | `append(key, value)` | `Promise<Number>` 长度 |
|
|
200
|
+
| strlen | `strlen(key)` | `Promise<Number>` |
|
|
201
|
+
|
|
202
|
+
### 列表操作
|
|
203
|
+
|
|
204
|
+
| 方法 | 签名 | 返回值 |
|
|
205
|
+
|------|------|--------|
|
|
206
|
+
| setForList | `setForList(key, value, seconds=0)` | `Promise<Number>` 列表长度 |
|
|
207
|
+
| addForList | `addForList(key, value, seconds=0)` | `Promise<Number>` 列表长度 |
|
|
208
|
+
| getForList | `getForList(key, start=0, end=-1)` | `Promise<Array>` 支持 Redis 风格负索引 |
|
|
209
|
+
| hasForList | `hasForList(key, value)` | `Promise<Boolean>` |
|
|
210
|
+
| clearForList | `clearForList(key)` | `Promise<Boolean>` |
|
|
211
|
+
|
|
212
|
+
### 集合操作
|
|
213
|
+
|
|
214
|
+
| 方法 | 签名 | 返回值 |
|
|
215
|
+
|------|------|--------|
|
|
216
|
+
| sadd | `sadd(key, members)` | `Promise<Number>` 添加数量,支持成员数组 |
|
|
217
|
+
| srem | `srem(key, members)` | `Promise<Number>` 移除数量,支持成员数组 |
|
|
218
|
+
| sismember | `sismember(key, member)` | `Promise<Boolean>` |
|
|
219
|
+
| smembers | `smembers(key)` | `Promise<Array>` |
|
|
220
|
+
| scard | `scard(key)` | `Promise<Number>` |
|
|
221
|
+
| sinter | `sinter(...keys)` | `Promise<Array>` 交集 |
|
|
222
|
+
| sunion | `sunion(...keys)` | `Promise<Array>` 并集 |
|
|
223
|
+
| sdiff | `sdiff(...keys)` | `Promise<Array>` 差集 |
|
|
224
|
+
|
|
225
|
+
### 有序集合操作
|
|
226
|
+
|
|
227
|
+
| 方法 | 签名 | 返回值 |
|
|
228
|
+
|------|------|--------|
|
|
229
|
+
| zadd | `zadd(key, score, member)` | `Promise<Boolean>` 新成员返回 true |
|
|
230
|
+
| zrem | `zrem(key, members)` | `Promise<Number>` 移除数量,支持成员数组 |
|
|
231
|
+
| zscore | `zscore(key, member)` | `Promise<Number\|null>` |
|
|
232
|
+
| zrank | `zrank(key, member)` | `Promise<Number\|null>` |
|
|
233
|
+
| zrange | `zrange(key, start, stop, withScores=false)` | `Promise<Array>` |
|
|
234
|
+
| zcard | `zcard(key)` | `Promise<Number>` |
|
|
235
|
+
| zincrby | `zincrby(key, increment, member)` | `Promise<Number>` |
|
|
236
|
+
|
|
237
|
+
### 发布订阅
|
|
238
|
+
|
|
239
|
+
> **注意**:发布订阅为进程内实现(基于 `_channels` 回调表),无跨进程能力,与 mm_redis 的跨进程语义不同,与 mm_cachebase 一致。
|
|
240
|
+
|
|
241
|
+
| 方法 | 签名 | 返回值 |
|
|
242
|
+
|------|------|--------|
|
|
243
|
+
| subscribe | `subscribe(channel, func)` | `Promise<Boolean>` |
|
|
244
|
+
| unsubscribe | `unsubscribe(channel)` | `Promise<Boolean>` |
|
|
245
|
+
| publish | `publish(channel, message)` | `Promise<Number>` 接收者数量 |
|
|
246
|
+
|
|
247
|
+
### 键管理
|
|
248
|
+
|
|
249
|
+
| 方法 | 签名 | 返回值 |
|
|
250
|
+
|------|------|--------|
|
|
251
|
+
| keys | `keys(pattern)` | `Promise<Array>` 支持 `*` 通配符 |
|
|
252
|
+
| dbsize | `dbsize()` | `Promise<Number>` |
|
|
253
|
+
| flushdb | `flushdb()` | `Promise<Boolean>` |
|
|
254
|
+
| clear | `clear([pattern])` | `Promise<Boolean>` flushdb 扩展,支持按模式清空 |
|
|
255
|
+
| clearExpire | `clearExpire()` | `Promise<Boolean>` 主动清理过期数据 |
|
|
256
|
+
|
|
257
|
+
## MongoDB 原生 API
|
|
258
|
+
|
|
259
|
+
除 Redis 兼容 API 外,本模块保留 MongoDB 原生操作能力。
|
|
260
|
+
|
|
261
|
+
### 核心 CRUD
|
|
262
|
+
|
|
263
|
+
| 方法 | 签名 | 返回值 |
|
|
264
|
+
|------|------|--------|
|
|
265
|
+
| findOne | `findOne(query, options)` | `Promise<Object\|null>` |
|
|
266
|
+
| find | `find(query, options)` | `Promise<Array>` 自动防注入 |
|
|
267
|
+
| addObj | `addObj(obj)` | `Promise<Object>` 插入结果 |
|
|
268
|
+
| addList | `addList(list)` | `Promise<Object>` 批量插入结果 |
|
|
269
|
+
| setting | `setting(query, body, options)` | `Promise<Object\|Boolean>` 修改结果 |
|
|
270
|
+
| delete | `delete(query)` | `Promise<Object>` 删除结果 |
|
|
271
|
+
| count | `count(query)` | `Promise<Number>` |
|
|
272
|
+
|
|
273
|
+
### 表与索引管理
|
|
274
|
+
|
|
275
|
+
| 方法 | 签名 | 返回值 |
|
|
276
|
+
|------|------|--------|
|
|
277
|
+
| addTable | `addTable(table)` | `Promise<Object>` 创建集合 |
|
|
278
|
+
| createIndex | `createIndex(keys, options)` | `Promise<Object>` |
|
|
279
|
+
| createTTLIndex | `createTTLIndex(keys, expireAfterSeconds)` | `Promise<Object>` |
|
|
280
|
+
| getIndexes | `getIndexes()` | `Promise<Array>` |
|
|
281
|
+
| dropIndex | `dropIndex(indexName)` | `Promise<Boolean>` |
|
|
282
|
+
| dropTable | `dropTable()` | `Promise<Boolean>` |
|
|
283
|
+
|
|
284
|
+
### 事务管理
|
|
285
|
+
|
|
286
|
+
| 方法 | 签名 | 返回值 |
|
|
287
|
+
|------|------|--------|
|
|
288
|
+
| startTransaction | `startTransaction()` | `Promise<Object>` 会话对象 |
|
|
289
|
+
| commitTransaction | `commitTransaction(session)` | `Promise<void>` |
|
|
290
|
+
| rollbackTransaction | `rollbackTransaction(session)` | `Promise<void>` |
|
|
130
291
|
|
|
131
292
|
```javascript
|
|
132
|
-
|
|
293
|
+
const session = await db.startTransaction();
|
|
294
|
+
try {
|
|
295
|
+
await db.addObj({ key: 'a', value: 1 }, { session });
|
|
296
|
+
await db.addObj({ key: 'b', value: 2 }, { session });
|
|
297
|
+
await db.commitTransaction(session);
|
|
298
|
+
} catch (err) {
|
|
299
|
+
await db.rollbackTransaction(session);
|
|
300
|
+
}
|
|
133
301
|
```
|
|
134
302
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
- `close()` - Close database connection
|
|
139
|
-
- `setUrl(url)` - Set connection URL
|
|
140
|
-
- `setConfig(config)` - Set configuration
|
|
141
|
-
|
|
142
|
-
#### Collection Operations
|
|
143
|
-
|
|
144
|
-
- `createTable(tableName)` - Create collection
|
|
145
|
-
- `dropTable(tableName)` - Drop collection
|
|
146
|
-
- `existsTable(tableName)` - Check if collection exists
|
|
147
|
-
|
|
148
|
-
#### Data Operations
|
|
149
|
-
|
|
150
|
-
- `insert(tableName, data)` - Insert document
|
|
151
|
-
- `insertBatch(tableName, datas)` - Batch insert documents
|
|
152
|
-
- `get(tableName, query, options)` - Query documents
|
|
153
|
-
- `getOne(tableName, query, options)` - Query single document
|
|
154
|
-
- `set(tableName, query, data, options)` - Update documents
|
|
155
|
-
- `del(tableName, query, options)` - Delete documents
|
|
156
|
-
- `count(tableName, query)` - Count documents
|
|
157
|
-
|
|
158
|
-
#### Transaction Support
|
|
159
|
-
|
|
160
|
-
- `startTransaction()` - Start transaction
|
|
161
|
-
- `commitTransaction(session)` - Commit transaction
|
|
162
|
-
- `rollbackTransaction(session)` - Rollback transaction
|
|
163
|
-
|
|
164
|
-
#### Index Management
|
|
165
|
-
|
|
166
|
-
- `createIndex(tableName, keys, options)` - Create index
|
|
167
|
-
- `createTTLIndex(tableName, keys, expireAfterSeconds)` - Create TTL index
|
|
168
|
-
- `getIndexes(tableName)` - Get index information
|
|
169
|
-
- `dropIndex(tableName, indexName)` - Drop index
|
|
170
|
-
|
|
171
|
-
### Redis-Compatible API
|
|
172
|
-
|
|
173
|
-
#### Key-Value Operations
|
|
174
|
-
|
|
175
|
-
- `set(key, value, seconds)` - Set key with optional expiration
|
|
176
|
-
- `get(key)` - Get value by key
|
|
177
|
-
- `del(key)` - Delete key
|
|
178
|
-
- `has(key)` / `exists(key)` - Check if key exists
|
|
179
|
-
- `clear(prefix)` - Clear keys with optional prefix
|
|
180
|
-
- `keys(pattern)` - Get matching keys
|
|
181
|
-
- `ttl(key)` - Get time to live
|
|
182
|
-
- `expire(key, seconds)` - Set expiration time
|
|
183
|
-
- `add(key, value)` - Add key if not exists
|
|
184
|
-
|
|
185
|
-
#### Hash Operations
|
|
186
|
-
|
|
187
|
-
- `hset(key, field, value)` - Set hash field
|
|
188
|
-
- `hget(key, field)` - Get hash field
|
|
189
|
-
- `hgetall(key)` - Get all hash fields
|
|
190
|
-
- `hmset(key, object)` - Set multiple hash fields
|
|
191
|
-
- `hmget(key, ...fields)` - Get multiple hash fields
|
|
192
|
-
- `hdel(key, field)` - Delete hash field
|
|
193
|
-
- `hdelMulti(key, ...fields)` - Delete multiple hash fields
|
|
194
|
-
- `hexists(key, field)` - Check if hash field exists
|
|
195
|
-
- `hkeys(key)` - Get all hash field names
|
|
196
|
-
- `hvals(key)` - Get all hash field values
|
|
197
|
-
- `hlen(key)` - Get number of hash fields
|
|
198
|
-
|
|
199
|
-
#### List Operations
|
|
200
|
-
|
|
201
|
-
- `lpush(key, value)` - Push to left of list
|
|
202
|
-
- `rpush(key, value)` - Push to right of list
|
|
203
|
-
- `lrange(key, start, stop)` - Get list range
|
|
204
|
-
- `lindex(key, index)` - Get list element by index
|
|
205
|
-
- `llen(key)` - Get list length
|
|
206
|
-
- `lpop(key)` - Pop from left of list
|
|
207
|
-
- `rpop(key)` - Pop from right of list
|
|
208
|
-
- `lset(key, index, value)` - Set list element by index
|
|
209
|
-
- `lrem(key, count, value)` - Remove list elements
|
|
210
|
-
- `ltrim(key, start, stop)` - Trim list
|
|
211
|
-
|
|
212
|
-
#### Set Operations
|
|
213
|
-
|
|
214
|
-
- `sadd(key, ...members)` - Add set members
|
|
215
|
-
- `saddMulti(key, ...members)` - Add multiple set members
|
|
216
|
-
- `srem(key, member)` - Remove set member
|
|
217
|
-
- `sremMulti(key, ...members)` - Remove multiple set members
|
|
218
|
-
- `smembers(key)` - Get all set members
|
|
219
|
-
- `scard(key)` - Get set size
|
|
220
|
-
- `sismember(key, member)` - Check if member exists in set
|
|
221
|
-
- `sdiff(key1, key2)` - Get set difference
|
|
222
|
-
- `sinter(key1, key2)` - Get set intersection
|
|
223
|
-
- `sunion(key1, key2)` - Get set union
|
|
224
|
-
|
|
225
|
-
#### Sorted Set Operations
|
|
226
|
-
|
|
227
|
-
- `zadd(key, member, score)` - Add sorted set member
|
|
228
|
-
- `zrem(key, member)` - Remove sorted set member
|
|
229
|
-
- `zrangebyscore(key, min, max)` - Get members by score range
|
|
230
|
-
- `zscore(key, member)` - Get member score
|
|
231
|
-
- `zcard(key)` - Get sorted set size
|
|
232
|
-
|
|
233
|
-
#### Batch Operations
|
|
234
|
-
|
|
235
|
-
- `mset(object)` - Set multiple keys
|
|
236
|
-
- `mget(...keys)` - Get multiple keys
|
|
237
|
-
- `del(...keys)` - Delete multiple keys
|
|
238
|
-
|
|
239
|
-
#### Numeric Operations
|
|
240
|
-
|
|
241
|
-
- `incr(key)` - Increment by 1
|
|
242
|
-
- `decr(key)` - Decrement by 1
|
|
243
|
-
- `addInt(key, value)` - Add integer value
|
|
244
|
-
- `addFloat(key, value)` - Add float value
|
|
245
|
-
- `addStr(key, value)` - Append string
|
|
246
|
-
|
|
247
|
-
## Event System
|
|
248
|
-
|
|
249
|
-
mm_mongodb provides a comprehensive event system for monitoring database operations:
|
|
303
|
+
## 事件系统
|
|
304
|
+
|
|
305
|
+
事件名采用**冒号风格**,与 mm_redis / mm_cachebase 完全一致。每个操作有 `:before`、`:after`、`:error` 三种事件。
|
|
250
306
|
|
|
251
307
|
```javascript
|
|
252
|
-
const
|
|
253
|
-
await
|
|
308
|
+
const cache = mongodbAdmin('myapp');
|
|
309
|
+
await cache.open();
|
|
254
310
|
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
311
|
+
// 监听 set 前置事件,可修改参数或取消操作
|
|
312
|
+
cache.on('set:before', (ctx) => {
|
|
313
|
+
$.log.debug('即将设置键:', ctx.key, '值:', ctx.value);
|
|
314
|
+
// 可修改 ctx.key / ctx.value / ctx.seconds
|
|
315
|
+
// 设置 ctx.cancel = true 可取消操作
|
|
258
316
|
});
|
|
259
317
|
|
|
260
|
-
|
|
261
|
-
|
|
318
|
+
// 监听 set 后置事件
|
|
319
|
+
cache.on('set:after', (data) => {
|
|
320
|
+
$.log.debug('设置完成:', data.key, '成功:', data.success);
|
|
262
321
|
});
|
|
263
322
|
|
|
264
|
-
|
|
265
|
-
|
|
323
|
+
// 监听错误事件
|
|
324
|
+
cache.on('set:error', (data) => {
|
|
325
|
+
$.log.error('设置出错:', data.key, data.error);
|
|
266
326
|
});
|
|
267
|
-
|
|
268
|
-
// Trigger events
|
|
269
|
-
await db.set('test_key', 'test_value');
|
|
270
327
|
```
|
|
271
328
|
|
|
272
|
-
###
|
|
329
|
+
### 支持的事件
|
|
273
330
|
|
|
274
|
-
|
|
275
|
-
- `{action}_after` - After operation completion
|
|
276
|
-
- `{action}_error` - On operation error
|
|
331
|
+
所有公共方法均有对应事件,命名规则为 `方法名:before` / `方法名:after` / `方法名:error`:
|
|
277
332
|
|
|
278
|
-
|
|
333
|
+
- 键值:`set`、`get`、`del`、`exists`、`add`
|
|
334
|
+
- 批量:`mset`、`mget`
|
|
335
|
+
- 过期:`expire`、`pexpire`、`persist`、`ttl`
|
|
336
|
+
- 哈希:`hset`、`hget`、`hgetall`、`hdel`、`hmset`、`hmget`、`hlen`
|
|
337
|
+
- 数值:`incr`、`decr`、`incrby`、`decrby`、`incrbyfloat`、`addInt`、`addFloat`
|
|
338
|
+
- 字符串:`setrange`
|
|
339
|
+
- 列表:`setForList`、`addForList`、`getForList`、`hasForList`、`clearForList`
|
|
340
|
+
- 集合:`sadd`、`srem`、`sismember`、`smembers`
|
|
341
|
+
- 有序集合:`zadd`、`zrem`
|
|
342
|
+
- 键管理:`keys`、`clear`、`clearExpire`
|
|
343
|
+
- 原生操作:`addObj`、`addList`、`delete`、`setting`
|
|
279
344
|
|
|
280
|
-
|
|
345
|
+
> **注意**:`destroy` 事件不可使用冒号格式(`destroy:after`),因为 mm_expand 的 `Base.emit` 会将冒号格式解释为 do 流程的阶段,会触发 `_destroyCore`。
|
|
281
346
|
|
|
282
|
-
|
|
283
|
-
2. **Create Appropriate Indexes** - Index frequently queried fields
|
|
284
|
-
3. **Use Batch Operations** - Prefer `mset`, `mget`, `insertBatch` for multiple operations
|
|
285
|
-
4. **Set Expiration Times** - Use TTL indexes for temporary data
|
|
286
|
-
5. **Optimize Queries** - Avoid full collection scans, use precise queries
|
|
347
|
+
## 使用示例
|
|
287
348
|
|
|
288
|
-
|
|
349
|
+
### 哈希表操作
|
|
289
350
|
|
|
290
|
-
|
|
351
|
+
```javascript
|
|
352
|
+
async function hashExample() {
|
|
353
|
+
await cache.hset('user:1', 'name', '张三');
|
|
354
|
+
await cache.hmset('user:1', { age: 25, city: '北京' });
|
|
355
|
+
const name = await cache.hget('user:1', 'name'); // 张三
|
|
356
|
+
const all = await cache.hgetall('user:1'); // { name: '张三', age: 25, city: '北京' }
|
|
357
|
+
await cache.hdel('user:1', ['age', 'city']); // 2
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### 集合操作
|
|
291
362
|
|
|
292
363
|
```javascript
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
await cache.open();
|
|
364
|
+
async function setExample() {
|
|
365
|
+
await cache.sadd('tags', ['node', 'js', 'cache']);
|
|
366
|
+
const isMember = await cache.sismember('tags', 'node'); // true
|
|
367
|
+
const members = await cache.smembers('tags'); // ['node', 'js', 'cache']
|
|
368
|
+
const count = await cache.scard('tags'); // 3
|
|
369
|
+
await cache.srem('tags', ['js']);
|
|
370
|
+
}
|
|
371
|
+
```
|
|
302
372
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
373
|
+
### 有序集合操作
|
|
374
|
+
|
|
375
|
+
```javascript
|
|
376
|
+
async function zsetExample() {
|
|
377
|
+
await cache.zadd('ranking', 100, 'user1');
|
|
378
|
+
await cache.zadd('ranking', 200, 'user2');
|
|
379
|
+
const rank = await cache.zrank('ranking', 'user1'); // 0
|
|
380
|
+
const top = await cache.zrange('ranking', 0, -1, true); // [{member:'user1', score:100}, ...]
|
|
381
|
+
await cache.zincrby('ranking', 50, 'user1'); // 新分数 150
|
|
382
|
+
}
|
|
306
383
|
```
|
|
307
384
|
|
|
308
|
-
|
|
385
|
+
### 发布订阅
|
|
386
|
+
|
|
387
|
+
```javascript
|
|
388
|
+
async function pubsubExample() {
|
|
389
|
+
await cache.subscribe('news', (message) => {
|
|
390
|
+
$.log.debug('收到消息:', message);
|
|
391
|
+
});
|
|
392
|
+
await cache.publish('news', 'hello world'); // 接收者数量: 1
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## 与 mm_redis / mm_cachebase 的接口一致性
|
|
397
|
+
|
|
398
|
+
本模块与 [mm_redis](../mm_redis)、[mm_cachebase](../mm_cachebase) 提供完全对齐的 Redis 兼容接口,调用方可通过统一的方式在内存缓存、Redis、MongoDB 之间无缝切换:
|
|
399
|
+
|
|
400
|
+
- **全异步**:所有方法返回 `Promise`
|
|
401
|
+
- **事件命名**:冒号风格 `xxx:before` / `xxx:after` / `xxx:error`
|
|
402
|
+
- **方法签名**:`set(key, value, seconds=0)`、`mset(obj, seconds=0)`、`incrby(key, increment, seconds=0)` 等
|
|
403
|
+
- **返回值**:`hset` 返回 `Boolean`,`del`/`exists`/`hdel` 返回 `Number`,`hget` 不存在返回 `null`
|
|
309
404
|
|
|
310
|
-
|
|
311
|
-
-
|
|
312
|
-
-
|
|
313
|
-
-
|
|
314
|
-
-
|
|
405
|
+
**差异点**:
|
|
406
|
+
- 发布订阅为进程内实现(mm_redis 支持跨进程,mm_cachebase 与本模块一致为进程内)
|
|
407
|
+
- 集合/有序集合用 JS 数组实现,存于 MongoDB 文档的 `value` 字段,无 Redis 的 O(log N) 复杂度保证
|
|
408
|
+
- 过期依赖 MongoDB 的 TTL 索引后台清理,与 mm_redis 的精确过期、mm_cachebase 的定时轮询语义略有差异
|
|
409
|
+
- 本模块额外提供 MongoDB 原生 CRUD、事务、索引管理能力,为 mm_redis / mm_cachebase 所不具备
|
|
315
410
|
|
|
316
|
-
##
|
|
411
|
+
## 性能优化
|
|
317
412
|
|
|
318
|
-
|
|
319
|
-
|
|
413
|
+
1. **使用连接池管理器** - 始终通过 `mongodbAdmin` 获取实例,复用连接池
|
|
414
|
+
2. **创建合适的索引** - 为频繁查询的字段创建索引
|
|
415
|
+
3. **使用批量操作** - 多个操作时优先使用 `mset`、`mget`、`addList`
|
|
416
|
+
4. **设置过期时间** - 临时数据使用 `seconds` 参数或 `expire`,配合 TTL 索引自动清理
|
|
417
|
+
5. **优化查询** - 避免全表扫描,使用精确查询条件
|
|
320
418
|
|
|
321
|
-
##
|
|
419
|
+
## 安全考虑
|
|
322
420
|
|
|
323
|
-
-
|
|
421
|
+
- 密码自动 URI 编码,安全传输
|
|
422
|
+
- 内置 `_sanitizeQuery` 查询注入防护,过滤 `$` 操作符注入
|
|
423
|
+
- 敏感配置通过环境变量管理
|
|
424
|
+
- 自动连接重试,提升稳定性
|
|
425
|
+
- 避免在查询中直接使用原始用户输入
|
|
426
|
+
|
|
427
|
+
## 依赖
|
|
428
|
+
|
|
429
|
+
- [mm_logs](https://www.npmjs.com/package/mm_logs) - 日志模块
|
|
430
|
+
- [mongodb](https://www.npmjs.com/package/mongodb) - 官方 MongoDB 驱动
|
|
431
|
+
- [mm_expand](https://www.npmjs.com/package/mm_expand) - 基础类扩展
|
|
432
|
+
|
|
433
|
+
## 兼容性
|
|
434
|
+
|
|
435
|
+
- **Node.js**: >= 14.0.0(推荐 16.0.0+)
|
|
324
436
|
- **MongoDB**: 4.0+
|
|
325
437
|
|
|
326
|
-
##
|
|
438
|
+
## 从 Redis 迁移
|
|
439
|
+
|
|
440
|
+
从 mm_redis 迁移到 mm_mongodb 非常简单,接口完全一致,仅需替换模块与初始化方式:
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
// mm_redis(原)
|
|
444
|
+
// const { redisAdmin } = require('mm_redis');
|
|
445
|
+
// const cache = redisAdmin('default');
|
|
446
|
+
// await cache.open();
|
|
447
|
+
|
|
448
|
+
// mm_mongodb(替换)
|
|
449
|
+
const { mongodbAdmin } = require('mm_mongodb');
|
|
450
|
+
const cache = mongodbAdmin('default');
|
|
451
|
+
await cache.open();
|
|
452
|
+
|
|
453
|
+
// Redis 命令保持不变
|
|
454
|
+
await cache.set('key', 'value');
|
|
455
|
+
const value = await cache.get('key');
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## 贡献
|
|
327
459
|
|
|
328
|
-
|
|
460
|
+
欢迎贡献代码!请遵循以下步骤:
|
|
329
461
|
|
|
330
|
-
1. Fork
|
|
331
|
-
2.
|
|
332
|
-
3.
|
|
333
|
-
4.
|
|
334
|
-
5.
|
|
462
|
+
1. Fork 仓库
|
|
463
|
+
2. 创建功能分支:`git checkout -b feature/your-feature`
|
|
464
|
+
3. 提交更改:`git commit -m "feat: add your feature"`
|
|
465
|
+
4. 推送到分支:`git push origin feature/your-feature`
|
|
466
|
+
5. 提交 Pull Request
|
|
335
467
|
|
|
336
|
-
##
|
|
468
|
+
## 问题与支持
|
|
337
469
|
|
|
338
|
-
|
|
470
|
+
如遇问题或有疑问:
|
|
339
471
|
|
|
340
|
-
- **
|
|
341
|
-
- **NPM Package**: [https://www.npmjs.com/package/mm_mongodb](https://www.npmjs.com/package/mm_mongodb)
|
|
472
|
+
- **Gitee Issues**: [https://gitee.com/qiuwenwu91/mm_mongodb/issues](https://gitee.com/qiuwenwu91/mm_mongodb/issues)
|
|
342
473
|
|
|
343
|
-
##
|
|
474
|
+
## 许可证
|
|
344
475
|
|
|
345
|
-
|
|
476
|
+
本项目基于 MIT 许可证开源 - 详见 [LICENSE](LICENSE) 文件。
|
|
346
477
|
|
|
347
|
-
##
|
|
478
|
+
## 作者
|
|
348
479
|
|
|
349
|
-
|
|
480
|
+
邱文武 - [qww.elins.cn](http://qww.elins.cn)
|
|
350
481
|
|
|
351
|
-
##
|
|
482
|
+
## 致谢
|
|
352
483
|
|
|
353
|
-
|
|
484
|
+
感谢所有贡献者和用户对 mm_mongodb 的改进与支持!
|