mm_redis 1.9.6 → 1.9.7

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.
@@ -4,198 +4,100 @@
4
4
 
5
5
  | 方法名 | Redis | MongoDB | CacheBase | Memory | 状态说明 |
6
6
  |-------|-------|---------|-----------|--------|----------|
7
- | set | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
7
+ | set | ✅ | ✅ | ⚠️ | ⚠️ | Redis和MongoDB正常工作,CacheBase和Memory行为不一致 |
8
8
  | get | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
9
- | del | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
10
- | has | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
11
- | clear | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
12
- | keys | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
13
- | ttl | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
9
+ | del | ✅ | ✅ | ⚠️ | ⚠️ | Redis和MongoDB正常工作,CacheBase和Memory行为不一致 |
10
+ | has | ✅ | ✅ | ✅ | ✅ | 所有方式都支持 |
11
+ | clear | ✅ | ✅ | ⚠️ | ⚠️ | Redis和MongoDB正常工作,CacheBase和Memory行为不一致 |
12
+ | keys | ✅ | ✅ | ✅ | ✅ | 所有方式都支持 |
13
+ | ttl | ⚠️ | ⚠️ | ⚠️ | ⚠️ | 所有方式行为不一致,需要依赖底层实现 |
14
14
  | add | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
15
- | addInt | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
16
- | addStr | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
17
- | sort | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
18
- | list_set | ✅ | ⚠️ | ⚠️ | ⚠️ | 所有方式均通过通用实现支持,但底层可能需要各自实现 |
19
- | list_add | ✅ | ⚠️ | ⚠️ | ⚠️ | 所有方式均通过通用实现支持,但底层可能需要各自实现 |
20
- | list_has | ✅ | ⚠️ | ⚠️ | ⚠️ | 所有方式均通过通用实现支持,但底层可能需要各自实现 |
21
- | list_get | ✅ | ⚠️ | ⚠️ | ⚠️ | 所有方式均通过通用实现支持,但底层可能需要各自实现 |
22
- | list_clear | ✅ | ⚠️ | ⚠️ | ⚠️ | 所有方式均通过通用实现支持,但底层可能需要各自实现 |
15
+ | addInt | ✅ | ❌ | ❌ | ❌ | 仅Redis支持 |
16
+ | addStr | ✅ | ❌ | ❌ | ❌ | 仅Redis支持 |
17
+ | sort | ✅ | ✅ | ❌ | ❌ | Redis和MongoDB支持,其他方式不支持 |
18
+ | exists | ✅ | ✅ | ⚠️ | ⚠️ | Redis和MongoDB正常工作,CacheBase和Memory行为不一致 |
19
+ | incrby | ✅ | ❌ | ❌ | ❌ | 仅Redis支持 |
20
+ | decrby | ✅ | ❌ | ❌ | ❌ | 仅Redis支持 |
21
+ | addFloat | ✅ | ❌ | ❌ | ❌ | 仅Redis支持 |
23
22
 
24
23
  ## Redis兼容方法 - 哈希表操作
25
24
 
26
25
  | 方法名 | Redis | MongoDB | CacheBase | Memory | 状态说明 |
27
26
  |-------|-------|---------|-----------|--------|----------|
28
- | hset | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
27
+ | hset | ✅ | ✅ | ⚠️ | ⚠️ | Redis和MongoDB正常工作,CacheBase和Memory行为不一致 |
29
28
  | hget | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
30
- | hgetall | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
31
- | hdel | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
32
- | hmset | ✅ | ✅ | ✅ | ✅ | 哈希表批量设置,所有方式均正常工作 |
33
- | hmget | ✅ | ✅ | ✅ | ✅ | 哈希表批量获取,所有方式均正常工作 |
34
- | hlen | ✅ | ✅ | ✅ | ✅ | 获取哈希表长度,所有方式均正常工作 |
29
+ | hgetall | ✅ | ✅ | ✅ | ✅ | 所有方式都支持 |
30
+ | hdel | ✅ | ✅ | ✅ | ✅ | 所有方式都支持 |
31
+ | hmset | ✅ | ✅ | ❌ | ❌ | Redis和MongoDB支持,其他方式不支持 |
32
+ | hmget | ❌ | ✅ | ❌ | ❌ | 仅MongoDB支持 |
33
+ | hlen | ✅ | ✅ | ❌ | ❌ | Redis和MongoDB支持,其他方式不支持 |
35
34
 
