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.
Files changed (4) hide show
  1. package/README.md +392 -261
  2. package/index.js +2839 -3978
  3. package/package.json +4 -4
  4. package/test.js +807 -285
package/README.md CHANGED
@@ -1,96 +1,90 @@
1
1
  # mm_mongodb
2
2
 
3
- A powerful MongoDB database operation module with Redis-compatible API, connection pooling, and event system support.
3
+ 一个基于 MongoDB 的数据库操作模块,提供与 **mm_redis / mm_cachebase 完全对齐** 的异步缓存操作接口,同时保留 MongoDB 原生的 CRUD、事务与索引管理能力,支持连接池、TTL 自动过期和冒号风格的事件通知系统。
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/mm_mongodb.svg)](https://www.npmjs.com/package/mm_mongodb)
6
- [![License](https://img.shields.io/npm/l/mm_mongodb.svg)](https://github.com/qwwang/mm_mongodb/blob/main/LICENSE)
7
- [![Node.js Version](https://img.shields.io/badge/node.js-%3E%3D14.0.0-brightgreen.svg)](https://nodejs.org/)
5
+ ## 项目信息
8
6
 
9
- ## Features
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
- - ✅ **Complete Redis-compatible API** - Use familiar Redis commands with MongoDB
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
- ## Installation
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
- ## Quick Start
30
+ ## 基本使用
28
31
 
29
- ### Basic Usage
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
- // Create MongoDB instance
35
- const db = new MongoDB();
36
-
37
- // Connect to database
38
- await db.open();
37
+ // 通过连接池管理器获取实例(推荐)
38
+ const cache = mongodbAdmin('myapp');
39
39
 
40
- // Create collection
41
- await db.createTable('users');
40
+ async function example() {
41
+ // 连接数据库
42
+ await cache.open();
42
43
 
43
- // Insert data
44
- await db.insert('users', { name: 'John', age: 25 });
44
+ // 设置缓存,10 秒过期
45
+ await cache.set("name", "张三", 10);
45
46
 
46
- // Query data
47
- const result = await db.get('users', { name: 'John' });
47
+ // 获取缓存
48
+ const value = await cache.get("name");
49
+ $.log.debug(value); // 输出: 张三
48
50
 
49
- // Update data
50
- await db.set('users', { name: 'John' }, { age: 26 });
51
+ // 删除缓存(返回删除数量)
52
+ const deleted = await cache.del("name");
53
+ $.log.debug(deleted); // 输出: 1
51
54
 
52
- // Delete data
53
- await db.del('users', { name: 'John' });
54
-
55
- // Close connection
56
- await db.close();
55
+ // 关闭连接
56
+ await cache.close();
57
+ }
57
58
  ```
58
59
 
59
- ### Redis-Compatible API
60
+ ### MongoDB 原生 API
60
61
 
61
62
  ```javascript
62
- const { mongodb_cache_admin } = require('mm_mongodb');
63
+ const { MongoDB } = require('mm_mongodb');
63
64
 
64
- // Get Redis-compatible cache instance
65
- const cache = mongodb_cache_admin('default');
65
+ const db = new MongoDB({ database: 'mydb' });
66
+ await db.open();
66
67
 
67
- // Connect to database
68
- await cache.open();
68
+ // 插入文档
69
+ await db.addObj({ key: 'user:1', name: 'John', age: 25 });
69
70
 
70
- // Use Redis-style commands
71
- await cache.set('name', 'Redis Compatible');
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
- // Hash operations
76
- await cache.hset('user:1', 'name', 'John');
77
- const name = await cache.hget('user:1', 'name');
74
+ // 更新文档
75
+ await db.setting({ key: 'user:1' }, { age: 26 });
78
76
 
79
- // List operations
80
- await cache.lpush('messages', 'Hello');
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
- // Set operations
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
- ## Configuration
83
+ ## 配置
90
84
 
91
- ### Environment Variables
85
+ ### 环境变量
92
86
 
93
- Configure MongoDB connection via environment variables:
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
- ### Configuration File
106
-
107
- Create `config.tpl.json` in your project root:
108
-
109
- ```json
110
- {
111
- "mongodb": {
112
- "url": "mongodb://username:password@host:port/database",
113
- "poolSize": 20,
114
- "minPoolSize": 5,
115
- "maxPoolSize": 100,
116
- "waitQueueTimeoutMS": 10000,
117
- "connectTimeoutMS": 10000,
118
- "socketTimeoutMS": 45000,
119
- "authSource": "admin",
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
- ## API Documentation
116
+ ## 连接管理
117
+
118
+ 与 mm_redis / mm_cachebase 一致的连接管理接口:
126
119
 
127
- ### Core MongoDB Class
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
- #### Constructor
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
- new MongoDB(scope = 'default', dir = '', config = {})
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
- #### Connection Management
136
-
137
- - `open()` - Connect to database
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 db = new MongoDB();
253
- await db.open();
308
+ const cache = mongodbAdmin('myapp');
309
+ await cache.open();
254
310
 
255
- // Listen to operation events
256
- db.on('set_before', (data) => {
257
- console.log('Before set operation:', data);
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
- db.on('set_after', (data) => {
261
- console.log('After set operation:', data);
318
+ // 监听 set 后置事件
319
+ cache.on('set:after', (data) => {
320
+ $.log.debug('设置完成:', data.key, '成功:', data.success);
262
321
  });
263
322
 
264
- db.on('set_error', (data) => {
265
- console.error('Set operation error:', data);
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
- ### Supported Events
329
+ ### 支持的事件
273
330
 
274
- - `{action}_before` - Before operation execution
275
- - `{action}_after` - After operation completion
276
- - `{action}_error` - On operation error
331
+ 所有公共方法均有对应事件,命名规则为 `方法名:before` / `方法名:after` / `方法名:error`:
277
332
 
278
- Where `{action}` can be: `set`, `del`, `add`, `hset`, `lpush`, `sadd`, `zadd`, etc.
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
- ## Performance Optimization
345
+ > **注意**:`destroy` 事件不可使用冒号格式(`destroy:after`),因为 mm_expand 的 `Base.emit` 会将冒号格式解释为 do 流程的阶段,会触发 `_destroyCore`。
281
346
 
282
- 1. **Use Connection Pooling** - Always use `mongodb_admin` or `mongodb_cache_admin` for connection pooling
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
- ## Migration from Redis
349
+ ### 哈希表操作
289
350
 
290
- Migrating from Redis to mm_mongodb is straightforward:
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
- // Redis client (original)
294
- // const redis = require('redis');
295
- // const client = redis.createClient();
296
- // await client.connect();
297
-
298
- // mm_mongodb (replacement)
299
- const { mongodb_cache_admin } = require('mm_mongodb');
300
- const cache = mongodb_cache_admin('default');
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
- // Your Redis commands remain the same
304
- await cache.set('key', 'value');
305
- const value = await cache.get('key');
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
- ## Security Considerations
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
- - Passwords are automatically URI-encoded for secure transmission
311
- - Built-in query injection protection
312
- - Environment variables for sensitive configuration
313
- - Automatic connection retry for improved stability
314
- - Avoid using raw user input in queries
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
- ## Dependencies
411
+ ## 性能优化
317
412
 
318
- - [mm_logs](https://www.npmjs.com/package/mm_logs) - Logging module
319
- - [mongodb](https://www.npmjs.com/package/mongodb) - Official MongoDB driver
413
+ 1. **使用连接池管理器** - 始终通过 `mongodbAdmin` 获取实例,复用连接池
414
+ 2. **创建合适的索引** - 为频繁查询的字段创建索引
415
+ 3. **使用批量操作** - 多个操作时优先使用 `mset`、`mget`、`addList`
416
+ 4. **设置过期时间** - 临时数据使用 `seconds` 参数或 `expire`,配合 TTL 索引自动清理
417
+ 5. **优化查询** - 避免全表扫描,使用精确查询条件
320
418
 
321
- ## Compatibility
419
+ ## 安全考虑
322
420
 
323
- - **Node.js**: >= 14.0.0 (Recommended: 16.0.0+)
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
- ## Contributing
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
- We welcome contributions! Please follow these steps:
460
+ 欢迎贡献代码!请遵循以下步骤:
329
461
 
330
- 1. Fork the repository
331
- 2. Create a feature branch: `git checkout -b feature/your-feature`
332
- 3. Commit your changes: `git commit -m "feat: add your feature"`
333
- 4. Push to the branch: `git push origin feature/your-feature`
334
- 5. Submit a pull request
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
- ## Issues and Support
468
+ ## 问题与支持
337
469
 
338
- If you encounter any issues or have questions:
470
+ 如遇问题或有疑问:
339
471
 
340
- - **GitHub Issues**: [https://github.com/qwwang/mm_mongodb/issues](https://github.com/qwwang/mm_mongodb/issues)
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
- ## License
474
+ ## 许可证
344
475
 
345
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
476
+ 本项目基于 MIT 许可证开源 - 详见 [LICENSE](LICENSE) 文件。
346
477
 
347
- ## Author
478
+ ## 作者
348
479
 
349
- Qiu Wenwu - [qww.elins.cn](http://qww.elins.cn)
480
+ 邱文武 - [qww.elins.cn](http://qww.elins.cn)
350
481
 
351
- ## Acknowledgments
482
+ ## 致谢
352
483
 
353
- Thanks to all contributors and users who have helped improve mm_mongodb!
484
+ 感谢所有贡献者和用户对 mm_mongodb 的改进与支持!