mm_mongodb 1.4.5 → 1.4.6

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/index.js CHANGED
@@ -154,6 +154,1139 @@ class MongoDB extends eventEmitter {
154
154
  // 所有重试都失败,抛出最后一个错误
155
155
  throw lastError;
156
156
  }
157
+
158
+ /**
159
+ * @description Redis兼容API - 获取缓存值
160
+ * @param {String} key 键名
161
+ * @return {Promise<any>} 缓存值
162
+ */
163
+ async get(key) {
164
+ try {
165
+ const result = await this.findOne({ key: key });
166
+ return result ? result.value : null;
167
+ } catch (error) {
168
+ $.log.error('[MongoDB] 获取缓存失败:', error);
169
+ return null;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * @description Redis兼容API - 清空所有缓存
175
+ * @return {Promise<Boolean>} 是否成功
176
+ */
177
+ async clear() {
178
+ try {
179
+ await this.del({});
180
+ return true;
181
+ } catch (error) {
182
+ $.log.error('[MongoDB] 清空缓存失败:', error);
183
+ return false;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * @description Redis兼容API - 获取所有键
189
+ * @param {String} pattern 匹配模式
190
+ * @return {Promise<Array>} 键名数组
191
+ */
192
+ async keys(pattern = '*') {
193
+ try {
194
+ let query = {};
195
+ if (pattern !== '*') {
196
+ // 简单的通配符支持
197
+ const regexPattern = pattern.replace(/\*/g, '.*').replace(/\?/g, '.');
198
+ query.key = { $regex: new RegExp(`^${regexPattern}$`) };
199
+ }
200
+ const results = await this.find(query, { projection: { key: 1 } });
201
+ return results.map(item => item.key);
202
+ } catch (error) {
203
+ $.log.error('[MongoDB] 获取键列表失败:', error);
204
+ return [];
205
+ }
206
+ }
207
+
208
+ /**
209
+ * @description Redis兼容API - 设置过期时间
210
+ * @param {String} key 键名
211
+ * @param {Number} seconds 过期时间(秒)
212
+ * @return {Promise<Boolean>} 是否成功
213
+ */
214
+ async ttl(key, seconds) {
215
+ try {
216
+ if (!seconds && seconds !== 0) {
217
+ // 获取剩余过期时间
218
+ const item = await this.findOne({ key: key });
219
+ if (!item || !item.expireAt) return -1;
220
+ const ttl = Math.floor((item.expireAt.getTime() - Date.now()) / 1000);
221
+ return ttl > 0 ? ttl : -2; // -2 表示已过期
222
+ } else {
223
+ // 设置过期时间
224
+ const expireAt = seconds === 0 ? null : new Date(Date.now() + seconds * 1000);
225
+ const result = await this.set({ key: key }, { expireAt: expireAt });
226
+ return result.modifiedCount > 0;
227
+ }
228
+ } catch (error) {
229
+ $.log.error('[MongoDB] 设置TTL失败:', error);
230
+ return false;
231
+ }
232
+ }
233
+
234
+ /**
235
+ * @description Redis兼容API - 添加缓存
236
+ * @param {String} key 键名
237
+ * @param {*} value 值
238
+ * @param {Number} expire 过期时间(秒)
239
+ * @return {Promise<Boolean>} 是否成功
240
+ */
241
+ async add(key, value, expire) {
242
+ try {
243
+ const data = {
244
+ key: key,
245
+ value: value
246
+ };
247
+
248
+ if (expire) {
249
+ data.expireAt = new Date(Date.now() + expire * 1000);
250
+ }
251
+
252
+ // 检查是否已存在
253
+ const existing = await this.findOne({ key: key });
254
+ if (existing) {
255
+ await this.set({ key: key }, data);
256
+ } else {
257
+ await this.addObj(data);
258
+ }
259
+ return true;
260
+ } catch (error) {
261
+ $.log.error('[MongoDB] 添加缓存失败:', error);
262
+ return false;
263
+ }
264
+ }
265
+
266
+ /**
267
+ * @description Redis兼容API - 增加整数值
268
+ * @param {String} key 键名
269
+ * @param {Number} value 增加值
270
+ * @return {Promise<Number>} 增加后的值
271
+ */
272
+ async addInt(key, value = 1) {
273
+ try {
274
+ const item = await this.findOne({ key: key });
275
+ let currentValue = 0;
276
+
277
+ if (item && typeof item.value === 'number') {
278
+ currentValue = item.value;
279
+ }
280
+
281
+ const newValue = currentValue + value;
282
+ await this.add(key, newValue);
283
+ return newValue;
284
+ } catch (error) {
285
+ $.log.error('[MongoDB] 增加整数值失败:', error);
286
+ return undefined;
287
+ }
288
+ }
289
+
290
+ /**
291
+ * @description Redis兼容API - 增加浮点数值
292
+ * @param {String} key 键名
293
+ * @param {Number} value 增加值
294
+ * @return {Promise<Number>} 增加后的值
295
+ */
296
+ async addFloat(key, value = 1.0) {
297
+ return this.addInt(key, value); // 复用addInt方法
298
+ }
299
+
300
+ /**
301
+ * @description Redis兼容API - 添加字符串
302
+ * @param {String} key 键名
303
+ * @param {String} value 字符串值
304
+ * @return {Promise<Boolean>} 是否成功
305
+ */
306
+ async addStr(key, value) {
307
+ return this.add(key, String(value));
308
+ }
309
+
310
+ /**
311
+ * @description Redis兼容API - 排序
312
+ * @param {String} key 键名
313
+ * @param {Object} options 排序选项
314
+ * @return {Promise<Array>} 排序结果
315
+ */
316
+ async sort(key, options = {}) {
317
+ try {
318
+ // 假设value是数组
319
+ const item = await this.findOne({ key: key });
320
+ if (!item || !Array.isArray(item.value)) return [];
321
+
322
+ const sorted = [...item.value];
323
+
324
+ // 处理排序选项
325
+ if (options.desc) {
326
+ sorted.sort((a, b) => b - a);
327
+ } else {
328
+ sorted.sort((a, b) => a - b);
329
+ }
330
+
331
+ // 处理限制
332
+ if (options.limit) {
333
+ const offset = options.offset || 0;
334
+ return sorted.slice(offset, offset + options.limit);
335
+ }
336
+
337
+ return sorted;
338
+ } catch (error) {
339
+ $.log.error('[MongoDB] 排序失败:', error);
340
+ return [];
341
+ }
342
+ }
343
+
344
+ /**
345
+ * @description Redis兼容API - 哈希表设置字段
346
+ * @param {String} key 键名
347
+ * @param {String} field 字段名
348
+ * @param {*} value 值
349
+ * @return {Promise<Boolean>} 是否成功
350
+ */
351
+ async hset(key, field, value) {
352
+ try {
353
+ const item = await this.findOne({ key: key });
354
+ let hash = item && typeof item.value === 'object' && item.value !== null ? item.value : {};
355
+
356
+ hash[field] = value;
357
+ await this.add(key, hash);
358
+ return true;
359
+ } catch (error) {
360
+ $.log.error('[MongoDB] 哈希表设置失败:', error);
361
+ return false;
362
+ }
363
+ }
364
+
365
+ /**
366
+ * @description Redis兼容API - 哈希表获取字段
367
+ * @param {String} key 键名
368
+ * @param {String} field 字段名
369
+ * @return {Promise<any>} 字段值
370
+ */
371
+ async hget(key, field) {
372
+ try {
373
+ const item = await this.findOne({ key: key });
374
+ if (!item || typeof item.value !== 'object' || item.value === null) {
375
+ return null;
376
+ }
377
+ return item.value[field] !== undefined ? item.value[field] : null;
378
+ } catch (error) {
379
+ $.log.error('[MongoDB] 哈希表获取失败:', error);
380
+ return null;
381
+ }
382
+ }
383
+
384
+ /**
385
+ * @description Redis兼容API - 哈希表获取所有字段和值
386
+ * @param {String} key 键名
387
+ * @return {Promise<Object>} 哈希表对象
388
+ */
389
+ async hgetall(key) {
390
+ try {
391
+ const item = await this.findOne({ key: key });
392
+ if (!item || typeof item.value !== 'object' || item.value === null) {
393
+ return {};
394
+ }
395
+ return item.value;
396
+ } catch (error) {
397
+ $.log.error('[MongoDB] 哈希表获取全部失败:', error);
398
+ return {};
399
+ }
400
+ }
401
+
402
+ /**
403
+ * @description Redis兼容API - 哈希表删除字段
404
+ * @param {String} key 键名
405
+ * @param {String} field 字段名
406
+ * @return {Promise<Boolean>} 是否成功
407
+ */
408
+ async hdel(key, field) {
409
+ try {
410
+ const item = await this.findOne({ key: key });
411
+ if (!item || typeof item.value !== 'object' || item.value === null) {
412
+ return false;
413
+ }
414
+
415
+ if (item.value[field] === undefined) {
416
+ return false;
417
+ }
418
+
419
+ delete item.value[field];
420
+ await this.set({ key: key }, { value: item.value });
421
+ return true;
422
+ } catch (error) {
423
+ $.log.error('[MongoDB] 哈希表删除失败:', error);
424
+ return false;
425
+ }
426
+ }
427
+
428
+ /**
429
+ * @description Redis兼容API - 检查键是否存在
430
+ * @param {String} key 键名
431
+ * @return {Promise<Boolean>} 是否存在
432
+ */
433
+ async has(key) {
434
+ try {
435
+ const result = await this.findOne({ key: key });
436
+ return result !== null;
437
+ } catch (error) {
438
+ $.log.error('[MongoDB] 检查键是否存在失败:', error);
439
+ return false;
440
+ }
441
+ }
442
+
443
+ /**
444
+ * @description Redis兼容API - 设置键过期时间(映射到ttl)
445
+ * @param {String} key 键名
446
+ * @param {Number} seconds 过期时间(秒)
447
+ * @return {Promise<Boolean>} 是否成功
448
+ */
449
+ async expire(key, seconds) {
450
+ return this.ttl(key, seconds);
451
+ }
452
+
453
+ /**
454
+ * @description Redis兼容API - 递增(通过addInt模拟)
455
+ * @param {String} key 键名
456
+ * @return {Promise<Number>} 递增后的值
457
+ */
458
+ async incr(key) {
459
+ return this.addInt(key, 1);
460
+ }
461
+
462
+ /**
463
+ * @description Redis兼容API - 递减(通过addInt模拟)
464
+ * @param {String} key 键名
465
+ * @return {Promise<Number>} 递减后的值
466
+ */
467
+ async decr(key) {
468
+ return this.addInt(key, -1);
469
+ }
470
+
471
+ /**
472
+ * @description Redis兼容API - 检查键是否存在(别名)
473
+ * @param {String} key 键名
474
+ * @return {Promise<Boolean>} 是否存在
475
+ */
476
+ async exists(key) {
477
+ return this.has(key);
478
+ }
479
+
480
+ /**
481
+ * @description Redis兼容API - 设置键值
482
+ * @param {String} key 键名
483
+ * @param {*} value 值
484
+ * @return {Promise<Boolean>} 是否成功
485
+ */
486
+ async set(key, value) {
487
+ try {
488
+ // 直接使用已有的add方法,它已正确处理查询条件格式
489
+ return await this.add(key, value);
490
+ } catch (error) {
491
+ $.log.error('[MongoDB] 设置键值失败:', error);
492
+ return false;
493
+ }
494
+ }
495
+
496
+ /**
497
+ * @description Redis兼容API - 哈希表批量设置
498
+ * @param {String} key 键名
499
+ * @param {Object} fieldValues 字段值对象
500
+ * @return {Promise<Boolean>} 是否成功
501
+ */
502
+ async hmset(key, fieldValues) {
503
+ try {
504
+ const item = await this.findOne({ key: key });
505
+ let hash = item && typeof item.value === 'object' && item.value !== null ? item.value : {};
506
+
507
+ // 合并字段值
508
+ Object.assign(hash, fieldValues);
509
+ await this.add(key, hash);
510
+ return true;
511
+ } catch (error) {
512
+ $.log.error('[MongoDB] 哈希表批量设置失败:', error);
513
+ return false;
514
+ }
515
+ }
516
+
517
+ /**
518
+ * @description Redis兼容API - 哈希表批量获取
519
+ * @param {String} key 键名
520
+ * @param {Array} fields 字段名数组
521
+ * @return {Promise<Object>} 字段值对象
522
+ */
523
+ async hmget(key, fields) {
524
+ try {
525
+ const item = await this.findOne({ key: key });
526
+ if (!item || typeof item.value !== 'object' || item.value === null) {
527
+ return {};
528
+ }
529
+
530
+ const result = {};
531
+ fields.forEach(field => {
532
+ result[field] = item.value[field] !== undefined ? item.value[field] : null;
533
+ });
534
+ return result;
535
+ } catch (error) {
536
+ $.log.error('[MongoDB] 哈希表批量获取失败:', error);
537
+ return {};
538
+ }
539
+ }
540
+
541
+ /**
542
+ * @description Redis兼容API - 哈希表批量删除字段
543
+ * @param {String} key 键名
544
+ * @param {Array} fields 字段名数组
545
+ * @return {Promise<Boolean>} 是否成功
546
+ */
547
+ async hdelMulti(key, fields) {
548
+ try {
549
+ const item = await this.findOne({ key: key });
550
+ if (!item || typeof item.value !== 'object' || item.value === null) {
551
+ return false;
552
+ }
553
+
554
+ let deleted = false;
555
+ fields.forEach(field => {
556
+ if (item.value[field] !== undefined) {
557
+ delete item.value[field];
558
+ deleted = true;
559
+ }
560
+ });
561
+
562
+ if (deleted) {
563
+ await this.set({ key: key }, { value: item.value });
564
+ }
565
+ return deleted;
566
+ } catch (error) {
567
+ $.log.error('[MongoDB] 哈希表批量删除失败:', error);
568
+ return false;
569
+ }
570
+ }
571
+
572
+ /**
573
+ * @description Redis兼容API - 哈希表获取所有值
574
+ * @param {String} key 键名
575
+ * @return {Promise<Array>} 值数组
576
+ */
577
+ async hvals(key) {
578
+ try {
579
+ const item = await this.findOne({ key: key });
580
+ if (!item || typeof item.value !== 'object' || item.value === null) {
581
+ return [];
582
+ }
583
+ return Object.values(item.value);
584
+ } catch (error) {
585
+ $.log.error('[MongoDB] 哈希表获取所有值失败:', error);
586
+ return [];
587
+ }
588
+ }
589
+
590
+ /**
591
+ * @description Redis兼容API - 哈希表获取所有字段名
592
+ * @param {String} key 键名
593
+ * @return {Promise<Array>} 字段名数组
594
+ */
595
+ async hkeys(key) {
596
+ try {
597
+ const item = await this.findOne({ key: key });
598
+ if (!item || typeof item.value !== 'object' || item.value === null) {
599
+ return [];
600
+ }
601
+ return Object.keys(item.value);
602
+ } catch (error) {
603
+ $.log.error('[MongoDB] 哈希表获取所有字段名失败:', error);
604
+ return [];
605
+ }
606
+ }
607
+
608
+ /**
609
+ * @description Redis兼容API - 哈希表字段数量
610
+ * @param {String} key 键名
611
+ * @return {Promise<Number>} 字段数量
612
+ */
613
+ async hlen(key) {
614
+ try {
615
+ const item = await this.findOne({ key: key });
616
+ if (!item || typeof item.value !== 'object' || item.value === null) {
617
+ return 0;
618
+ }
619
+ return Object.keys(item.value).length;
620
+ } catch (error) {
621
+ $.log.error('[MongoDB] 哈希表字段数量失败:', error);
622
+ return 0;
623
+ }
624
+ }
625
+
626
+ /**
627
+ * @description Redis兼容API - 哈希表字段是否存在
628
+ * @param {String} key 键名
629
+ * @param {String} field 字段名
630
+ * @return {Promise<Boolean>} 是否存在
631
+ */
632
+ async hexists(key, field) {
633
+ try {
634
+ const item = await this.findOne({ key: key });
635
+ if (!item || typeof item.value !== 'object' || item.value === null) {
636
+ return false;
637
+ }
638
+ return field in item.value;
639
+ } catch (error) {
640
+ $.log.error('[MongoDB] 哈希表字段是否存在失败:', error);
641
+ return false;
642
+ }
643
+ }
644
+
645
+ /**
646
+ * @description Redis兼容API - 列表尾部添加元素
647
+ * @param {String} key 键名
648
+ * @param {*} value 值
649
+ * @return {Promise<Number>} 添加后列表长度
650
+ */
651
+ async rpush(key, value) {
652
+ try {
653
+ const item = await this.findOne({ key: key });
654
+ let list = item && Array.isArray(item.value) ? item.value : [];
655
+
656
+ list.push(value);
657
+ await this.add(key, list);
658
+ return list.length;
659
+ } catch (error) {
660
+ $.log.error('[MongoDB] 列表尾部添加失败:', error);
661
+ return 0;
662
+ }
663
+ }
664
+
665
+ /**
666
+ * @description Redis兼容API - 列表头部添加元素
667
+ * @param {String} key 键名
668
+ * @param {*} value 值
669
+ * @return {Promise<Number>} 添加后列表长度
670
+ */
671
+ async lpush(key, value) {
672
+ try {
673
+ const item = await this.findOne({ key: key });
674
+ let list = item && Array.isArray(item.value) ? item.value : [];
675
+
676
+ list.unshift(value);
677
+ await this.add(key, list);
678
+ return list.length;
679
+ } catch (error) {
680
+ $.log.error('[MongoDB] 列表头部添加失败:', error);
681
+ return 0;
682
+ }
683
+ }
684
+
685
+ /**
686
+ * @description Redis兼容API - 列表范围查询
687
+ * @param {String} key 键名
688
+ * @param {Number} start 起始索引
689
+ * @param {Number} stop 结束索引
690
+ * @return {Promise<Array>} 元素数组
691
+ */
692
+ async lrange(key, start, stop) {
693
+ try {
694
+ const item = await this.findOne({ key: key });
695
+ if (!item || !Array.isArray(item.value)) {
696
+ return [];
697
+ }
698
+
699
+ // 处理负数索引
700
+ let startIndex = start < 0 ? item.value.length + start : start;
701
+ let endIndex = stop < 0 ? item.value.length + stop : stop;
702
+
703
+ // 确保索引有效
704
+ startIndex = Math.max(0, Math.min(startIndex, item.value.length - 1));
705
+ endIndex = Math.max(startIndex, Math.min(endIndex, item.value.length - 1));
706
+
707
+ return item.value.slice(startIndex, endIndex + 1);
708
+ } catch (error) {
709
+ $.log.error('[MongoDB] 列表范围查询失败:', error);
710
+ return [];
711
+ }
712
+ }
713
+
714
+ /**
715
+ * @description Redis兼容API - 获取列表指定索引元素
716
+ * @param {String} key 键名
717
+ * @param {Number} index 索引
718
+ * @return {Promise<any>} 元素值
719
+ */
720
+ async lindex(key, index) {
721
+ try {
722
+ const item = await this.findOne({ key: key });
723
+ if (!item || !Array.isArray(item.value)) {
724
+ return null;
725
+ }
726
+
727
+ // 处理负数索引
728
+ const realIndex = index < 0 ? item.value.length + index : index;
729
+
730
+ if (realIndex < 0 || realIndex >= item.value.length) {
731
+ return null;
732
+ }
733
+
734
+ return item.value[realIndex];
735
+ } catch (error) {
736
+ $.log.error('[MongoDB] 获取列表索引元素失败:', error);
737
+ return null;
738
+ }
739
+ }
740
+
741
+ /**
742
+ * @description Redis兼容API - 设置列表指定索引元素
743
+ * @param {String} key 键名
744
+ * @param {Number} index 索引
745
+ * @param {*} value 值
746
+ * @return {Promise<Boolean>} 是否成功
747
+ */
748
+ async lset(key, index, value) {
749
+ try {
750
+ const item = await this.findOne({ key: key });
751
+ if (!item || !Array.isArray(item.value)) {
752
+ return false;
753
+ }
754
+
755
+ // 处理负数索引
756
+ const realIndex = index < 0 ? item.value.length + index : index;
757
+
758
+ if (realIndex < 0 || realIndex >= item.value.length) {
759
+ return false;
760
+ }
761
+
762
+ item.value[realIndex] = value;
763
+ await this.set({ key: key }, { value: item.value });
764
+ return true;
765
+ } catch (error) {
766
+ $.log.error('[MongoDB] 设置列表索引元素失败:', error);
767
+ return false;
768
+ }
769
+ }
770
+
771
+ /**
772
+ * @description Redis兼容API - 删除列表元素
773
+ * @param {String} key 键名
774
+ * @param {Number} count 删除数量
775
+ * @param {*} value 要删除的值
776
+ * @return {Promise<Number>} 删除的元素数量
777
+ */
778
+ async lrem(key, count, value) {
779
+ try {
780
+ const item = await this.findOne({ key: key });
781
+ if (!item || !Array.isArray(item.value)) {
782
+ return 0;
783
+ }
784
+
785
+ let list = [...item.value];
786
+ let removedCount = 0;
787
+
788
+ if (count > 0) {
789
+ // 从头部删除count个等于value的元素
790
+ for (let i = 0; i < list.length && removedCount < count; i++) {
791
+ if (JSON.stringify(list[i]) === JSON.stringify(value)) {
792
+ list.splice(i, 1);
793
+ removedCount++;
794
+ i--; // 调整索引
795
+ }
796
+ }
797
+ } else if (count < 0) {
798
+ // 从尾部删除|count|个等于value的元素
799
+ count = Math.abs(count);
800
+ for (let i = list.length - 1; i >= 0 && removedCount < count; i--) {
801
+ if (JSON.stringify(list[i]) === JSON.stringify(value)) {
802
+ list.splice(i, 1);
803
+ removedCount++;
804
+ }
805
+ }
806
+ } else {
807
+ // 删除所有等于value的元素
808
+ list = list.filter(item => JSON.stringify(item) !== JSON.stringify(value));
809
+ removedCount = item.value.length - list.length;
810
+ }
811
+
812
+ if (removedCount > 0) {
813
+ await this.set({ key: key }, { value: list });
814
+ }
815
+
816
+ return removedCount;
817
+ } catch (error) {
818
+ $.log.error('[MongoDB] 删除列表元素失败:', error);
819
+ return 0;
820
+ }
821
+ }
822
+
823
+ /**
824
+ * @description Redis兼容API - 裁剪列表
825
+ * @param {String} key 键名
826
+ * @param {Number} start 起始索引
827
+ * @param {Number} stop 结束索引
828
+ * @return {Promise<Boolean>} 是否成功
829
+ */
830
+ async ltrim(key, start, stop) {
831
+ try {
832
+ const item = await this.findOne({ key: key });
833
+ if (!item || !Array.isArray(item.value)) {
834
+ return false;
835
+ }
836
+
837
+ // 使用lrange的逻辑获取裁剪后的列表
838
+ const trimmed = await this.lrange(key, start, stop);
839
+ await this.set({ key: key }, { value: trimmed });
840
+ return true;
841
+ } catch (error) {
842
+ $.log.error('[MongoDB] 裁剪列表失败:', error);
843
+ return false;
844
+ }
845
+ }
846
+
847
+ /**
848
+ * @description Redis兼容API - 列表长度
849
+ * @param {String} key 键名
850
+ * @return {Promise<Number>} 列表长度
851
+ */
852
+ async llen(key) {
853
+ try {
854
+ const item = await this.findOne({ key: key });
855
+ if (!item || !Array.isArray(item.value)) {
856
+ return 0;
857
+ }
858
+ return item.value.length;
859
+ } catch (error) {
860
+ $.log.error('[MongoDB] 获取列表长度失败:', error);
861
+ return 0;
862
+ }
863
+ }
864
+
865
+ /**
866
+ * @description Redis兼容API - 集合批量添加
867
+ * @param {String} key 键名
868
+ * @param {Array} members 成员数组
869
+ * @return {Promise<Number>} 添加的成员数量
870
+ */
871
+ async saddMulti(key, members) {
872
+ try {
873
+ const item = await this.findOne({ key: key });
874
+ let set = item && Array.isArray(item.value) ? [...new Set(item.value)] : [];
875
+ let addedCount = 0;
876
+
877
+ members.forEach(member => {
878
+ if (!set.some(existing => JSON.stringify(existing) === JSON.stringify(member))) {
879
+ set.push(member);
880
+ addedCount++;
881
+ }
882
+ });
883
+
884
+ if (addedCount > 0) {
885
+ await this.add(key, set);
886
+ }
887
+
888
+ return addedCount;
889
+ } catch (error) {
890
+ $.log.error('[MongoDB] 集合并发添加失败:', error);
891
+ return 0;
892
+ }
893
+ }
894
+
895
+ /**
896
+ * @description Redis兼容API - 集合批量删除
897
+ * @param {String} key 键名
898
+ * @param {Array} members 成员数组
899
+ * @return {Promise<Number>} 删除的成员数量
900
+ */
901
+ async sremMulti(key, members) {
902
+ try {
903
+ const item = await this.findOne({ key: key });
904
+ if (!item || !Array.isArray(item.value)) {
905
+ return 0;
906
+ }
907
+
908
+ let set = [...new Set(item.value)];
909
+ const originalLength = set.length;
910
+
911
+ // 创建要删除的值的字符串表示集合用于快速查找
912
+ const membersToRemove = new Set(members.map(m => JSON.stringify(m)));
913
+ set = set.filter(member => !membersToRemove.has(JSON.stringify(member)));
914
+
915
+ const removedCount = originalLength - set.length;
916
+ if (removedCount > 0) {
917
+ await this.add(key, set);
918
+ }
919
+
920
+ return removedCount;
921
+ } catch (error) {
922
+ $.log.error('[MongoDB] 集合并发删除失败:', error);
923
+ return 0;
924
+ }
925
+ }
926
+
927
+ /**
928
+ * @description Redis兼容API - 集合差集
929
+ * @param {String} key1 第一个集合键
930
+ * @param {String} key2 第二个集合键
931
+ * @return {Promise<Array>} 差集数组
932
+ */
933
+ async sdiff(key1, key2) {
934
+ try {
935
+ const item1 = await this.findOne({ key: key1 });
936
+ const item2 = await this.findOne({ key: key2 });
937
+
938
+ const set1 = item1 && Array.isArray(item1.value) ? [...new Set(item1.value)] : [];
939
+ const set2 = item2 && Array.isArray(item2.value) ? [...new Set(item2.value)] : [];
940
+
941
+ // 创建第二个集合值的字符串表示用于快速查找
942
+ const set2Str = new Set(set2.map(m => JSON.stringify(m)));
943
+
944
+ // 找出在set1中但不在set2中的元素
945
+ return set1.filter(member => !set2Str.has(JSON.stringify(member)));
946
+ } catch (error) {
947
+ $.log.error('[MongoDB] 集合差集失败:', error);
948
+ return [];
949
+ }
950
+ }
951
+
952
+ /**
953
+ * @description Redis兼容API - 集合交集
954
+ * @param {String} key1 第一个集合键
955
+ * @param {String} key2 第二个集合键
956
+ * @return {Promise<Array>} 交集数组
957
+ */
958
+ async sinter(key1, key2) {
959
+ try {
960
+ const item1 = await this.findOne({ key: key1 });
961
+ const item2 = await this.findOne({ key: key2 });
962
+
963
+ const set1 = item1 && Array.isArray(item1.value) ? [...new Set(item1.value)] : [];
964
+ const set2 = item2 && Array.isArray(item2.value) ? [...new Set(item2.value)] : [];
965
+
966
+ // 创建较小集合的字符串表示用于快速查找,优化性能
967
+ let smallerSet, largerSet;
968
+ if (set1.length <= set2.length) {
969
+ smallerSet = set1;
970
+ largerSet = set2;
971
+ } else {
972
+ smallerSet = set2;
973
+ largerSet = set1;
974
+ }
975
+
976
+ const largerSetStr = new Set(largerSet.map(m => JSON.stringify(m)));
977
+
978
+ // 找出同时存在于两个集合中的元素
979
+ return smallerSet.filter(member => largerSetStr.has(JSON.stringify(member)));
980
+ } catch (error) {
981
+ $.log.error('[MongoDB] 集合并集失败:', error);
982
+ return [];
983
+ }
984
+ }
985
+
986
+ /**
987
+ * @description Redis兼容API - 集合并集
988
+ * @param {String} key1 第一个集合键
989
+ * @param {String} key2 第二个集合键
990
+ * @return {Promise<Array>} 并集数组
991
+ */
992
+ async sunion(key1, key2) {
993
+ try {
994
+ const item1 = await this.findOne({ key: key1 });
995
+ const item2 = await this.findOne({ key: key2 });
996
+
997
+ const set1 = item1 && Array.isArray(item1.value) ? item1.value : [];
998
+ const set2 = item2 && Array.isArray(item2.value) ? item2.value : [];
999
+
1000
+ // 合并并去重
1001
+ const combined = [...set1, ...set2];
1002
+ // 使用Map进行对象去重,基于JSON字符串表示
1003
+ const uniqueMap = new Map();
1004
+
1005
+ combined.forEach(item => {
1006
+ const key = JSON.stringify(item);
1007
+ if (!uniqueMap.has(key)) {
1008
+ uniqueMap.set(key, item);
1009
+ }
1010
+ });
1011
+
1012
+ return Array.from(uniqueMap.values());
1013
+ } catch (error) {
1014
+ $.log.error('[MongoDB] 集合并集失败:', error);
1015
+ return [];
1016
+ }
1017
+ }
1018
+
1019
+ /**
1020
+ * @description Redis兼容API - 集合成员数量
1021
+ * @param {String} key 键名
1022
+ * @return {Promise<Number>} 成员数量
1023
+ */
1024
+ async scard(key) {
1025
+ try {
1026
+ const item = await this.findOne({ key: key });
1027
+ if (!item || !Array.isArray(item.value)) {
1028
+ return 0;
1029
+ }
1030
+
1031
+ // 使用Set去重并计算大小
1032
+ const uniqueItems = new Set(item.value.map(m => JSON.stringify(m)));
1033
+ return uniqueItems.size;
1034
+ } catch (error) {
1035
+ $.log.error('[MongoDB] 获取集合成员数量失败:', error);
1036
+ return 0;
1037
+ }
1038
+ }
1039
+
1040
+ /**
1041
+ * @description Redis兼容API - 检查是否是集合成员
1042
+ * @param {String} key 键名
1043
+ * @param {*} member 成员
1044
+ * @return {Promise<Boolean>} 是否是成员
1045
+ */
1046
+ async sismember(key, member) {
1047
+ try {
1048
+ const item = await this.findOne({ key: key });
1049
+ if (!item || !Array.isArray(item.value)) {
1050
+ return false;
1051
+ }
1052
+
1053
+ // 检查是否存在相同的成员
1054
+ const memberStr = JSON.stringify(member);
1055
+ return item.value.some(m => JSON.stringify(m) === memberStr);
1056
+ } catch (error) {
1057
+ $.log.error('[MongoDB] 检查集合成员失败:', error);
1058
+ return false;
1059
+ }
1060
+ }
1061
+
1062
+ /**
1063
+ * @description Redis兼容API - 批量获取
1064
+ * @param {Array} keys 键名数组
1065
+ * @return {Promise<Object>} 键值对象
1066
+ */
1067
+ async mget(keys) {
1068
+ try {
1069
+ const results = await this.find({ key: { $in: keys } });
1070
+ const resultMap = {};
1071
+ results.forEach(item => {
1072
+ resultMap[item.key] = item.value;
1073
+ });
1074
+ return resultMap;
1075
+ } catch (error) {
1076
+ $.log.error('[MongoDB] 批量获取失败:', error);
1077
+ return {};
1078
+ }
1079
+ }
1080
+
1081
+ /**
1082
+ * @description Redis兼容API - 批量设置
1083
+ * @param {Object} keyValues 键值对象
1084
+ * @return {Promise<Boolean>} 是否成功
1085
+ */
1086
+ async mset(keyValues) {
1087
+ try {
1088
+ for (const [key, value] of Object.entries(keyValues)) {
1089
+ await this.add(key, value);
1090
+ }
1091
+ return true;
1092
+ } catch (error) {
1093
+ $.log.error('[MongoDB] 批量设置失败:', error);
1094
+ return false;
1095
+ }
1096
+ }
1097
+
1098
+ /**
1099
+ * @description Redis兼容API - 有序集合添加成员
1100
+ * @param {String} key 键名
1101
+ * @param {Number} score 分数
1102
+ * @param {*} member 成员
1103
+ * @return {Promise<Boolean>} 是否成功
1104
+ */
1105
+ async zadd(key, score, member) {
1106
+ try {
1107
+ const item = await this.findOne({ key: key });
1108
+ let sortedSet = item && Array.isArray(item.value) ? item.value : [];
1109
+
1110
+ // 检查成员是否已存在,如果存在则更新分数
1111
+ const memberStr = JSON.stringify(member);
1112
+ const existingIndex = sortedSet.findIndex(item => JSON.stringify(item.member) === memberStr);
1113
+
1114
+ if (existingIndex >= 0) {
1115
+ sortedSet[existingIndex].score = score;
1116
+ } else {
1117
+ sortedSet.push({ score, member });
1118
+ }
1119
+
1120
+ // 按分数排序
1121
+ sortedSet.sort((a, b) => a.score - b.score);
1122
+ await this.add(key, sortedSet);
1123
+ return true;
1124
+ } catch (error) {
1125
+ $.log.error('[MongoDB] 有序集合添加成员失败:', error);
1126
+ return false;
1127
+ }
1128
+ }
1129
+
1130
+ /**
1131
+ * @description Redis兼容API - 有序集合范围查询(按分数)
1132
+ * @param {String} key 键名
1133
+ * @param {Number} min 最小分数
1134
+ * @param {Number} max 最大分数
1135
+ * @return {Promise<Array>} 成员数组
1136
+ */
1137
+ async zrangebyscore(key, min, max) {
1138
+ try {
1139
+ const item = await this.findOne({ key: key });
1140
+ if (!item || !Array.isArray(item.value)) {
1141
+ return [];
1142
+ }
1143
+
1144
+ // 过滤分数范围内的成员
1145
+ return item.value
1146
+ .filter(item => item.score >= min && item.score <= max)
1147
+ .map(item => item.member);
1148
+ } catch (error) {
1149
+ $.log.error('[MongoDB] 有序集合范围查询失败:', error);
1150
+ return [];
1151
+ }
1152
+ }
1153
+
1154
+ /**
1155
+ * @description Redis兼容API - 有序集合成员数量
1156
+ * @param {String} key 键名
1157
+ * @return {Promise<Number>} 成员数量
1158
+ */
1159
+ async zcard(key) {
1160
+ try {
1161
+ const item = await this.findOne({ key: key });
1162
+ if (!item || !Array.isArray(item.value)) {
1163
+ return 0;
1164
+ }
1165
+ return item.value.length;
1166
+ } catch (error) {
1167
+ $.log.error('[MongoDB] 有序集合成员数量失败:', error);
1168
+ return 0;
1169
+ }
1170
+ }
1171
+
1172
+ /**
1173
+ * @description Redis兼容API - 有序集合删除成员
1174
+ * @param {String} key 键名
1175
+ * @param {*} member 成员
1176
+ * @return {Promise<Number>} 删除的成员数量
1177
+ */
1178
+ async zrem(key, member) {
1179
+ try {
1180
+ const item = await this.findOne({ key: key });
1181
+ if (!item || !Array.isArray(item.value)) {
1182
+ return 0;
1183
+ }
1184
+
1185
+ const memberStr = JSON.stringify(member);
1186
+ const filtered = item.value.filter(item => JSON.stringify(item.member) !== memberStr);
1187
+ const removedCount = item.value.length - filtered.length;
1188
+
1189
+ if (removedCount > 0) {
1190
+ await this.add(key, filtered);
1191
+ }
1192
+
1193
+ return removedCount;
1194
+ } catch (error) {
1195
+ $.log.error('[MongoDB] 有序集合删除成员失败:', error);
1196
+ return 0;
1197
+ }
1198
+ }
1199
+
1200
+ /**
1201
+ * @description Redis兼容API - 获取成员分数
1202
+ * @param {String} key 键名
1203
+ * @param {*} member 成员
1204
+ * @return {Promise<Number|null>} 分数
1205
+ */
1206
+ async zscore(key, member) {
1207
+ try {
1208
+ const item = await this.findOne({ key: key });
1209
+ if (!item || !Array.isArray(item.value)) {
1210
+ return null;
1211
+ }
1212
+
1213
+ const memberStr = JSON.stringify(member);
1214
+ const found = item.value.find(item => JSON.stringify(item.member) === memberStr);
1215
+ return found ? found.score : null;
1216
+ } catch (error) {
1217
+ $.log.error('[MongoDB] 获取成员分数失败:', error);
1218
+ return null;
1219
+ }
1220
+ }
1221
+
1222
+ /**
1223
+ * @description Redis兼容API - 列表弹出头部元素
1224
+ * @param {String} key 键名
1225
+ * @return {Promise<any|null>} 弹出的元素
1226
+ */
1227
+ async lpop(key) {
1228
+ try {
1229
+ const item = await this.findOne({ key: key });
1230
+ if (!item || !Array.isArray(item.value) || item.value.length === 0) {
1231
+ return null;
1232
+ }
1233
+
1234
+ const popped = item.value.shift();
1235
+ await this.set({ key: key }, { value: item.value });
1236
+ return popped;
1237
+ } catch (error) {
1238
+ $.log.error('[MongoDB] 列表弹出头部元素失败:', error);
1239
+ return null;
1240
+ }
1241
+ }
1242
+
1243
+ /**
1244
+ * @description Redis兼容API - 列表弹出尾部元素
1245
+ * @param {String} key 键名
1246
+ * @return {Promise<any|null>} 弹出的元素
1247
+ */
1248
+ async rpop(key) {
1249
+ try {
1250
+ const item = await this.findOne({ key: key });
1251
+ if (!item || !Array.isArray(item.value) || item.value.length === 0) {
1252
+ return null;
1253
+ }
1254
+
1255
+ const popped = item.value.pop();
1256
+ await this.set({ key: key }, { value: item.value });
1257
+ return popped;
1258
+ } catch (error) {
1259
+ $.log.error('[MongoDB] 列表弹出尾部元素失败:', error);
1260
+ return null;
1261
+ }
1262
+ }
1263
+
1264
+ /**
1265
+ * @description Redis兼容API - 获取集合所有成员
1266
+ * @param {String} key 键名
1267
+ * @return {Promise<Array>} 成员数组
1268
+ */
1269
+ async smembers(key) {
1270
+ try {
1271
+ const item = await this.findOne({ key: key });
1272
+ if (!item || !Array.isArray(item.value)) {
1273
+ return [];
1274
+ }
1275
+
1276
+ // 去重并返回
1277
+ const uniqueMap = new Map();
1278
+ item.value.forEach(member => {
1279
+ const keyStr = JSON.stringify(member);
1280
+ if (!uniqueMap.has(keyStr)) {
1281
+ uniqueMap.set(keyStr, member);
1282
+ }
1283
+ });
1284
+ return Array.from(uniqueMap.values());
1285
+ } catch (error) {
1286
+ $.log.error('[MongoDB] 获取集合所有成员失败:', error);
1287
+ return [];
1288
+ }
1289
+ }
157
1290
  }
158
1291
 
159
1292
  /**
@@ -250,28 +1383,6 @@ MongoDB.prototype.addTable = function (table) {
250
1383
  return this.db.createCollection(table);
251
1384
  };
252
1385
 
253
- /**
254
- * @description 创建TTL索引
255
- * @param {String} table 表名
256
- * @param {String} field 过期字段名
257
- * @param {Number} expireAfterSeconds 过期时间(秒)
258
- * @return {Promise<Object>} 执行结果
259
- */
260
- MongoDB.prototype.createTTLIndex = async function (table, field, expireAfterSeconds) {
261
- if (!table) table = this.table;
262
- return await this.db.collection(table).createIndex(
263
- { [field]: 1 },
264
- { expireAfterSeconds: expireAfterSeconds || 0 }
265
- );
266
- };
267
-
268
- /**
269
- * @description 创建普通索引
270
- * @param {String} table 表名
271
- * @param {Object} keys 索引字段对象
272
- * @param {Object} options 索引选项
273
- * @return {Promise<Object>} 执行结果
274
- */
275
1386
  /**
276
1387
  * @description 创建索引
277
1388
  * @param {Object} keys 索引键配置 {field: 1或-1}
@@ -433,19 +1544,22 @@ MongoDB.prototype.addList = async function(list) {
433
1544
 
434
1545
  /**
435
1546
  * @description 删除缓存
436
- * @param {Object} query 查询条件对象
1547
+ * @param {Object|String} query 查询条件对象或键名
437
1548
  * @return {Promise|Object} 执行结果
438
1549
  */
439
1550
  MongoDB.prototype.del = async function(query) {
440
1551
  try {
1552
+ // 如果query是字符串,则作为键名处理
1553
+ const safeQuery = typeof query === 'string' ? { key: query } : query;
1554
+
441
1555
  // 触发前置事件
442
- this.emit('beforeDelete', { table: this.table, query });
1556
+ this.emit('beforeDelete', { table: this.table, query: safeQuery });
443
1557
 
444
1558
  // 执行删除操作
445
- const result = await this.db.collection(this.table).deleteMany(query);
1559
+ const result = await this.db.collection(this.table).deleteMany(safeQuery);
446
1560
 
447
1561
  // 触发后置事件
448
- this.emit('afterDelete', { table: this.table, query, result });
1562
+ this.emit('afterDelete', { table: this.table, query: safeQuery, result });
449
1563
 
450
1564
  return result;
451
1565
  } catch (error) {
@@ -457,34 +1571,47 @@ MongoDB.prototype.del = async function(query) {
457
1571
 
458
1572
  /**
459
1573
  * @description 修改缓存
460
- * @param {Object} query 查询条件对象
1574
+ * @param {Object|String} query 查询条件对象或键名
461
1575
  * @param {Object} body 修改对象
462
1576
  * @param {Object} options 附加选项
463
1577
  * @return {Promise|Object} 执行结果
464
1578
  */
465
1579
  MongoDB.prototype.set = async function(query, body, options = {}) {
466
1580
  try {
1581
+ // 处理Redis兼容模式:如果query是字符串,则将query作为键名
1582
+ const safeQuery = typeof query === 'string' ? { key: query } : query;
1583
+
467
1584
  // 默认使用$set操作符进行部分更新
468
- if (!body.$set && !body.$inc && !body.$push) {
469
- body = { $set: body };
1585
+ let updateBody;
1586
+ if (typeof query === 'string') {
1587
+ // Redis兼容模式:如果是字符串键名,直接设置value
1588
+ updateBody = { $set: { value: body } };
1589
+ } else if (!body.$set && !body.$inc && !body.$push) {
1590
+ updateBody = { $set: body };
1591
+ } else {
1592
+ updateBody = body;
470
1593
  }
1594
+
471
1595
  // 默认更新多条
472
1596
  const updateOptions = Object.assign({ multi: true }, options);
473
1597
 
474
1598
  // 触发前置事件
475
- this.emit('beforeUpdate', { table: this.table, query, update: body, options: updateOptions });
1599
+ this.emit('beforeUpdate', { table: this.table, query: safeQuery, update: updateBody, options: updateOptions });
476
1600
 
477
1601
  // 执行更新操作
478
- const result = await this.db.collection(this.table).updateMany(query, body, updateOptions);
1602
+ const result = await this.db.collection(this.table).updateMany(safeQuery, updateBody, updateOptions);
479
1603
 
480
1604
  // 触发后置事件
481
- this.emit('afterUpdate', { table: this.table, query, update: body, options: updateOptions, result });
1605
+ this.emit('afterUpdate', { table: this.table, query: safeQuery, update: updateBody, options: updateOptions, result });
482
1606
 
483
- return result;
1607
+ // Redis兼容模式返回布尔值,否则返回完整结果
1608
+ return typeof query === 'string' ? result.modifiedCount > 0 : result;
484
1609
  } catch (error) {
485
1610
  // 触发错误事件
486
1611
  this.emit('updateError', { table: this.table, query, update: body, options, error });
487
- throw error;
1612
+
1613
+ // Redis兼容模式返回布尔值
1614
+ return typeof query === 'string' ? false : null;
488
1615
  }
489
1616
  };
490
1617