36
35
  ## Redis兼容方法 - 其他常用命令
37
36
 
38
37
  | 方法名 | Redis | MongoDB | CacheBase | Memory | 状态说明 |
39
38
  |-------|-------|---------|-----------|--------|----------|
40
- | expire | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
41
- | incr | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作,返回正确数值 |
42
- | decr | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作,返回正确数值 |
43
- | exists | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
44
- | mget | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作,返回正确的结果数组 |
45
- | mset | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
46
- | pexpire | ✅ | ✅ | ✅ | ✅ | 毫秒级过期时间设置,所有方式均正常工作 |
47
- | persist | ✅ | ✅ | ✅ | ✅ | 移除过期时间,所有方式均正常工作 |
48
- | strlen | ✅ | ✅ | ✅ | ✅ | 字符串长度获取,所有方式均正常工作 |
49
- | type | ✅ | ✅ | ✅ | ✅ | 数据类型检测,所有方式均正常工作 |
50
- | rename | ✅ | ✅ | ✅ | ✅ | 键重命名,所有方式均正常工作 |
39
+ | expire | ✅ | ✅ | ✅ | ✅ | 所有方式都支持 |
40
+ | incr | ✅ | ✅ | ✅ | ✅ | 所有方式都支持 |
41
+ | decr | ✅ | ❌ | ❌ | ❌ | 仅Redis支持 |
42
+ | mget | ✅ | ✅ | ❌ | ❌ | Redis和MongoDB支持,其他方式不支持 |
43
+ | mset | ✅ | ✅ | ❌ | ❌ | Redis和MongoDB支持,其他方式不支持 |
44
+ | pexpire | ❌ | ❌ | ❌ | ❌ | 所有方式都不支持 |
45
+ | persist | ✅ | ❌ | ✅ | ✅ | Redis、CacheBase和Memory支持,MongoDB不支持 |
46
+ | strlen | ✅ | ❌ | ✅ | ❌ | Redis和CacheBase支持,其他方式不支持 |
47
+ | type | ❌ | ❌ | ❌ | ❌ | 所有方式都不支持 |
48
+ | rename | ❌ | ❌ | ❌ | ❌ | 所有方式都不支持 |
51
49
 
52
50
  ## Redis兼容方法 - 集合操作
53
51
 
54
52
  | 方法名 | Redis | MongoDB | CacheBase | Memory | 状态说明 |
55
53
  |-------|-------|---------|-----------|--------|----------|
56
- | sadd | ✅ | ✅ | ✅ | ✅ | 集合添加元素,所有方式均正常工作 |
57
- | srem | ✅ | ✅ | ✅ | ✅ | 集合移除元素,所有方式均正常工作 |
58
- | sismember | ✅ | ✅ | ✅ | ✅ | 检查元素是否在集合中,所有方式均正常工作 |
59
- | smembers | ✅ | ✅ | ✅ | ✅ | 获取集合所有元素,所有方式均正常工作 |
54
+ | sadd | ✅ | ❌ | ❌ | ❌ | 仅Redis支持 |
55
+ | srem | ✅ | ❌ | ❌ | ❌ | 仅Redis支持 |
56
+ | sismember | ✅ | ✅ | ❌ | ❌ | Redis和MongoDB支持,其他方式不支持 |
57
+ | smembers | ✅ | ✅ | ❌ | ❌ | Redis和MongoDB支持,其他方式不支持 |
60
58
 
61
59
  ## 数组/列表操作方法
62
60
 
63
61
  | 方法名 | Redis | MongoDB | CacheBase | Memory | 状态说明 |
64
62
  |-------|-------|---------|-----------|--------|----------|
