nestjs-redis-plus
Version:
A super-powered Nestjs Redis module
314 lines (313 loc) • 10.8 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisService = void 0;
const core_1 = require("@nestjs/core");
const common_1 = require("@nestjs/common");
const redis_constants_1 = require("./redis.constants");
let RedisService = class RedisService {
clientName;
moduleRef;
redis;
constructor(clientName, moduleRef) {
this.clientName = clientName;
this.moduleRef = moduleRef;
}
onModuleInit() {
this.redis = this.moduleRef.get(this.clientName);
}
get client() {
return this.redis;
}
get keyPrefix() {
return this.redis.options.keyPrefix;
}
// Keys
toInt(value, radix = 10) {
if (!value)
return 0;
const int = parseInt(value, radix);
if (isNaN(int))
return null;
return int;
}
toFloat(value) {
if (!value)
return 0;
const float = parseFloat(value);
if (isNaN(float))
return null;
return float;
}
setKey(key, value) {
return this.redis.set(key, value);
}
setKeys(map) {
return this.redis.mset(map);
}
getKey(key) {
return this.redis.get(key);
}
getAndSetKey(key, value) {
return this.redis.getset(key, value);
}
getAndDeleteKey(key) {
return this.redis.getdel(key);
}
getAndExpireKey(key, ttl) {
return this.redis.getex(key, "EX", ttl);
}
async getKeys(keys) {
const values = await this.redis.mget(keys);
return new Map(keys.map((key, index) => [key, values[index]]));
}
delKey(key) {
return this.redis.del(key);
}
delKeys(keys) {
return this.redis.del(keys);
}
keyExists(key) {
return this.redis.exists(key);
}
setJSONKey(key, value) {
return this.redis.set(key, JSON.stringify(value));
}
async getJSONKey(key) {
const value = await this.redis.get(key);
return value ? JSON.parse(value) : null;
}
async getIntKey(key, radix, isStrict) {
const value = await this.redis.get(key);
const int = this.toInt(value, radix);
if (isStrict && int === null)
throw new Error(`"${key}" is not an int: ${value}`);
return int;
}
async getFloatKey(key, isStrict) {
const value = await this.redis.get(key);
const float = this.toFloat(value);
if (isStrict && float === null)
throw new Error(`"${key}" is not a float: ${value}`);
return float;
}
async scanKeys(pattern, count = 100, includeKeyPrefix = true) {
const keys = [];
const match = includeKeyPrefix ? `${this.keyPrefix}${pattern}` : pattern;
const cursor = this.redis.scanStream({ match, count });
for await (const matches of cursor)
keys.push(...matches);
return keys;
}
async expireKey(key, ttl) {
const isExpired = await this.redis.expire(key, ttl);
return isExpired === 1;
}
async setAndExpireKey(key, value, ttl) {
const isSet = await this.redis.set(key, value, "EX", ttl);
return isSet === "OK";
}
async lockKey(key) {
const isLocked = await this.redis.set(key, "locked", "NX");
return isLocked === "OK";
}
async isKeyLocked(key, ensure) {
if (!ensure)
return this.keyExists(key);
const value = await this.getKey(key);
return value === "locked";
}
unlockKey(key) {
return this.delKey(key);
}
async lockAndExpireKey(key, ttl) {
const isLocked = await this.redis.set(key, "locked", "EX", ttl, "NX");
return isLocked === "OK";
}
async refreshKeyLock(key, ttl) {
const isRefreshed = await this.redis.set(key, "locked", "EX", ttl, "XX");
return isRefreshed === "OK";
}
incrKey(key) {
return this.redis.incr(key);
}
incrKeyBy(key, increment) {
return this.redis.incrby(key, increment);
}
async incrKeyByFloat(key, increment) {
const result = await this.redis.incrbyfloat(key, increment);
return parseFloat(result);
}
decrKey(key) {
return this.redis.decr(key);
}
decrKeyBy(key, decrement) {
return this.redis.decrby(key, decrement);
}
// Maps
setMap(key, map) {
return this.redis.hmset(key, map);
}
async getMap(key) {
const records = await this.getMapAsObject(key);
return new Map(Object.entries(records));
}
async getMapAsObject(key) {
const records = await this.redis.hgetall(key);
return records;
}
getMapKeys(key) {
return this.redis.hkeys(key);
}
getMapValues(key) {
return this.redis.hvals(key);
}
setField(key, field, value) {
return this.redis.hset(key, field, value);
}
setFields(key, fields) {
return this.redis.hmset(key, fields);
}
getField(key, field) {
return this.redis.hget(key, field);
}
getFields(key, fields) {
return this.redis.hmget(key, ...fields);
}
delField(key, field) {
return this.redis.hdel(key, field);
}
delFields(key, fields) {
return this.redis.hdel(key, ...fields);
}
fieldExists(key, field) {
return this.redis.hexists(key, field);
}
async getIntField(key, field, radix, isStrict) {
const value = await this.getField(key, field);
const int = this.toInt(value, radix);
if (isStrict && int === null)
throw new Error(`"${key}.${field}" is not an int: ${value}`);
return int;
}
async getFloatField(key, field, isStrict) {
const value = await this.getField(key, field);
const float = this.toFloat(value);
if (isStrict && float === null)
throw new Error(`"${key}.${field}" is not a float: ${value}`);
return float;
}
async scanMap(key, pattern, count = 100, includeKeyPrefix = true) {
const fields = [];
const match = includeKeyPrefix ? `${this.keyPrefix}${pattern}` : pattern;
const cursor = this.redis.hscanStream(key, { match, count });
for await (const matches of cursor)
fields.push(...matches);
return fields;
}
async lockField(key, field) {
const isLocked = await this.redis.hsetnx(key, field, "locked");
return isLocked === 1;
}
async isFieldLocked(key, field, ensure) {
if (!ensure)
return this.fieldExists(key, field);
const value = await this.getField(key, field);
return value === "locked";
}
unlockField(key, field) {
return this.delField(key, field);
}
incrFieldBy(key, field, increment) {
return this.redis.hincrby(key, field, increment);
}
async incrFieldByFloat(key, field, increment) {
const result = await this.redis.hincrbyfloat(key, field, increment);
return parseFloat(result);
}
// Sets
async getSet(key) {
const members = await this.redis.smembers(key);
return new Set(members);
}
async getIntSet(key, radix, isStrict) {
const members = await this.redis.smembers(key);
const ints = members.flatMap((member, idx) => {
const int = this.toInt(member, radix);
if (isStrict && int === null)
throw new Error(`"${key}[${idx}]" is not an int: ${member}`);
return int;
});
return new Set(ints);
}
async getFloatSet(key, isStrict) {
const members = await this.redis.smembers(key);
const floats = members
.flatMap((member, idx) => {
const float = parseFloat(member);
if (isStrict && float === null)
throw new Error(`"${key}[${idx}]" is not a float: ${member}`);
return float;
})
.sort((a, b) => a - b);
return new Set(floats);
}
async scanSet(key, pattern, count = 100, includeKeyPrefix = true) {
const members = [];
const match = includeKeyPrefix ? `${this.keyPrefix}${pattern}` : pattern;
const cursor = this.redis.sscanStream(key, {
match,
count,
});
for await (const matches of cursor)
members.push(...matches);
return members;
}
async addToSet(key, values, isStrict) {
const added = await this.redis.sadd(key, values);
if (isStrict && added !== values.length)
throw new Error(`Only ${added} of ${values.length} were added to "${key}"`);
return added;
}
async removeFromSet(key, values, isStrict) {
const removed = await this.redis.srem(key, values);
if (isStrict && removed !== values.length)
throw new Error(`Only ${removed} of ${values.length} were removed from "${key}"`);
return removed;
}
countSet(key) {
return this.redis.scard(key);
}
async isMemberOfSet(key, member) {
const isMember = await this.redis.sismember(key, member);
return isMember === 1;
}
async areMembersOfSet(key, members, returnType) {
const replies = await this.redis.smismember(key, members);
if (returnType)
return replies.flatMap((reply, idx) => {
if ((returnType === "included" && reply === 0) ||
(returnType === "excluded" && reply === 1))
return [];
return [members[idx]];
});
return replies.every((reply) => reply === 1);
}
};
RedisService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)(redis_constants_1.REDIS_NAME_TOKEN)),
__metadata("design:paramtypes", [String, core_1.ModuleRef])
], RedisService);
exports.RedisService = RedisService;