diff --git a/src/commands/zadd.js b/src/commands/zadd.js index 2575eda0e..5e99fa1c3 100644 --- a/src/commands/zadd.js +++ b/src/commands/zadd.js @@ -1,7 +1,7 @@ export function zadd(key, ...vals) { // consume options const options = [] - while (['NX', 'XX', 'CH', 'INCR'].includes(vals[0])) { + while (['NX', 'XX', 'GT', 'LT', 'CH', 'INCR'].includes(vals[0])) { options.push(vals.shift()) } @@ -12,12 +12,16 @@ export function zadd(key, ...vals) { // set option vals const nx = options.includes('NX') const xx = options.includes('XX') + const gt = options.includes('GT') + const lt = options.includes('LT') const ch = options.includes('CH') const incr = options.includes('INCR') // validate options if (nx && xx) throw new Error('XX and NX options at the same time are not compatible') + if (gt && lt) + throw new Error('GT and LT options at the same time are not compatible') if (incr && elems > 2) throw new Error('INCR option supports a single increment-element pair') @@ -36,8 +40,15 @@ export function zadd(key, ...vals) { if (map.has(value)) { if (!nx) { + const exist = map.get(value) if (incr) { - score += Number(map.get(value).score) + score += Number(exist.score) + } + if (lt && score >= exist.score || gt && score <= exist.score) { + // null if INCR and we don't update + if(incr) return null + // eslint-disable-next-line no-continue + continue } map.set(value, { score, value }) updated++ @@ -48,6 +59,11 @@ export function zadd(key, ...vals) { map.set(value, { score, value }) added++ } + + // if INCR return value is the new score as a string + if(incr) { + return `${score}` + } } this.data.set(key, map) diff --git a/test/integration/commands/zadd.js b/test/integration/commands/zadd.js index f662c057d..71b38b292 100644 --- a/test/integration/commands/zadd.js +++ b/test/integration/commands/zadd.js @@ -34,6 +34,11 @@ runTwinSuite('zadd', command => { redis[command]('key', ['NX', 'XX'], 1, 'value') ).rejects.toThrow('not compatible') }) + it('should not allow GT and LT options in the same call', async () => { + await expect( + redis[command]('key', ['GT', 'LT'], 1, 'value') + ).rejects.toThrow('not compatible') + }) it('should not update a value that exists with NX option', async () => { await redis[command]('key', 'NX', 1, 'value') expect(await redis[command]('key', 'NX', 2, 'value')).toBe(0) @@ -46,6 +51,42 @@ runTwinSuite('zadd', command => { await redis[command]('key', 1, 'value') expect(await redis[command]('key', ['XX', 'CH'], 2, 'value')).toBe(1) }) + it('should not update a value with equal or lower score with LT option', async () => { + await redis[command]('key', 'LT', 2, 'value') + expect(await redis[command]('key', ['LT', 'CH'], 2, 'value')).toBe(0) + expect(await redis[command]('key', ['LT', 'CH'], 3, 'value')).toBe(0) + expect(await redis[command]('key', ['LT', 'CH'], 1, 'value')).toBe(1) + }) + it('should not update a value with equal or higher score with GT option', async () => { + await redis[command]('key', 'GT', 2, 'value') + expect(await redis[command]('key', ['GT', 'CH'], 2, 'value')).toBe(0) + expect(await redis[command]('key', ['GT', 'CH'], 1, 'value')).toBe(0) + expect(await redis[command]('key', ['GT', 'CH'], 3, 'value')).toBe(1) + }) + it('should handle INCR and LT option', async () => { + await redis[command]('key', 2, 'value') + // LT + INCR w/ non-negative should never work + expect(await redis[command]('key', ['LT', 'CH', 'INCR'], 0, 'value')).toBe(null) + expect(await redis[command]('key', ['LT', 'CH', 'INCR'], -2, 'value')).toBe('0') + expect(await redis.zrange('key', 0, -1, 'WITHSCORES')).toEqual([ + 'value', + '0', + ]) + }) + it('should handle INCR and GT option', async () => { + await redis[command]('key', 2, 'value') + expect(await redis[command]('key', ['GT', 'CH', 'INCR'], 2, 'value')).toBe('4') + expect(await redis.zrange('key', 0, -1, 'WITHSCORES')).toEqual([ + 'value', + '4', + ]) + // should work even if specified score is lower + expect(await redis[command]('key', ['GT', 'CH', 'INCR'], 1, 'value')).toBe('5') + expect(await redis.zrange('key', 0, -1, 'WITHSCORES')).toEqual([ + 'value', + '5', + ]) + }) it('should handle INCR option', async () => { await redis[command]('key', 1, 'value') await redis[command]('key', 'INCR', 2, 'value')