65
- | list_set | ✅ | ✅ | ✅ | ✅ | 已实现并测试通过 |
66
- | list_add | ✅ | ✅ | ✅ | ✅ | 已实现并测试通过 |
67
- | list_has | ✅ | ✅ | ✅ | ✅ | 已实现并测试通过 |
68
- | list_get | ✅ | ✅ | ✅ | ✅ | 已实现并测试通过 |
69
- | list_clear | ✅ | ✅ | ✅ | ✅ | 已实现并测试通过 |
63
+ | list_set | ✅ | ❌ | ⚠️ | ⚠️ | Redis正常工作,CacheBase和Memory行为不一致,MongoDB不支持 |
64
+ | list_add | ✅ | ❌ | ⚠️ | ⚠️ | Redis正常工作,CacheBase和Memory行为不一致,MongoDB不支持 |
65
+ | list_has | ✅ | ❌ | ⚠️ | ⚠️ | Redis正常工作,CacheBase和Memory行为不一致,MongoDB不支持 |
66
+ | list_get | ✅ | ❌ | ⚠️ | ⚠️ | Redis正常工作,CacheBase和Memory行为不一致,MongoDB不支持 |
67
+ | list_clear | ✅ | ❌ | ✅ | ✅ | MongoDB不支持,其他方式支持 |
70
68
 
71
69
  ## 兼容性报告总结
72
70
 
73
- 基于全面测试,以下是四种缓存方式的兼容性报告:
71
+ 基于实际功能测试,以下是四种缓存方式的兼容性报告:
74
72
 
75
73
  | 方法类别 | CacheBase | Redis | MongoDB | Memory | 状态说明 |
76
74
  |---------|-----------|-------|---------|--------|----------|
77
- | 基础键值操作 | ✅ | ✅ | ✅ | ✅ | 所有方式均正常工作 |
78
- | 数组操作 | ⚠️ | ✅ | ⚠️ | ⚠️ | 所有方式通过通用实现支持,但底层可能需要各自实现 |
79
- | 过期时间操作 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
80
- | 原子递增操作 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
81
- | 批量操作 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
82
- | 哈希表基本操作 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
83
- | 哈希表批量操作 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
84
- | 集合操作 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
85
- | 字符串操作 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
86
- | 类型检测 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
87
- | 键重命名 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
88
- | 清理操作 | ✅ | ✅ | ✅ | ✅ | 全兼容 |
75
+ | 基础键值操作 | ⚠️ | ✅ | ✅ | ⚠️ | Redis和MongoDB正常工作,CacheBase和Memory部分方法行为不一致 |
76
+ | 数组操作 | ⚠️ | ✅ | ❌ | ⚠️ | Redis完全支持,CacheBase和Memory行为不一致,MongoDB不支持 |
77
+ | 过期时间操作 | ✅ | ✅ | ✅ | ✅ | 基本过期时间操作支持,但MongoDB不支持persist |
78
+ | 原子递增操作 | ✅ | ✅ | ✅ | ✅ | incr支持,但decr、incrby、decrby只有Redis支持 |
79
+ | 批量操作 | ❌ | ✅ | ✅ | ❌ | Redis和MongoDB支持,其他方式不支持 |
80
+ | 哈希表基本操作 | ✅ | ✅ | ✅ | ✅ | 基本操作均支持 |
81
+ | 哈希表批量操作 | ❌ | ✅ | ✅ | ❌ | Redis和MongoDB支持,其他方式不支持 |
82
+ | 集合操作 | ❌ | ✅ | ⚠️ | ❌ | Redis完整支持,MongoDB支持部分方法,其他方式不支持 |
83
+ | 字符串操作 | ⚠️ | ✅ | ❌ | ❌ | Redis完全支持,CacheBase支持部分方法,其他方式不支持 |
84
+ | 类型检测 | ❌ | ❌ | ❌ | ❌ | 所有方式都不支持 |
85
+ | 键重命名 | ❌ | ❌ | ❌ | ❌ | 所有方式都不支持 |
86
+ | 清理操作 | ✅ | ✅ | ✅ | ✅ | clear操作正常工作 |
89
87
 
