Skip to content

Commit

Permalink
feat: add
Browse files Browse the repository at this point in the history
  • Loading branch information
mrnagydavid committed Sep 26, 2024
1 parent a26f76b commit 126224d
Show file tree
Hide file tree
Showing 4 changed files with 413 additions and 0 deletions.
132 changes: 132 additions & 0 deletions src/redisClient.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { localTime } from '@naturalcycles/js-lib'
import { RedisClient } from './redisClient'

let client: RedisClient

beforeAll(() => {
client = new RedisClient()
})

beforeEach(async () => {
await client.dropTable('test')
})

afterAll(async () => {
await client.dropTable('test')
await client.disconnect()
})

describe('hashmap functions', () => {
test('hset should save a map', async () => {
await client.hset('test:key', { foo: 'bar' })

const result = await client.hgetall('test:key')

expect(result).toEqual({ foo: 'bar' })
})

test('should store/fetch numbers as strings', async () => {
await client.hset('test:key', { one: 1 })

const result = await client.hgetall('test:key')

expect(result).toEqual({ one: '1' })
})

test('hgetall should not fetch nested objects', async () => {
await client.hset('test:key', { nested: { one: 1 } })

const result = await client.hgetall('test:key')

expect(result).toEqual({ nested: '[object Object]' })
})

test('hget should fetch map property', async () => {
await client.hset('test:key', { foo: 'bar' })

const result = await client.hget('test:key', 'foo')

expect(result).toBe('bar')
})

test('hget should fetch value as string', async () => {
await client.hset('test:key', { one: 1 })

const result = await client.hget('test:key', 'one')

expect(result).toBe('1')
})

test('hmgetBuffer should get the values of the fields as strings', async () => {
await client.hset('test:key', { one: 1, two: 2, three: 3 })

const result = await client.hmget('test:key', ['one', 'three'])

expect(result).toEqual(['1', '3'])
})

test('hmgetBuffer should get the values of the fields as buffers', async () => {
await client.hset('test:key', { one: 1, two: 2, three: 3 })

const result = await client.hmgetBuffer('test:key', ['one', 'three'])

expect(result).toEqual([Buffer.from('1'), Buffer.from('3')])
})

test('hincr should change the value and return with a numeric result', async () => {
await client.hset('test:key', { one: 1 })

const result = await client.hincr('test:key', 'one', -2)

expect(result).toBe(-1)
})

test('hincr should increase the value by 1 by default', async () => {
await client.hset('test:key', { one: 1 })

const result = await client.hincr('test:key', 'one')

expect(result).toBe(2)
})

test('hincr should set the value to 1 for a non-existing field', async () => {
const result = await client.hincr('test:key', 'one')

expect(result).toBe(1)
})

test('hScanCount should return the number of keys in the hash', async () => {
await client.hset('test:key', { one: 1, two: 2, three: 3 })

const result = await client.hScanCount('test:key', {})

expect(result).toBe(3)
})

test('hScanCount with a match pattern should return the number of matching keys in the hash', async () => {
await client.hset('test:key', { one: 1, two: 2, three: 3 })

const result = await client.hScanCount('test:key', { match: 't*' })

expect(result).toBe(2)
})

test('hdel should delete a fields from the hash', async () => {
await client.hset('test:key', { one: 1, two: 2, three: 3 })

await client.hdel('test:key', ['two', 'three'])

const result = await client.hgetall('test:key')
expect(result).toEqual({ one: '1' })
})

test('hsetWithTTL should set the fields with expiry', async () => {
const now = localTime.now().unix

await client.hsetWithTTL('test:key', { foo1: 'bar' }, now + 1000)
await client.hsetWithTTL('test:key', { foo2: 'bar' }, now - 1)

const result = await client.hgetall('test:key')
expect(result).toEqual({ foo1: 'bar' })
})
})
64 changes: 64 additions & 0 deletions src/redisClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
AnyObject,
CommonLogger,
NullableBuffer,
NullableString,
Expand Down Expand Up @@ -124,6 +125,38 @@ export class RedisClient implements CommonClient {
await this.redis().set(key, value)
}

async hgetall<T extends Record<string, string> = Record<string, string>>(
key: string,
): Promise<T | null> {
const result = await this.redis().hgetall(key)
if (Object.keys(result).length === 0) return null
return result as T
}

async hget(key: string, field: string): Promise<NullableString> {
return await this.redis().hget(key, field)
}

async hset(key: string, value: AnyObject): Promise<void> {
await this.redis().hset(key, value)
}

async hdel(key: string, fields: string[]): Promise<void> {
await this.redis().hdel(key, ...fields)
}

async hmget(key: string, fields: string[]): Promise<NullableString[]> {
return await this.redis().hmget(key, ...fields)
}

async hmgetBuffer(key: string, fields: string[]): Promise<NullableBuffer[]> {
return await this.redis().hmgetBuffer(key, ...fields)
}

async hincr(key: string, field: string, increment: number = 1): Promise<number> {
return await this.redis().hincrby(key, field, increment)
}

async setWithTTL(
key: string,
value: string | number | Buffer,
Expand All @@ -132,6 +165,20 @@ export class RedisClient implements CommonClient {
await this.redis().set(key, value, 'EXAT', expireAt)
}

async hsetWithTTL(
key: string,
value: AnyObject,
expireAt: UnixTimestampNumber
): Promise<void> {
const valueKeys = Object.keys(value)
const numberOfKeys = valueKeys.length
const keyList = valueKeys.join(' ')
const commandString = `HEXPIREAT ${key} ${expireAt} FIELDS ${numberOfKeys} ${keyList}`
const [command, ...args] = commandString.split(' ')
await this.redis().hset(key, value)
await this.redis().call(command!, args)
}

async mset(obj: Record<string, string | number>): Promise<void> {
await this.redis().mset(obj)
}
Expand Down Expand Up @@ -205,6 +252,23 @@ export class RedisClient implements CommonClient {
return count
}

hscanStream(key: string, opt: ScanStreamOptions): ReadableTyped<string[]> {
return this.redis().hscanStream(key, opt)
}

async hScanCount(key: string, opt: ScanStreamOptions): Promise<number> {
let count = 0

const stream = await this.redis().hscanStream(key, opt)

await stream.forEach((keyValueList: string[]) => {
console.log({ keyValueList })
count += keyValueList.length / 2
})

return count
}

async withPipeline(fn: (pipeline: ChainableCommander) => Promisable<void>): Promise<void> {
const pipeline = this.redis().pipeline()
await fn(pipeline)
Expand Down
Loading

0 comments on commit 126224d

Please sign in to comment.