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/README.md +132 -214
- package/compatibility_table.md +85 -0
- package/index.js +1161 -34
- package/package.json +1 -1
- package/test_redis_compatibility.js +123 -0
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(
|
|
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
|
-
|
|
469
|
-
|
|
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:
|
|
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(
|
|
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:
|
|
1605
|
+
this.emit('afterUpdate', { table: this.table, query: safeQuery, update: updateBody, options: updateOptions, result });
|
|
482
1606
|
|
|
483
|
-
|
|
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
|
-
|
|
1612
|
+
|
|
1613
|
+
// Redis兼容模式返回布尔值
|
|
1614
|
+
return typeof query === 'string' ? false : null;
|
|
488
1615
|
}
|
|
489
1616
|
};
|
|
490
1617
|
|