90
88
  ### 测试统计
91
89
 
92
- - **总测试用例**:43个
93
- - **通过数**:43个测试用例
94
- - **整体成功率**:100%
95
-
96
- ### 所有缓存方式都支持的功能:
97
- - **核心键值操作**:set, get, exists, del, has, keys, sort(add除外)
98
- - **哈希表操作**:hset, hget, hgetall, hdel, hmset, hmget, hlen
99
- - **过期时间操作**:expire, ttl, persist, pexpire
100
- - **原子递增操作**:incr, decr
101
- - **批量操作**:mset, mget
102
- - **集合操作**:sadd, srem, sismember, smembers
103
- - **字符串操作**:strlen
104
- - **类型检测**:type
105
- - **键重命名**:rename
106
- - **清理操作**:clear
107
- - **数组操作**:list_set, list_add, list_has, list_get, list_clear (通过通用实现)
108
-
109
- ### CacheBase 缓存方式:
110
- - **测试状态**:✅ 所有12个方法类别测试通过
111
- - **兼容性**:完全兼容Redis接口
112
- - **返回值**:所有操作都返回符合预期的结果
113
- - **优点**:无需外部依赖,性能优秀,返回值一致性高,支持持久化
114
-
115
- ### MongoDB 缓存方式:
116
- - **测试状态**:✅ 所有12个方法类别测试通过
117
- - **兼容性**:完全兼容Redis接口
118
- - **返回值**:所有操作都返回正确的结果,包括incr/decr返回数值,mget返回正确的结果数组
119
- - **优点**:支持持久化存储,重启后数据不会丢失
120
-
121
- ### Redis 缓存方式:
122
- - **测试状态**:✅ 所有12个方法类别测试通过
123
- - **通过率**:100%
124
- - **优点**:支持持久化存储,重启后数据不会丢失,支持分布式缓存
125
- - **注意事项**:已修复认证配置问题,现在可以正常使用
126
-
127
- ### Memory 缓存方式:
128
- - **测试状态**:✅ 所有12个方法类别测试通过
129
- - **兼容性**:完全兼容Redis接口
130
- - **优点**:最高性能,纯内存操作,无I/O开销
131
- - **缺点**:不支持持久化,程序重启后数据会丢失
132
-
133
- ### 已确认移除的方法:
134
- - hexists:测试中报错 "TypeError: cache.hexists is not a function"
135
- - hkeys、hvals:已移除
136
-
137
- ## 最佳使用建议
138
-
139
- 1. **Memory 方式**(推荐用于内存临时缓存):
140
- - 所有功能完全兼容,返回值符合预期
141
- - 性能最优,纯内存操作无I/O开销
142
- - 适合短期缓存、临时数据缓存场景
143
- - 不需要额外的外部服务依赖
144
- - 程序重启后数据会丢失
145
-
146
- 2. **CacheBase 方式**(推荐用于单机持久化缓存):
147
- - 所有功能完全兼容,返回值符合预期
148
- - 性能稳定,在多次依赖更新中保持一致表现
149
- - 适合需要持久化但不需要分布式的场景
150
- - 不需要额外的外部服务依赖
151
- - 程序重启后数据不会丢失
152
-
153
- 3. **Redis 方式**(推荐用于分布式持久化缓存):
154
- - 已完全修复所有兼容性问题
155
- - 支持分布式缓存,数据可在多实例间共享
156
- - 适合需要高性能、分布式缓存或Redis特有功能的场景
157
- - 需要外部Redis服务
158
- - 程序重启后数据不会丢失
159
-
160
- 4. **MongoDB 方式**(推荐用于已有MongoDB环境的场景):
161
- - 所有方法均完全兼容Redis接口
162
- - 所有操作都返回正确的结果,包括incr/decr和mget
163
- - 适合需要持久化缓存且已有MongoDB环境的场景
164
- - 提供了数据持久化保障
165
- - 需要外部MongoDB服务
166
- - 程序重启后数据不会丢失
167
-
168
- ## 实际使用建议
169
-
170
- 1. **开发环境**:推荐使用Memory或CacheBase,无需额外配置,使用简单
171
-
172
- 2. **生产环境**:
173
- - 如果需要极致性能且数据不敏感:使用Memory
174
- - 如果需要单机持久化:使用CacheBase
175
- - 如果需要高性能、分布式缓存:使用Redis
176
- - 如果需要缓存持久化且已有MongoDB环境:使用MongoDB
177
-
178
- 3. **跨环境部署**:
179
- - 可以根据环境配置自动切换缓存方式
180
- - 所有缓存方式的返回值特性一致,可以放心切换
181
-
182
- 4. **性能考虑**:
183
- - Memory:纯内存操作,最快但不持久化
184
- - CacheBase:文件系统操作,性能优秀且支持持久化
185
- - Redis:网络操作,但性能优秀,支持持久化和分布式
186
- - MongoDB:网络+磁盘操作,性能相对较低但支持持久化
187
-
188
- ## 兼容性维护建议
189
-
190
- 1. **文档维护**:
191
- - 更新README.md,明确标注支持的方法和各缓存方式的特点
192
-
193
- 2. **测试维护**:
194
- - 保留test.js作为标准全面兼容性测试
195
- - 在发布新版本前运行全面测试
196
-
197
- 3. **错误处理**:
198
- - 对已移除的方法添加更友好的错误提示
199
- - 考虑在代码中添加方法兼容性检测
200
-
201
- 注:✅ 完全兼容/功能正常 | ⚠️ 功能正常但有注意事项 | ❌ 不兼容/未实现/已移除
90
+ - **总测试用例**:43个方法
91
+ - **通过数**:不同缓存方式通过数量不同(Redis最多,MongoDB次之)
92
+ - **整体支持情况**:
93
+ - Redis:支持28个方法,不支持9个,行为不一致6个
94
+ - MongoDB:支持23个方法,不支持15个,行为不一致0个
95
+ - CacheBase:支持15个方法,不支持17个,行为不一致7个
96
+ - Memory:支持15个方法,不支持17个,行为不一致7个
97
+ - **注意事项**:
98
+ 1. CacheBase和Memory的行为完全一致
99
+ 2. Redis完全支持列表操作方法
100
+ 3. MongoDB完全不支持列表操作方法(list_*)
101
+ 4. addInt、addStr、incrby、decrby和addFloat仅Redis支持
102
+ 5. 基础键值操作在Redis和MongoDB中表现最佳
103
+ 6. 集合操作只有Redis完整支持
package/index.js CHANGED
@@ -512,50 +512,26 @@ Redis.prototype.isConnected = function () {
512
512
  * @param {Number} seconds 秒, 为空则查询有效时长
513
513
  * @return {Promise<Number|Boolean>} 查询时长或设置结果, 时长为-1表示永不过期
514
514
  */
515
- Redis.prototype.ttl = async function (key, seconds) {
515
+ Redis.prototype.ttl = async function (key) {
516
516
  try {
517
517
  const connection = await this._getConnection();
518
518
  try {
519
519
  key = this.applyPrefix(key);
520
- if (seconds !== undefined) {
521
- if (seconds > 0) {
522
- // 设置过期时间
523
- if (typeof connection.expire === 'function') {
524
- return await connection.expire(key, seconds);
525
- } else if (typeof connection.execute === 'function') {
526
- return await connection.execute('EXPIRE', [key, seconds]);
527
- } else {
528
- $.log.error('[Redis] 连接对象不支持expire操作');
529
- return false;
530
- }
531
- } else {
532
- // 移除过期时间
533
- if (typeof connection.persist === 'function') {
534
- return await connection.persist(key);
535
- } else if (typeof connection.execute === 'function') {
536
- return await connection.execute('PERSIST', [key]);
537
- } else {
538
- $.log.error('[Redis] 连接对象不支持persist操作');
539
- return false;
540
- }
541
- }
520
+ // 只获取过期时间,符合ttl的标准功能
521
+ if (typeof connection.ttl === 'function') {
522
+ return await connection.ttl(key);
523
+ } else if (typeof connection.execute === 'function') {
524
+ return await connection.execute('TTL', [key]);
542
525
  } else {
543
- // 获取过期时间
544
- if (typeof connection.ttl === 'function') {
545
- return await connection.ttl(key);
546
- } else if (typeof connection.execute === 'function') {
547
- return await connection.execute('TTL', [key]);
548
- } else {
549
- $.log.error('[Redis] 连接对象不支持ttl操作');
550
- return -2; // 默认返回键不存在
551
- }
526
+ $.log.error('[Redis] 连接对象不支持ttl操作');
527
+ return -2; // 默认返回键不存在
552
528
  }
553
529
  } finally {
554
530
  this._recycleConnection(connection);
555
531
  }
556
532
  } catch (err) {
557
- $.log.error(`[Redis] 设置或获取过期时间失败 [${key}]:`, err);
558
- return seconds !== undefined ? false : -2; // -2表示出错
533
+ $.log.error(`[Redis] 获取过期时间失败 [${key}]:`, err);
534
+ return -2; // -2表示出错
559
535
  }
560
536
  };
561
537
 
@@ -2572,7 +2548,8 @@ Redis.prototype.sinter = async function (keys) {
2572
2548
  const connection = await this._getConnection();
2573
2549
  try {
2574
2550
  const prefixedKeys = keys.map(key => this.applyPrefix(key));
2575
- const result = await connection.execute('sinter', ...prefixedKeys);
2551
+ // 使用Redis v4正确的方法
2552
+ const result = await connection.sInter(...prefixedKeys);
2576
2553
  return result || [];
2577
2554
  } finally {
2578
2555
  this._recycleConnection(connection);
@@ -2593,7 +2570,8 @@ Redis.prototype.sunion = async function (keys) {
2593
2570
  const connection = await this._getConnection();
2594
2571
  try {
2595
2572
  const prefixedKeys = keys.map(key => this.applyPrefix(key));
2596
- const result = await connection.execute('sunion', ...prefixedKeys);
2573
+ // 使用Redis v4正确的方法
2574
+ const result = await connection.sUnion(...prefixedKeys);
2597
2575
  return result || [];
2598
2576
  } finally {
2599
2577
  this._recycleConnection(connection);
@@ -2614,7 +2592,8 @@ Redis.prototype.sdiff = async function (keys) {
2614
2592
  const connection = await this._getConnection();
2615
2593
  try {
2616
2594
  const prefixedKeys = keys.map(key => this.applyPrefix(key));
2617
- const result = await connection.execute('sdiff', ...prefixedKeys);
2595
+ // 使用Redis v4正确的方法
2596
+ const result = await connection.sDiff(...prefixedKeys);
2618
2597
  return result || [];
2619
2598
  } finally {
2620
2599
  this._recycleConnection(connection);
@@ -2763,21 +2742,38 @@ Redis.prototype.zrange = async function (key, start, stop, withScores = false) {
2763
2742
  const connection = await this._getConnection();
2764
2743
  try {
2765
2744
  const prefixedKey = this.applyPrefix(key);
2766
- const result = await connection.zRange(prefixedKey, start, stop, { WITHSCORES: withScores });
2767
-
2768
- // 如果需要返回分数,将结果转换为对象数组
2769
- if (withScores && result && result.length) {
2770
- const formattedResult = [];
2771
- for (let i = 0; i < result.length; i += 2) {
2772
- formattedResult.push({
2773
- member: result[i],
2774
- score: parseFloat(result[i + 1])
2775
- });
2745
+
2746
+ if (withScores) {
2747
+ // 对于需要分数的情况,使用更可靠的方法
2748
+ try {
2749
+ // 直接获取所有成员的分数
2750
+ const members = await connection.zRange(prefixedKey, start, stop);
2751
+ const results = [];
2752
+
2753
+ // 为每个成员单独获取分数,确保准确性
2754
+ for (const member of members) {
2755
+ try {
2756
+ let score = await connection.zScore(prefixedKey, member);
2757
+ score = score ? parseFloat(score) : 0;
2758
+ results.push({ member, score });
2759
+ } catch (scoreErr) {
2760
+ // 如果获取分数失败,设置默认值0
2761
+ results.push({ member, score: 0 });
2762
+ $.log.warn(`[Redis] 获取成员 ${member} 分数失败:`, scoreErr);
2763
+ }
2764
+ }
2765
+
2766
+ return results;
2767
+ } catch (err) {
2768
+ $.log.error(`[Redis] 使用备用方法获取有序集合范围失败 [${prefixedKey}]:`, err);
2769
+ // 回退到简单列表
2770
+ return [];
2776
2771
  }
2777
- return formattedResult;
2772
+ } else {
2773
+ // 不需要分数的情况
2774
+ const result = await connection.zRange(prefixedKey, start, stop);
2775
+ return result || [];
2778
2776
  }
2779
-
2780
- return result || [];
2781
2777
  } finally {
2782
2778
  this._recycleConnection(connection);
2783
2779
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_redis",
3
- "version": "1.9.6",
3
+ "version": "1.9.7",
4
4
  "description": "高效的Redis客户端封装库,提供连接池管理、数据类型操作、错误处理和批量操作优化",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/test.js CHANGED
@@ -252,11 +252,17 @@ async function test() {
252
252
  }
253
253
  }
254
254
 
255
- test().catch(err => {
255
+ test().then(() => {
256
+ // 测试成功完成
257
+ $.log.debug('[Redis] 测试脚本正常退出');
258
+ process.exit(0);
259
+ }).catch(err => {
256
260
  $.log.error('[Redis] 未捕获的错误:', err);
257
261
  process.exit(1);
258
262
  });
259
263
 
264
+ // 添加超时保护,但让Promise处理正常结束
260
265
  setTimeout(() => {
261
- process.exit(0);
266
+ $.log.warn('[Redis] 测试脚本超时');
267
+ process.exit(1);
262
268
  }, 10000); // 延长超时时间以确保所有测试完成
@@ -8,7 +8,7 @@ async function verifyCompatibility() {
8
8
 
9
9
  console.log('开始验证兼容性表格中的所有方法...');
10
10
 
11
- // 1. 测试核心缓存方法 - 已标记为完全兼容的方法
11
+ // 1. 测试核心缓存方法
12
12
  console.log('\n=== 核心缓存方法验证 ===');
13
13
  await testBasicMethods(rs);
14
14
 
@@ -20,9 +20,13 @@ async function verifyCompatibility() {
20
20
  console.log('\n=== 列表操作方法验证 ===');
21
21
  await testListMethods(rs);
22
22
 
23
- // 4. 清理并关闭连接
23
+ // 4. 测试哈希表相关方法
24
+ console.log('\n=== 哈希表方法验证 ===');
25
+ await testHashMethods(rs);
26
+
27
+ // 5. 清理并关闭连接
24
28
  console.log('\n清理测试数据...');
25
- await rs.del(["list_test_key"]);
29
+ await rs.del(["list_test_key", "hash_test_key", "test_counter", "test_string"]);
26
30
 
27
31
  await rs.close();
28
32
  console.log('\n所有验证完成!');
@@ -35,7 +39,7 @@ async function verifyCompatibility() {
35
39
 
36
40
  async function testBasicMethods(rs) {
37
41
  try {
38
- // 测试文档中所有标记为✅的核心方法
42
+ // 测试核心方法
39
43
  const methods = [
40
44
  { name: 'set', result: await rs.set('temp_key', 'test_value') },
41
45
  { name: 'get', result: await rs.get('temp_key') },
@@ -44,18 +48,20 @@ async function testBasicMethods(rs) {
44
48
  { name: 'ttl', result: await rs.ttl('temp_key') },
45
49
  { name: 'persist', result: await rs.persist('temp_key') },
46
50
  { name: 'add', result: await rs.add('new_temp_key', 'new_value') },
47
- { name: 'addInt', result: await rs.addInt('counter', 10) },
48
- { name: 'addStr', result: await rs.addStr('string_key', 'text') },
51
+ { name: 'addInt', result: await rs.addInt('test_counter', 10) },
52
+ { name: 'addStr', result: await rs.addStr('test_string', 'text') },
49
53
  { name: 'keys', result: await rs.keys('*') },
50
54
  { name: 'clear', result: await rs.clear('temp_key') },
51
55
  { name: 'del', result: await rs.del('new_temp_key') },
52
- { name: 'incr', result: await rs.incr('counter') },
53
- { name: 'decr', result: await rs.decr('counter') },
54
- { name: 'mget', result: await rs.mget(['counter', 'string_key']) },
56
+ { name: 'incr', result: await rs.incr('test_counter') },
57
+ { name: 'decr', result: await rs.decr('test_counter') },
58
+ { name: 'mget', result: await rs.mget(['test_counter', 'test_string']) },
55
59
  { name: 'mset', result: await rs.mset({batch1: 'value1', batch2: 'value2'}) },
56
- { name: 'expire', result: await rs.expire('counter', 60) },
57
- // { name: 'type', result: await rs.type('counter') },
58
- // { name: 'rename', result: await rs.rename('string_key', 'renamed_string') }
60
+ { name: 'expire', result: await rs.expire('test_counter', 60) },
61
+ { name: 'sort', result: await rs.sort('temp_sort', {order: 'asc'}) },
62
+ { name: 'decrby', result: await rs.decrby('test_counter', 5) },
63
+ { name: 'incrby', result: await rs.incrby('test_counter', 10) },
64
+ { name: 'addFloat', result: await rs.addFloat('test_counter', 0.5) }
59
65
  ];
60
66
 
61
67
  console.log('所有核心方法测试结果:');
@@ -125,6 +131,42 @@ async function testListMethods(rs) {
125
131
  }
126
132
  }
127
133
 
134
+ async function testHashMethods(rs) {
135
+ try {
136
+ // 测试哈希表方法
137
+ const hashKey = 'hash_test_key';
138
+
139
+ console.log('哈希表方法测试结果:');
140
+
141
+ // 测试hset
142
+ const hsetResult = await rs.hset(hashKey, 'field1', 'value1');
143
+ console.log(`- hset: ${typeof hsetResult !== 'undefined' ? '✅ 可用' : '❌ 不可用'}`);
144
+
145
+ // 测试hget
146
+ const hgetResult = await rs.hget(hashKey, 'field1');
147
+ console.log(`- hget: ${hgetResult === 'value1' ? '✅ 可用' : '❌ 不可用'}`);
148
+
149
+ // 测试hmset
150
+ const hmsetResult = await rs.hmset(hashKey, {field2: 'value2', field3: 'value3'});
151
+ console.log(`- hmset: ${typeof hmsetResult !== 'undefined' ? '✅ 可用' : '❌ 不可用'}`);
152
+
153
+ // 测试hgetall
154
+ const hgetallResult = await rs.hgetall(hashKey);
155
+ console.log(`- hgetall: ${typeof hgetallResult === 'object' ? '✅ 可用' : '❌ 不可用'}`);
156
+
157
+ // 测试hdel
158
+ const hdelResult = await rs.hdel(hashKey, 'field1');
159
+ console.log(`- hdel: ${typeof hdelResult !== 'undefined' ? '✅ 可用' : '❌ 不可用'}`);
160
+
161
+ // 测试hlen
162
+ const hlenResult = await rs.hlen(hashKey);
163
+ console.log(`- hlen: ${typeof hlenResult === 'number' ? '✅ 可用' : '❌ 不可用'}`);
164
+
165
+ } catch (error) {
166
+ console.error('哈希表方法测试失败:', error);
167
+ }
168
+ }
169
+
128
170
  verifyCompatibility();
129
171
 
130
172
  setTimeout(() => {