Skip to content

Commit 8a6dbd3

Browse files
author
Julien Kernec'h
committed
Merge branch 'release/0.5.0'
2 parents b669058 + 025a799 commit 8a6dbd3

File tree

5 files changed

+183
-19
lines changed

5 files changed

+183
-19
lines changed

README.md

+35
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,41 @@ redisCache.wrap(key, function (cb) {
7575
console.log(user);
7676
});
7777
});
78+
79+
// The del() method accepts a single key or array of keys,
80+
// with or without a callback.
81+
redisCache.set('foo', 'bar', function () {
82+
redisCache.set('bar', 'baz', function() {
83+
redisCache.set('baz', 'foo', function() {
84+
redisCache.del('foo');
85+
redisCache.del(['bar', 'baz'], function() { });
86+
});
87+
});
88+
});
89+
90+
// The keys() method uses the Redis SCAN command and accepts
91+
// optional `pattern` and `options` arguments. The `pattern`
92+
// must be a Redis glob-style string and defaults to '*'. The
93+
// options argument must be an object and accepts a single
94+
// `scanCount` property, which determines the number of elements
95+
// returned internally per call to SCAN. The default `scanCount`
96+
// is 100.
97+
redisCache.set('foo', 'bar', function () {
98+
redisCache.set('far', 'boo', function () {
99+
redisCache.keys('fo*', function (err, arrayOfKeys) {
100+
// arrayOfKeys: ['foo']
101+
});
102+
103+
redisCache.keys(function (err, arrayOfKeys) {
104+
// arrayOfKeys: ['foo', 'far']
105+
});
106+
107+
redisCache.keys('fa*', { scanCount: 10 }, function (err, arrayOfKeys) {
108+
// arrayOfKeys: ['far']
109+
});
110+
});
111+
});
112+
78113
```
79114

80115
### Multi-store

index.js

+59-8
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ function redisStore(args) {
186186
options = options || {};
187187
options.parse = true;
188188

189-
cb = cb ? cb : (err, result) => err ? reject(err) : resolve(result)
189+
cb = cb ? cb : (err, result) => err ? reject(err) : resolve(result);
190190

191191
var compress = (options.compress || options.compress === false) ? options.compress : redisOptions.compress;
192192
if (compress) {
@@ -222,7 +222,7 @@ function redisStore(args) {
222222
options = {};
223223
}
224224

225-
cb = cb ? cb : (err, result) => err ? reject(err) : resolve(result)
225+
cb = cb ? cb : (err, result) => err ? reject(err) : resolve(result);
226226

227227
if (!self.isCacheableValue(value)) {
228228
return cb(new Error('value cannot be ' + value));
@@ -267,7 +267,7 @@ function redisStore(args) {
267267
/**
268268
* Delete value of a given key
269269
* @method del
270-
* @param {String} key - The cache key
270+
* @param {String|Array} key - The cache key or array of keys to delete
271271
* @param {Object} [options] - The options (optional)
272272
* @param {Function} [cb] - A callback that returns a potential error, otherwise null
273273
*/
@@ -281,7 +281,17 @@ function redisStore(args) {
281281
if (err) {
282282
return cb && cb(err);
283283
}
284-
conn.del(key, handleResponse(conn, cb));
284+
285+
if (Array.isArray(key)) {
286+
var multi = conn.multi();
287+
for (var i = 0, l = key.length; i < l; ++i) {
288+
multi.del(key[i]);
289+
}
290+
multi.exec(handleResponse(conn, cb));
291+
}
292+
else {
293+
conn.del(key, handleResponse(conn, cb));
294+
}
285295
});
286296
};
287297

@@ -315,22 +325,63 @@ function redisStore(args) {
315325
};
316326

317327
/**
318-
* Returns all keys matching pattern.
328+
* Returns all keys matching pattern using the SCAN command.
319329
* @method keys
320-
* @param {String} pattern - The pattern used to match keys
330+
* @param {String} [pattern] - The pattern used to match keys (default: *)
331+
* @param {Object} [options] - The options (default: {})
332+
* @param {number} [options.scanCount] - The number of keys to traverse with each call to SCAN (default: 100)
321333
* @param {Function} cb - A callback that returns a potential error and the response
322334
*/
323-
self.keys = function(pattern, cb) {
335+
self.keys = function(pattern, options, cb) {
336+
337+
// Account for all argument permutations.
338+
// Only cb supplied.
324339
if (typeof pattern === 'function') {
325340
cb = pattern;
341+
options = {};
326342
pattern = '*';
327343
}
344+
// options and cb supplied.
345+
else if (typeof pattern === 'object') {
346+
cb = options;
347+
options = pattern;
348+
pattern = '*';
349+
}
350+
// pattern and cb supplied.
351+
else if (typeof options === 'function') {
352+
cb = options;
353+
options = {};
354+
}
328355

329356
connect(function(err, conn) {
330357
if (err) {
331358
return cb && cb(err);
332359
}
333-
conn.keys(pattern, handleResponse(conn, cb));
360+
361+
// Use an object to dedupe as scan can return duplicates
362+
var keysObj = {};
363+
var scanCount = Number(options.scanCount) || 100;
364+
365+
(function nextBatch(cursorId) {
366+
conn.scan(cursorId, 'match', pattern, 'count', scanCount, function (err, result) {
367+
if (err) {
368+
handleResponse(conn, cb)(err);
369+
}
370+
371+
var nextCursorId = result[0];
372+
var keys = result[1];
373+
374+
for (var i = 0, l = keys.length; i < l; ++i) {
375+
keysObj[keys[i]] = 1;
376+
}
377+
378+
if (nextCursorId !== '0') {
379+
return nextBatch(nextCursorId);
380+
}
381+
382+
handleResponse(conn, cb)(null, Object.keys(keysObj));
383+
});
384+
})(0);
334385
});
335386
};
336387

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cache-manager-redis",
3-
"version": "0.4.0",
3+
"version": "0.5.0",
44
"description": "Redis store for the node-cache-manager",
55
"main": "index.js",
66
"scripts": {

test/lib/redis-store-spec.js

+82-10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ before(function () {
3535
});
3636
});
3737

38+
beforeEach(function(done) {
39+
redisCache.reset(function() {
40+
done();
41+
});
42+
});
43+
3844
describe ('initialization', function () {
3945

4046
it('should create a store with password instead of auth_pass (auth_pass is deprecated for redis > 2.5)', function (done) {
@@ -257,6 +263,30 @@ describe('del', function () {
257263
});
258264
});
259265

266+
it('should delete multiple values for a given array of keys', function (done) {
267+
redisCache.set('foo', 'bar', function () {
268+
redisCache.set('bar', 'baz', function () {
269+
redisCache.set('baz', 'foo', function () {
270+
redisCache.del(['foo', 'bar', 'baz'], function (err) {
271+
assert.equal(err, null);
272+
done();
273+
});
274+
});
275+
});
276+
});
277+
});
278+
279+
it('should delete multiple values for a given array of keys without callback', function (done) {
280+
redisCache.set('foo', 'bar', function () {
281+
redisCache.set('bar', 'baz', function () {
282+
redisCache.set('baz', 'foo', function () {
283+
redisCache.del(['foo', 'bar', 'baz']);
284+
done();
285+
});
286+
});
287+
});
288+
});
289+
260290
it('should return an error if there is an error acquiring a connection', function (done) {
261291
var pool = redisCache.store._pool;
262292
sinon.stub(pool, 'acquireDb').yieldsAsync('Something unexpected');
@@ -335,22 +365,64 @@ describe('ttl', function () {
335365
describe('keys', function () {
336366
it('should return an array of keys for the given pattern', function (done) {
337367
redisCache.set('foo', 'bar', function () {
338-
redisCache.keys('f*', function (err, arrayOfKeys) {
339-
assert.equal(err, null);
340-
assert.notEqual(arrayOfKeys, null);
341-
assert.notEqual(arrayOfKeys.indexOf('foo'), -1);
342-
done();
368+
redisCache.set('far', 'boo', function () {
369+
redisCache.set('faz', 'bam', function () {
370+
redisCache.keys('f*', function (err, arrayOfKeys) {
371+
assert.equal(err, null);
372+
assert.notEqual(arrayOfKeys, null);
373+
assert.notEqual(arrayOfKeys.indexOf('foo'), -1);
374+
assert.equal(arrayOfKeys.length, 3);
375+
done();
376+
});
377+
});
378+
});
379+
});
380+
});
381+
382+
it('should accept a scanCount option', function (done) {
383+
redisCache.set('foo', 'bar', function () {
384+
redisCache.set('far', 'boo', function () {
385+
redisCache.set('faz', 'bam', function () {
386+
redisCache.keys('f*', { scanCount: 10 }, function (err, arrayOfKeys) {
387+
assert.equal(err, null);
388+
assert.notEqual(arrayOfKeys, null);
389+
assert.notEqual(arrayOfKeys.indexOf('foo'), -1);
390+
assert.equal(arrayOfKeys.length, 3);
391+
done();
392+
});
393+
});
343394
});
344395
});
345396
});
346397

347398
it('should return an array of keys without pattern', function (done) {
348399
redisCache.set('foo', 'bar', function () {
349-
redisCache.keys(function (err, arrayOfKeys) {
350-
assert.equal(err, null);
351-
assert.notEqual(arrayOfKeys, null);
352-
assert.notEqual(arrayOfKeys.indexOf('foo'), -1);
353-
done();
400+
redisCache.set('far', 'boo', function () {
401+
redisCache.set('faz', 'bam', function () {
402+
redisCache.keys(function (err, arrayOfKeys) {
403+
assert.equal(err, null);
404+
assert.notEqual(arrayOfKeys, null);
405+
assert.notEqual(arrayOfKeys.indexOf('foo'), -1);
406+
assert.equal(arrayOfKeys.length, 3);
407+
done();
408+
});
409+
});
410+
});
411+
});
412+
});
413+
414+
it('should accept scanCount option without pattern', function (done) {
415+
redisCache.set('foo', 'bar', function () {
416+
redisCache.set('far', 'boo', function () {
417+
redisCache.set('faz', 'bam', function () {
418+
redisCache.keys({ scanCount: 10 }, function (err, arrayOfKeys) {
419+
assert.equal(err, null);
420+
assert.notEqual(arrayOfKeys, null);
421+
assert.notEqual(arrayOfKeys.indexOf('foo'), -1);
422+
assert.equal(arrayOfKeys.length, 3);
423+
done();
424+
});
425+
});
354426
});
355427
});
356428
});

test/lib/redis-store-zlib-spec.js

+6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ describe('Compression Tests', function () {
4242
testJson = JSON.stringify(testObject);
4343
});
4444

45+
beforeEach(function(done) {
46+
redisCompressCache.reset(function () {
47+
done();
48+
});
49+
});
50+
4551
describe('compress set', function () {
4652
it('should store a value without ttl', function (done) {
4753
redisCompressCache.set('foo', 'bar', function (err) {

0 commit comments

Comments
 (0)