RedbackA high-level Redis library for Node.JS | |
| lib/Redback.js |
Module dependencies.
|
var redis = require('redis'),
Structure = require('./Structure'),
Channel = require('./Channel').Channel,
Cache = require('./Cache').Cache;
|
Define the available Redis structures.
|
var base = ['Hash','List','Set','SortedSet','Bitfield'];
|
Define the available advanced structures.
|
var advanced = ['KeyPair','DensitySet','CappedList','SocialGraph'];
|
The Redback object wraps the Redis client and acts as a factory
for structures.
param: string host (optional) param: int port (optional) param: Object options (optional) api: public
|
var Redback = exports.Redback = function (host, port, options) {
this.client = redis.createClient.apply(this, arguments);
this.namespace = '';
if (typeof options === 'object') {
this.namespace = options.namespace || '';
}
}
|
Create a new Cache.
|
Redback.prototype.createCache = function (namespace) {
namespace = namespace || 'cache';
if (this.namespace.length) {
namespace = this.namespace + ':' + namespace;
}
return new Cache(this.client, namespace);
}
|
Create a new Channel.
|
Redback.prototype.createChannel = function (channel) {
if (!channel) {
throw new Error('A channel key is required');
}
if (this.namespace.length) {
channel = this.namespace + ':' + channel;
}
return new Channel(this.client, channel);
}
|
Send a (BG)SAVE command to Redis.
|
Redback.prototype.save = function (background, callback) {
if (typeof background === 'function') {
callback = background;
this.client.save(callback);
} else {
this.client.bgsave(callback);
}
return this;
}
|
Create a new Redback client.
param: string host (optional) param: int port (optional) param: Object options (optional) api: public
|
exports.createClient = function (host, port, options) {
return new Redback(host, port, options);
}
|
Add the Redis structures from ./base_structures
|
base.forEach(function (structure) {
Redback.prototype.addStructure(structure,
require('./base_structures/' + structure)[structure]);
});
|
Add the advanced structures from ./advanced_structures
|
advanced.forEach(function (structure) {
Redback.prototype.addStructure(structure,
require('./advanced_structures/' + structure)[structure]);
});
|
Redis constants.
|
Redback.prototype.INF = exports.INF = '+inf';
Redback.prototype.NINF = exports.NINF = '-inf';
|
Export prototypes so that they can be extended.
|
exports.Client = redis.RedisClient;
exports.Structure = Structure.Structure;
exports.Cache = Cache;
exports.Channel = Channel;
|
| lib/Structure.js |
All Redback structures inherit from this.
|
var Structure = exports.Structure = function () {};
|
Create a new Structure by extending the base Structure.
|
exports.new = function (methods) {
return Structure.prototype.extend(methods);
}
|
Expire the structure after a certain number of seconds.
|
Structure.prototype.expire = function (ttl, callback) {
callback = callback || function () {};
this.client.expire(this.key, ttl, callback);
return this;
}
|
Expire the structure at a certain date.
|
Structure.prototype.expireAt = function (when, callback) {
callback = callback || function () {};
if (typeof when.getTime === 'function') {
when = Math.round(when.getTime() / 1000); //ms => s
}
this.client.expireat(this.key, when, callback);
return this;
}
|
Get the number of seconds before the structure expires.
param: Function callback return: this api: public
|
Structure.prototype.ttl = function (callback) {
this.client.ttl(this.key, callback);
return this;
}
|
Remove the structure's associated expiry.
|
Structure.prototype.persist = function (callback) {
callback = callback || function () {};
this.client.persist(this.key, callback);
return this;
}
|
Remove the structure from the Redis database.
|
Structure.prototype.destroy =
Structure.prototype.flush = function (callback) {
callback = callback || function () {};
this.client.del(this.key, callback);
return this;
}
|
A helper for creating atomically auto-incrementing keys.
param: Function callback return: this api: public
|
Structure.prototype.autoincrement = function (callback) {
var key = this.key + ':_autoinc',
multi = this.client.multi();
multi.setnx(key, 1).get(key).incr(key);
multi.exec(function (err, replies) {
if (err) return callback(err, null);
callback(null, replies[1]);
});
return this;
}
|
Takes a redback structure or key string and returns the key.
|
Structure.prototype.getKey = function (key, which) {
which = which || 'key';
if (typeof key[which] !== 'undefined') {
return key[which];
}
return key;
}
|
A helper that extracts the Redis keys from many Structure or string arguments.
param: Array structures param: Function callback return: this api: public
|
Structure.prototype.getKeys = function (structures, which) {
var structures = Array.prototype.slice.call(structures),
callback = structures.pop(),
self = this,
keys = [];
for (var i = 0, l = structures.length; i < l; i++) {
if (Array.isArray(structures[i])) {
structures[i].forEach(function (structure) {
keys.push(self.getKey(structure, which));
});
} else {
keys.push(this.getKey(structures[i], which));
}
}
keys.push(callback);
return keys;
}
|
Add the namespace on to a key.
|
Structure.prototype.namespaceKey = function (key) {
key = key || '';
if (this.namespace.length) {
key = this.namespace + ':' + key;
}
return key;
}
|
Extend the structure.
|
Structure.prototype.extend = function (methods) {
var structure = function (client, key, namespace, init_args) {
this.client = client;
this.namespace = namespace || '';
if (!key) {
throw new Error('A key is required');
}
if (Array.isArray(key)) {
key = key.join(':');
}
this.id = key;
if (this.namespace.length) {
key = this.namespace + ':' + key;
}
this.key = key;
if (typeof this.init === 'function') {
this.init.apply(this, init_args);
}
}, ctor = function () {
this.constructor = structure;
}
ctor.prototype = this;
structure.prototype = new ctor;
structure.__super__ = this;
if (typeof methods === 'object') {
for (var i in methods) {
structure.prototype[i] = methods[i];
}
}
return structure;
}
|
| lib/base_structures/Bitfield.js |
Module dependencies.
|
var Structure = require('../Structure');
|
Wrap the Redis bit commands.
Usage
redback.createBitfield(key);
Reference
http://redis.io/commands#string
Redis Structure
(namespace:)key = string
|
var Bitfield = exports.Bitfield = Structure.new();
|
Get a single bit
|
Bitfield.prototype.get = function (bit, callback) {
callback = callback || function () {};
this.client.getbit(this.key, bit, callback);
return this;
}
|
Set a single bit. The callback receives the previous value.
|
Bitfield.prototype.set = function (bit, value, callback) {
callback = callback || function () {};
this.client.setbit(this.key, bit, value ? 1 : 0, callback);
return this;
}
|
| lib/base_structures/Hash.js |
Module dependencies.
|
var Structure = require('../Structure');
|
A wrapper for the Redis hash type.
Usage
redback.createHash(key);
Reference
http://redis.io/topics/data-types#hashes
Redis Structure
(namespace:)key = hash(key => value)
|
var Hash = exports.Hash = Structure.new();
|
Get an array of hash keys.
param: Function callback return: this api: public
|
Hash.prototype.keys = function (callback) {
this.client.hkeys(this.key, callback);
return this;
}
|
Get an array of hash values.
param: Function callback return: this api: public
|
Hash.prototype.values = function (callback) {
this.client.hvals(this.key, callback);
return this;
}
|
Get the number of hash keys.
param: Function callback return: this api: public
|
Hash.prototype.length = function (callback) {
this.client.hlen(this.key, callback);
return this;
}
|
Delete a hash key.
|
Hash.prototype.delete = Hash.prototype.del = function (hash_key, callback) {
callback = callback || function () {};
this.client.hdel(this.key, hash_key, callback);
return this;
}
|
Checks whether a hash key exists.
param: string hash_key param: Function callback return: this api: public
|
Hash.prototype.exists = function (hash_key, callback) {
this.client.hexists(this.key, hash_key, callback);
return this;
}
|
Sets one or more key/value pairs.
To set one key/value pair:
hash.set('foo', 'bar', callback);
To set multiple:
hash.set({key1:'value1', key2:'value2}, callback);
param: string | Object hash_key param: string value (optional) param: Function callback (optional) return: this api: public
|
Hash.prototype.set = function (hash_key, value, callback) {
if (typeof hash_key === 'object') {
callback = value || function () {};
this.client.hmset(this.key, hash_key, callback);
} else {
callback = callback || function () {};
this.client.hset(this.key, hash_key, value, callback);
}
return this;
}
|
Sets a key/value pair if the key doesn't already exist.
param: string hash_key param: string value param: Function callback return: this api: public
|
Hash.prototype.add = function (hash_key, value, callback) {
callback = callback || function () {};
this.client.hsetnx(this.key, hash_key, value, callback);
return this;
}
|
Gets one or more key/value pairs.
To get all key/value pairs in the hash:
hash.get('foo', callback);
To get certain key/value pairs:
hash.get(['foo','bar'], callback);
hash.get('foo', callback);
|
Hash.prototype.get = function (hash_key, callback) {
if (typeof hash_key === 'function') {
callback = hash_key;
this.client.hgetall(this.key, callback);
} else if (Array.isArray(hash_key)) {
this.client.hmget(this.key, hash_key, callback)
} else {
this.client.hget(this.key, hash_key, callback);
}
return this;
}
|
Increment the specified hash value.
|
Hash.prototype.increment =
Hash.prototype.incrBy = function (hash_key, amount, callback) {
callback = callback || function () {};
if (typeof amount === 'function') {
callback = amount;
amount = 1;
}
this.client.hincrby(this.key, hash_key, amount, callback);
return this;
}
|
Decrement the specified hash value.
|
Hash.prototype.decrement =
Hash.prototype.decrBy = function (hash_key, amount, callback) {
callback = callback || function () {};
if (typeof amount === 'function') {
callback = amount;
amount = 1;
}
this.client.hincrby(this.key, hash_key, -1 * amount, callback);
return this;
}
|
| lib/base_structures/List.js |
Module dependencies.
|
var Structure = require('../Structure');
|
A wrapper for the Redis list type.
Usage
redback.createList(key);
Reference
http://redis.io/topics/data-types#lists
Redis Structure
(namespace:)key = list(values)
|
var List = exports.List = Structure.new();
|
Get the list as an array.
param: Function callback return: this api: public
|
List.prototype.values = function (callback) {
this.client.lrange(this.key, 0, -1, callback);
return this;
}
|
Get a range of list elements.
|
List.prototype.range = function (start, end, callback) {
if (typeof end === 'function') {
callback = end;
end = -1;
}
this.client.lrange(this.key, start, end, callback);
return this;
}
|
Get one or more elements starting at the specified index.
|
List.prototype.get = function (index, count, callback) {
if (typeof count === 'function') {
callback = count;
this.client.lindex(this.key, index, callback);
} else {
this.client.lrange(this.key, index, index + count - 1, callback);
}
return this;
}
|
Cap the length of the list.
|
List.prototype.cap = function (length, keep_earliest, callback) {
callback = callback || function () {};
var start = 0, end = -1;
if (typeof keep_earliest === 'function') {
start = -1 * length;
callback = keep_earliest;
} else {
end = length - 1;
}
this.client.ltrim(this.key, start, end, callback);
return this;
}
|
Remove one or more list elements matching the value.
|
List.prototype.remove = function (value, count, callback) {
callback = callback || function () {};
if (typeof count === 'function') {
callback = count;
count = 1;
}
this.client.lrem(this.key, count, value, callback);
return this;
}
|
Trim a list to the specified bounds.
|
List.prototype.trim = function (start, end, callback) {
callback = callback || function () {};
this.client.ltrim(this.key, start, end, callback);
return this;
}
|
Insert an element before the specified pivot.
|
List.prototype.insertBefore = function (pivot, value, callback) {
callback = callback || function () {};
this.client.linsert(this.key, 'BEFORE', pivot, value, callback);
return this;
}
|
Insert an element after the specified pivot.
|
List.prototype.insertAfter = function (pivot, value, callback) {
callback = callback || function () {};
this.client.linsert(this.key, 'AFTER', pivot, value, callback);
return this;
}
|
Set the element at the specified index.
|
List.prototype.set = function (index, value, callback) {
callback = callback || function () {};
this.client.lset(this.key, index, value, callback);
return this;
}
|
Get the number of elements in the list.
param: Function callback return: this api: public
|
List.prototype.length = function (callback) {
this.client.llen(this.key, callback);
return this;
}
|
Get and remove the first element in the list.
|
List.prototype.shift = function (wait, callback) {
if (typeof wait === 'function') {
callback = wait;
this.client.lpop(this.key, callback);
} else {
this.client.blpop(this.key, wait, callback);
}
return this;
}
|
Get and remove the last element in the list.
|
List.prototype.pop = function (wait, callback) {
if (typeof wait === 'function') {
callback = wait;
this.client.rpop(this.key, callback);
} else {
this.client.brpop(this.key, wait, callback);
}
return this;
}
|
Add one or more elements to the start of the list.
|
List.prototype.unshift = List.prototype.lpush = function (values, callback) {
callback = callback || function () {};
if (Array.isArray(values)) {
var multi = this.client.multi(), key = this.key;
values.reverse().forEach(function (value) {
multi.lpush(key, value);
});
multi.exec(callback);
} else {
this.client.lpush(this.key, values, callback);
}
return this;
}
|
Add one or more elements to the end of the list.
|
List.prototype.push = List.prototype.add = function (values, callback) {
callback = callback || function () {};
if (Array.isArray(values)) {
var multi = this.client.multi(), key = this.key;
values.forEach(function (value) {
multi.rpush(key, value);
});
multi.exec(callback);
} else {
this.client.rpush(this.key, values, callback);
}
return this;
}
|
Remove the last element of the list and add it to the start
of another list.
param: String | List list param: bool wait (optional) - seconds to block while waiting param: Function callback (optional) return: this api: public
|
List.prototype.popShift = function (list, wait, callback) {
callback = callback || function () {};
list = this.getKey(list);
if (typeof wait === 'function') {
callback = wait;
this.client.rpoplpush(this.key, list, callback);
} else {
this.client.brpoplpush(this.key, list, wait, callback);
}
return this;
}
|
| lib/base_structures/Set.js |
Module dependencies.
|
var Structure = require('../Structure');
|
A wrapper for the Redis set type.
Usage
redback.createSet(key);
Reference
http://redis.io/topics/data-types#sets
Redis Structure
(namespace:)key = set(elements)
|
var Set = exports.Set = Structure.new();
|
Add one or more elements to the set.
|
Set.prototype.add = function (element, callback) {
callback = callback || function () {};
if (Array.isArray(element)) {
return this.addAll(element, callback);
}
this.client.sadd(this.key, element, callback);
return this;
}
|
Remove one or more elements from the set.
|
Set.prototype.remove = function (element, callback) {
callback = callback || function () {};
if (Array.isArray(element)) {
return this.removeAll(element, callback);
}
this.client.srem(this.key, element, callback);
return this;
}
|
Get an array of elements in the set.
param: Function callback return: this api: public
|
Set.prototype.elements = Set.prototype.members = function (callback) {
this.client.smembers(this.key, callback);
return this;
}
|
Move an element to another set.
|
Set.prototype.move = function (dest, element, callback) {
callback = callback || function () {};
this.client.smove(this.key, this.getKey(dest), element, callback);
return this;
}
|
Check whether an element exists in the set.
param: string element param: Function callback return: this api: public
|
Set.prototype.exists = Set.prototype.contains = function (element, callback) {
this.client.sismember(this.key, element, callback);
return this;
}
|
Get the length (cardinality) of the set.
param: Function callback return: this api: public
|
Set.prototype.length = Set.prototype.cardinality = function (callback) {
this.client.scard(this.key, callback);
return this;
}
|
Get a random element from the set and optionally remove it.
|
Set.prototype.random = function (remove, callback) {
if (typeof remove === 'function') {
callback = remove;
this.client.srandmember(this.key, callback);
} else {
this.client.spop(this.key, callback);
}
return this;
}
|
Get the intersection of one or more sets.
|
Set.prototype.inter = function (sets, callback) {
sets = this.getKeys(arguments);
sets.unshift(this.key);
this.client.sinter.apply(this.client, sets);
return this;
}
|
Get the intersection of one or more sets and store it another
set (dest).
|
Set.prototype.interStore = function (dest, sets, callback) {
sets = this.getKeys(arguments);
dest = sets.shift();
sets.unshift(dest, this.key);
this.client.sinterstore.apply(this.client, sets);
return this;
}
|
Get the union of one or more sets.
|
Set.prototype.union = function (sets, callback) {
sets = this.getKeys(arguments);
sets.unshift(this.key);
this.client.sunion.apply(this.client, sets);
return this;
}
|
Get the union of one or more sets and store it another
set (dest).
|
Set.prototype.unionStore = function (dest, sets, callback) {
sets = this.getKeys(arguments);
dest = sets.shift();
sets.unshift(dest, this.key);
this.client.sunionstore.apply(this.client, sets);
return this;
}
|
Get the difference of one or more sets.
|
Set.prototype.diff = function (sets, callback) {
sets = this.getKeys(arguments);
sets.unshift(this.key);
this.client.sdiff.apply(this.client, sets);
return this;
}
|
Get the difference of one or more sets and store it another
set (dest).
|
Set.prototype.diffStore = function (dest, sets, callback) {
sets = this.getKeys(arguments);
dest = sets.shift();
sets.unshift(dest, this.key);
this.client.sdiffstore.apply(this.client, sets);
return this;
}
|
| lib/base_structures/SortedSet.js |
Module dependencies.
|
var Structure = require('../Structure');
|
A wrapper for the Redis sorted set (zset) type. Each element has a
score which is used to rank and order all elements in the set. Elements
are ranked from lowest score to highest (the lowest score has
a rank of 0)
Usage
redback.createSortedSet(key);
Reference
http://redis.io/topics/data-types#sorted-sets
Redis Structure
(namespace:)key = zset(score => element)
|
var SortedSet = exports.SortedSet = Structure.new();
|
Add one or more elements to the set.
To add a single element and score:
set.add(12, 'foo', callback);
To add multiple elements/scores:
set.add({foo:12, bar:3}, callback);
param: int score (optional) param: string | Object element(s) param: Function callback (optional) return: this api: public
|
SortedSet.prototype.add = function (score, element, callback) {
callback = callback || function () {};
if (typeof score === 'object') {
callback = element;
element = score;
return this.addAll(element, callback);
}
this.client.zadd(this.key, score, element, callback);
return this;
}
|
Remove one or more elements from the set.
|
SortedSet.prototype.remove = function (element, callback) {
callback = callback || function () {};
if (Array.isArray(element)) {
return this.removeAll(element, callback);
}
this.client.zrem(this.key, element, callback);
return this;
}
|
Get the number of elements in the set.
param: Function callback return: this api: public
|
SortedSet.prototype.length = function (callback) {
this.client.zcard(this.key, callback);
return this;
}
|
Check whether an element exists in the set.
param: string element param: Function callback return: this api: public
|
SortedSet.prototype.exists =
SortedSet.prototype.contains = function (element, callback) {
this.client.zscore(this.key, element, function (err, score) {
callback(err, score != null);
});
return this;
}
|
Get the rank of the specified element.
param: string element param: Function callback return: this api: public
|
SortedSet.prototype.rank = function (element, callback) {
this.client.zrank(this.key, element, callback)
return this;
}
|
Get the score of the specified element.
param: string element param: Function callback return: this api: public
|
SortedSet.prototype.score = function (element, callback) {
this.client.zscore(this.key, element, callback)
return this;
}
|
Increment the specified element's score.
|
SortedSet.prototype.increment =
SortedSet.prototype.incrBy = function (element, amount, callback) {
callback = callback || function () {};
if (typeof amount === 'function') {
callback = amount;
amount = 1;
}
this.client.zincrby(this.key, amount, element, callback);
return this;
}
|
Decrement the specified element's score.
|
SortedSet.prototype.decrement =
SortedSet.prototype.decrBy = function (element, amount, callback) {
callback = callback || function () {};
if (typeof amount === 'function') {
callback = amount;
amount = 1;
}
this.client.zincrby(this.key, -1 * amount, element, callback);
return this;
}
|
Get all elements in the set as an object {element: score, ...} .
If without_scores is true then just an array of elements is returned.
|
SortedSet.prototype.get = function (without_scores, callback) {
if (typeof without_scores === 'function') {
callback = without_scores;
this.client.zrange(this.key, 0, -1, 'WITHSCORES', this.parseScores(callback));
} else {
this.client.zrange(this.key, 0, -1, callback);
}
return this;
}
|
Get elements with scores between the specified range. Elements are returned
as an object {element: score, ..} and ordered from highest score to lowest.
Note that the start and end range is inclusive and can be an integer or
the constants redback.INF to represent infinity, or redback.NINF to
represent negative infinity. start must be <= end .
param: int start param: int end param: int count (optional) - the maximum number of elements to return param: int offset (optional) - if using count, start at this offset param: Function callback return: this api: public
|
SortedSet.prototype.getScores = function (start, end, count, offset, callback) {
if (null === start) start = '-inf';
if (null === end) end = '+inf';
if (typeof count === 'function') {
callback = count;
this.client.zrangebyscore(this.key, start, end,
'WITHSCORES', this.parseScores(callback));
return this;
} else if (typeof offset === 'function') {
callback = offset;
offset = 0;
}
this.client.zrangebyscore(this.key, start, end, 'WITHSCORES',
'LIMIT', offset, count, this.parseScores(callback));
return this;
}
|
The same as getScores() but elements are ordered from lowest score to
highest.
Note that end must be <= start .
param: int start param: int end param: int count (optional) - the maximum number of elements to return param: int offset (optional) - if using count, start at this offset param: Function callback return: this api: public
|
SortedSet.prototype.getScoresReverse = function (start, end, count, offset, callback) {
if (null === start) start = '+inf';
if (null === end) end = '-inf';
if (typeof count === 'function') {
callback = count;
this.client.zrevrangebyscore(this.key, start, end,
'WITHSCORES', this.parseScores(callback));
return this;
} else if (typeof offset === 'function') {
callback = offset;
offset = 0;
}
this.client.zrevrangebyscore(this.key, start, end, 'WITHSCORES',
'LIMIT', offset, count, this.parseScores(callback));
return this;
}
|
Remove elements with scores between the specified range (inclusive).
|
SortedSet.prototype.removeScores = function (start, end, callback) {
callback = callback || function () {};
if (null === start) start = '-inf';
if (null === end) end = '+inf';
this.client.zremrangebyscore(this.key, start, end, callback);
return this;
}
|
Count the number of elements with scores between the specified
range (inclusive).
param: int start param: int end param: Function callback return: this api: public
|
SortedSet.prototype.countScores = function (start, end, callback) {
if (null === start) start = '-inf';
if (null === end) end = '+inf';
this.client.zcount(this.key, start, end, callback);
return this;
}
|
Get elements with ranks between the specified range (inclusive).
To get the first 3 elements in the set (with the highest scores):
set.getRanks(0, 2, callback);
To get the last 3 elements in the set (lowest scores):
set.getRanks(-3, -1, callback);
param: int start param: int end param: Function callback return: this api: public
|
SortedSet.prototype.getRanks = function (start, end, callback) {
if (null === start) start = 0;
if (null === end) end = -1;
this.client.zrange(this.key, start, end,
'WITHSCORES', this.parseScores(callback));
return this;
}
|
The same as getRanks() but elements are ordered from lowest score
to the highest.
Note that start and end have been deliberately switched for consistency.
getScoresReverse(arg1, arg2, ..) expects arg1 >= arg2 and so does this
method.
param: int end param: int start param: Function callback return: this api: public
|
SortedSet.prototype.getRanksReverse = function (end, start, callback) {
if (null === start) start = -1;
if (null === end) end = 0;
this.client.zrevrange(this.key, start, end,
'WITHSCORES', this.parseScores(callback));
return this;
}
|
Remove elements with ranks between the specified range (inclusive).
|
SortedSet.prototype.removeRanks = function (start, end, callback) {
callback = callback || function () {};
if (null === start) start = -1;
if (null === end) end = 0;
this.client.zremrangebyrank(this.key, start, end, callback);
return this;
}
|
Get count elements with the highest scores.
param: int count param: Function callback return: this api: public
|
SortedSet.prototype.highestScores = function (count, callback) {
this.getRanks(-1 * count, -1, callback);
return this;
}
|
Get count elements with the lowest scores.
param: int count param: Function callback return: this api: public
|
SortedSet.prototype.lowestScores = function (count, callback) {
this.getRanks(0, count - 1, callback);
return this;
}
|
Get the intersection of one or more sets. For more information on weights,
aggregate functions, etc. see: http://redis.io/commands/zinterstore
param: int dest param: string | Set | Array set(s) param: int | Array weights (optional) param: string aggregate (optional) - either SUM, MIN or MAX param: Function callback return: this api: public
|
SortedSet.prototype.inter = function (dest, sets, weights, aggregate, callback) {
var args = [], self = this;
args.push(this.getKey(dest));
if (typeof weights === 'function') {
callback = weights;
weights = aggregate = false;
} else if (typeof aggregate === 'function') {
callback = aggregate;
aggregate = false;
}
if (Array.isArray(sets)) {
args.push(sets.length);
sets.forEach(function (set) {
args.push(self.getKey(set));
});
} else {
args.push(1, this.getKey(sets));
}
if (weights) {
args.push('WEIGHTS');
if (Array.isArray(weights)) {
weights.forEach(function (weight) {
args.push(weight);
});
} else {
args.push(weights);
}
}
if (aggregate) {
args.push('AGGREGATE', aggregate);
}
args.push(callback);
this.client.zinterstore.apply(this.client, args);
return this;
}
|
Get the union of one or more sets. For more information on weights,
aggregate functions, etc. see: http://redis.io/commands/zunionstore
param: int dest param: string | Set | Array set(s) param: int | Array weights (optional) param: string aggregate (optional) - either SUM, MIN or MAX param: Function callback return: this api: public
|
SortedSet.prototype.union = function (dest, sets, weights, aggregate, callback) {
var args = [], self = this;
args.push(this.getKey(dest));
if (typeof weights === 'function') {
callback = weights;
weights = aggregate = false;
} else if (typeof aggregate === 'function') {
callback = aggregate;
aggregate = false;
}
if (Array.isArray(sets)) {
args.push(sets.length);
sets.forEach(function (set) {
args.push(self.getKey(set));
});
} else {
args.push(1, this.getKey(sets));
}
if (weights) {
args.push('WEIGHTS');
if (Array.isArray(weights)) {
weights.forEach(function (weight) {
args.push(weight);
});
} else {
args.push(weights);
}
}
if (aggregate) {
args.push('AGGREGATE', aggregate);
}
args.push(callback);
this.client.zunionstore.apply(this.client, args);
return this;
}
|
| lib/advanced_structures/CappedList.js |
Module dependencies.
|
var List = require('../base_structures/List').List;
|
A Redis list with a fixed length. Each command that adds a value to the
list is followed by an LTRIM command.
Usage
redback.createCappedList(key [, max_length]);
Reference
http://redis.io/topics/data-types#lists
http://redis.io/commands/ltrim
Redis Structure
(namespace:)key = list(values)
|
var CappedList = exports.CappedList = List.prototype.extend();
|
Insert an element before the specified pivot.
|
CappedList.prototype.insertBefore = function (pivot, value, callback) {
callback = callback || function () {};
var multi = this.client.multi()
multi.linsert(this.key, 'BEFORE', pivot, value);
multi.ltrim(this.key, -1 * this.len, -1);
multi.exec(callback);
return this;
}
|
Insert an element after the specified pivot.
|
CappedList.prototype.insertAfter = function (pivot, value, callback) {
callback = callback || function () {};
var multi = this.client.multi()
multi.linsert(this.key, 'AFTER', pivot, value);
multi.ltrim(this.key, -1 * this.len, -1);
multi.exec(callback);
return this;
}
|
Add one or more elements to the start of the list.
|
CappedList.prototype.unshift = CappedList.prototype.lpush = function (values, callback) {
callback = callback || function () {};
var multi = this.client.multi();
if (Array.isArray(values)) {
var key = this.key;
values.reverse().forEach(function (value) {
multi.lpush(key, value);
});
} else {
multi.lpush(this.key, values);
}
multi.ltrim(this.key, -1 * this.len, -1);
multi.exec(callback);
return this;
}
|
Add one or more elements to the end of the list.
|
CappedList.prototype.push = CappedList.prototype.add = function (values, callback) {
callback = callback || function () {};
var multi = this.client.multi();
if (Array.isArray(values)) {
var key = this.key;
values.forEach(function (value) {
multi.rpush(key, value);
});
} else {
multi.rpush(this.key, values);
}
multi.ltrim(this.key, -1 * this.len, -1);
multi.exec(callback);
return this;
}
|
| lib/advanced_structures/DensitySet.js |
Module dependencies.
|
var SortedSet = require('../base_structures/SortedSet').SortedSet;
|
The DensitySet is similar to a SortedSet but the ability to explicitly
set an element's score has been removed. Instead, adding/removing
an element will increment/decrement its score, e.g.
DensitySet.add(['foo','foo','foo'], ..) //'foo' has a score of 3
Usage
redback.createDensitySet(key);
Reference
http://redis.io/topics/data-types#sorted-sets
Redis Structure
(namespace:)key = zset(count => element)
|
var DensitySet = exports.DensitySet = SortedSet.prototype.extend();
|
Add one or more elements to the set.
|
DensitySet.prototype.add = function (element, callback) {
callback = callback || function () {};
if (Array.isArray(element)) {
return this.addAll(element, callback);
}
this.client.zincrby(this.key, 1, element, callback);
return this;
}
|
Remove one or more elements from the set.
|
DensitySet.prototype.remove = function (element, callback) {
callback = callback || function () {};
if (Array.isArray(element)) {
return this.removeAll(element, callback);
}
var self = this;
this.client.zincrby(this.key, -1, element, function (err, removed) {
if (err) return callback(err, null);
self.client.zremrangebyscore(self.key, '-inf', 0, callback);
});
return this;
}
|
| lib/advanced_structures/KeyPair.js |
Module dependencies.
|
var Structure = require('../Structure');
|
The KeyPair is a structure where unique values are assigned an
ID (like a table with a primary auto-incrementing key and
a single unique column). Internally, the KeyPair uses two Redis
hashes to provide O(1) lookup by both ID and value.
Usage
redback.createKeyPair(key);
Reference
http://redis.io/topics/data-types#hashes
Redis Structure
(namespace:)key = hash(id => value)
(namespace:)key:ids = hash(value => id)
|
var KeyPair = exports.KeyPair = Structure.new();
|
Add a unique value to the KeyPair and return its id. If the value already
exists, the existing id is returned.
|
KeyPair.prototype.add = function (value, callback) {
if (Array.isArray(value)) {
return this.addAll(value, callback);
}
var self = this, hashed_value = this.hashValue(value);
this.client.hget(this.idkey, value, function (err, id) {
if (err) return callback(err, null);
if (null !== id) {
callback(null, id);
} else {
self.autoincrement(function (err, id) {
if (err) return callback(err, null);
var multi = self.client.multi();
multi.hsetnx(self.idkey, hashed_value, id);
multi.hsetnx(self.key, id, value);
multi.exec(function(err, response) {
if (err) return callback(err, null);
self.client.hget(self.idkey, hashed_value, function (err, real_id) {
if (err) return callback(err, null);
if (real_id == id) {
return callback(null, real_id);
} else {
self.client.hdel(self.key, id, function (err) {
if (err) {
callback(err, null);
} else {
callback(null, real_id);
}
});
}
});
});
});
}
});
return this;
}
|
Add multiple unique values to the KeyPair and return and
object containing {value: id, ...}.
param: Array values param: Function callback return: this api: public
|
KeyPair.prototype.addAll = function (values, callback) {
var self = this,
remaining = values.length,
ids = {},
failed = false;
values.forEach(function (value) {
self.add(value, function (err, id) {
if (failed) {
return;
} else if (err) {
failed = true;
return callback(err, null);
} else {
ids[value] = id;
if (!--remaining) callback(null, ids);
}
});
});
}
|
Lookup a unique value and get the associated id.
param: string value param: Function callback return: this api: public
|
KeyPair.prototype.get = function (value, callback) {
if (typeof value === 'function') {
callback = value;
this.client.hgetall(this.key, callback);
} else if (Array.isArray(value)) {
for (var i = 0, l = value.length; i < l; i++) {
value[i] = this.hashValue(value[i]);
}
this.client.hmget(this.idkey, value, callback)
} else {
this.client.hget(this.idkey, this.hashValue(value), callback);
}
return this;
}
|
Get the value associated with the id.
param: int id param: Function callback return: this api: public
|
KeyPair.prototype.getById = function (id, callback) {
this.client.hget(this.key, id, callback);
return this;
}
|
Get an array of ids.
param: Function callback return: this api: public
|
KeyPair.prototype.ids = function (callback) {
this.client.hkeys(this.key, callback);
return this;
}
|
Get an array of values.
param: string value param: Function callback return: this api: public
|
KeyPair.prototype.values = function (callback) {
this.client.hvals(this.key, callback);
return this;
}
|
Check whether a unique value already exists and has an associated id.
param: string value param: Function callback return: this api: public
|
KeyPair.prototype.exists = function (value, callback) {
this.client.hexists(this.idkey, this.hashValue(value), callback);
return this;
}
|
Checks whether an id exists.
param: string value param: Function callback return: this api: public
|
KeyPair.prototype.idExists = function (id, callback) {
this.client.hexists(this.key, id, callback);
return this;
}
|
Deletes a unique value and its associated id.
|
KeyPair.prototype.delete = function (value, callback) {
callback = callback || function () {};
var self = this, value = this.hashValue(value);
this.client.hget(this.idkey, value, function (err, id) {
if (err || value == null) return callback(err);
self._delete(id, value, callback);
});
return this;
}
|
Deletes an id and its associated unique value.
|
KeyPair.prototype.deleteById = function (id, callback) {
callback = callback || function () {};
var self = this;
this.client.hget(this.key, id, function (err, value) {
if (err || value == null) return callback(err);
self._delete(id, self.hashValue(value), callback);
});
return this;
}
|
Get the number of unique values.
param: Function callback return: this api: public
|
KeyPair.prototype.length = function (callback) {
this.client.hlen(this.key, callback);
return this;
}
|
Override this method if you need to hash the unique value
in the second internal hash (i.e. if values are large).
|
KeyPair.prototype.hashValue = function (value) {
return value;
}
|
| lib/advanced_structures/SocialGraph.js |
Module dependencies.
|
var Structure = require('../Structure');
|
Build a social graph similar to Twitter's.
Usage
redback.createSocialGraph(id [, prefix]);
Reference
http://redis.io/topics/data-types#sets
Redis Structure
(namespace:)(prefix:)id:following = set(ids)
(namespace:)(prefix:)id:followers = set(ids)
|
var SocialGraph = exports.SocialGraph = Structure.new();
|
Follow one or more users.
|
SocialGraph.prototype.follow = function (users, callback) {
var self = this,
users = this.getKeys(arguments, 'id'),
multi = this.client.multi();
callback = users.pop();
users.forEach(function (user) {
multi.sadd(self.key_prefix + user + ':followers', self.id);
multi.sadd(self.following, user);
});
multi.exec(callback);
return this;
}
|
Unfollow one or more users.
|
SocialGraph.prototype.unfollow = function (users, callback) {
var self = this,
users = this.getKeys(arguments, 'id'),
multi = this.client.multi();
callback = users.pop();
users.forEach(function (user) {
multi.srem(self.key_prefix + user + ':followers', self.id);
multi.srem(self.following, user);
});
multi.exec(callback);
return this;
}
|
Gets the users whom the current users follows as an array.
param: Function callback return: this api: public
|
SocialGraph.prototype.getFollowing = function (callback) {
this.client.smembers(this.following, callback);
return this;
}
|
Gets an array of users who follow the current user.
param: Function callback return: this api: public
|
SocialGraph.prototype.getFollowers = function (callback) {
this.client.smembers(this.followers, callback);
return this;
}
|
Count how many users the current user follows.
param: Function callback return: this api: public
|
SocialGraph.prototype.countFollowing = function (callback) {
this.client.scard(this.following, callback);
return this;
}
|
Count how many users follow the current user.
param: Function callback return: this api: public
|
SocialGraph.prototype.countFollowers = function (callback) {
this.client.scard(this.followers, callback);
return this;
}
|
Checks whether the current user follows the specified user.
|
SocialGraph.prototype.isFollowing = function (user, callback) {
user = this.getKey(user, 'id');
this.client.sismember(this.following, user, callback);
return this;
}
|
Checks whether the specified user follows the current user.
|
SocialGraph.prototype.hasFollower = function (user, callback) {
user = this.getKey(user, 'id');
this.client.sismember(this.followers, user, callback);
return this;
}
|
Gets an array of common followers for one or more users.
|
SocialGraph.prototype.getCommonFollowers = function (users, callback) {
var users = this.getSocialKeys(arguments, 'followers');
users.unshift(this.followers);
this.client.sinter.apply(this.client, users);
return this;
}
|
Gets an array of users who are followed by all of the specified user(s).
|
SocialGraph.prototype.getCommonFollowing = function (users, callback) {
var users = this.getSocialKeys(arguments, 'following');
users.unshift(this.following);
this.client.sinter.apply(this.client, users);
return this;
}
|
Gets an array of users who follow the current user but do not follow any
of the other specified users.
|
SocialGraph.prototype.getDifferentFollowers = function (users, callback) {
var users = this.getSocialKeys(arguments, 'followers');
users.unshift(this.followers);
this.client.sdiff.apply(this.client, users);
return this;
}
|
Gets an array of users who are followed by the current user but not any of
the other specified users.
|
SocialGraph.prototype.getDifferentFollowing = function (users, callback) {
var users = this.getSocialKeys(arguments, 'following');
users.unshift(this.following);
this.client.sdiff.apply(this.client, users);
return this;
}
|
| lib/Channel.js |
Module dependencies.
|
var EventEmitter = require('events').EventEmitter;
|
Wrap the Redis pub/sub commands.
Usage
redback.createChannel(name);
Reference
http://redis.io/topics/pubsub
|
var Channel = exports.Channel = function (client, channel_name) {
this.name = channel_name;
this.setClient(client);
}
|
Channel is an event emitter.
|
Channel.prototype = new EventEmitter();
|
Bind a new Redis client (e.g. if not exclusively using pub/sub mode).
param: Object client return: this api: public
|
Channel.prototype.setClient = function (client) {
this.client = client;
var self = this;
['message','subscribe','unsubscribe'].forEach(function (event) {
self.client.on(event, function (channel, arg) {
if (channel == self.name) {
self.emit(event, arg);
}
});
});
return this;
}
|
Publish a message to the channel.
param: string msg param: Function callback return: this api: public
|
Channel.prototype.publish = function (msg, callback) {
this.client.publish(this.name, msg, callback);
return this;
}
|
Subscribe to the channel.
param: Function callback return: this api: public
|
Channel.prototype.subscribe = function (callback) {
this.client.subscribe(this.name);
if (typeof callback === 'function') {
this.on('subscribe', callback);
}
return this;
}
|
Unsubscribe from the channel.
param: Function callback return: this api: public
|
Channel.prototype.unsubscribe = function (callback) {
this.client.unsubscribe(this.name);
if (typeof callback === 'function') {
this.on('unsubscribe', callback);
}
return this;
}
|
| lib/Cache.js |
Use Redis as a cache backend.
Usage
redback.createCache(namespace);
Reference
http://redis.io/commands#string
Redis Structure
(namespace:)cache_namespace:key = string
|
var Cache = exports.Cache = function (client, namespace) {
this.client = client;
this.namespace = namespace;
}
|
Cache one or more values.
To cache a single value by key:
cache.set('foo', 'bar', callback);
To set multiple cached values by key:
cache.set({foo:'bar', key2:'value2'}, callback);
|
Cache.prototype.set = function (key, value, expiry, callback) {
callback = callback || function () {};
if (typeof expiry === 'function') {
callback = expiry;
this.client.set(this.getKey(key), value, callback);
} else if (typeof value === 'function') {
callback = value;
var i, set = [];
for (i in key) {
set.push(this.getKey(i));
set.push(key[i]);
}
set.push(callback);
this.client.mset.apply(this.client, set);
} else {
this.client.setex(this.getKey(key), expiry, value, callback);
}
return this;
}
|
Add one or more values to the cache, but only if the cache
key(s) do not already exist.
param: string | Object key param: string value (optional) param: Function callback (optional) return: this api: public
|
Cache.prototype.add = function (key, value, callback) {
callback = callback || function () {};
if (typeof value === 'function') {
callback = value;
var i, set = [];
for (i in key) {
set.push(this.getKey(i));
set.push(key[i]);
}
set.push(callback);
this.client.msetnx.apply(this.client, set);
} else {
this.client.setnx(this.getKey(key), value, callback);
}
return this;
}
|
Get one or more values from the cache.
To get a single cached value by key:
cache.get('foo', callback);
To get multiple cached values by key:
cache.get(['foo','bar'], callback);
To get all cached values:
cache.get(callback);
param: string key param: string value param: Function callback return: this api: public
|
Cache.prototype.get = function (key, callback) {
var namespace_len = this.namespace.length + 1,
self = this;
var multi_get = function (keys) {
var get_args = [];
keys.forEach(function (key) {
get_args.push(key);
})
get_args.push(function (err, values) {
if (err) return callback(err, null);
var i, l, ret = {};
for (i = 0, l = keys.length; i < l; i++) {
ret[keys[i].substr(namespace_len)] = values[i];
}
callback(null, ret);
});
self.client.mget.apply(self.client, get_args);
}
if (typeof key === 'function') {
callback = key;
this.keys('*', true, function (err, keys) {
if (err) callback(err, null);
multi_get(keys);
});
} else if (Array.isArray(key)) {
if (!key.length) callback(null, null);
for (var get = [], i = 0, l = key.length; i < l; i++) {
key[i] = this.getKey(key[i]);
}
multi_get(key);
} else {
this.client.get(this.getKey(key), callback);
}
return this;
}
|
Set a cache key and return the current value.
param: string key param: string value param: Function callback return: this api: public
|
Cache.prototype.getSet = function (key, value, callback) {
this.client.getset(this.getKey(key), value, callback);
return this;
}
|
Check whether a cache key exists.
param: string key param: Function callback return: this api: public
|
Cache.prototype.exists = function (key, callback) {
this.client.exists(this.getKey(key), callback);
return this;
}
|
Increment the specified cache value.
|
Cache.prototype.increment =
Cache.prototype.incrBy = function (key, amount, callback) {
callback = callback || function () {};
if (typeof amount === 'function') {
callback = amount;
amount = 1;
}
this.client.incrby(this.getKey(key), amount, callback);
return this;
}
|
Decrement the specified cache value.
|
Cache.prototype.decrement =
Cache.prototype.decrBy = function (key, amount, callback) {
callback = callback || function () {};
if (typeof amount === 'function') {
callback = amount;
amount = 1;
}
this.client.decrby(this.getKey(key), amount, callback);
return this;
}
|
Get all cache keys matching the pattern.
|
Cache.prototype.keys = function (pattern, keep_namespace, callback) {
if (typeof pattern === 'function') {
keep_namespace = false;
callback = pattern;
pattern = '*';
} else if (typeof keep_namespace === 'function') {
callback = keep_namespace;
keep_namespace = false;
}
var self = this;
if (keep_namespace) {
this.client.keys(this.namespace + ':' + pattern, function (err, keys) {
if (err) return callback(err, null);
if (!keys) return callback(null, []);
callback(null, keys);
});
} else {
var namespace_len = this.namespace.length + 1;
this.client.keys(this.namespace + ':' + pattern, function (err, keys) {
if (err) return callback(err, null);
if (!keys) return callback(null, []);
if (null == keys) return callback(null, []);
for (var i = 0, l = keys.length; i < l; i++) {
keys[i] = keys[i].substr(namespace_len);
}
callback(null, keys);
});
}
}
|
Flush all cache keys matching the pattern.
|
Cache.prototype.flush = function (pattern, callback) {
callback = callback || function () {};
if (typeof pattern === 'function') {
callback = pattern;
pattern = '*';
}
var self = this;
this.keys(pattern, true, function (err, keys) {
if (err) return callback(err, null);
if (!keys) return callback(err, []);
var error = false, remaining = keys.length, del_count = 0;
keys.forEach(function (key) {
self.client.del(key, function (err, deleted) {
if (error) {
return;
} else if (err) {
error = true;
return callback(err, null);
}
del_count++;
if (!--remaining) callback(err, del_count);
});
});
});
}
|
Expire the cache key after a certain number of seconds.
|
Cache.prototype.expire = function (key, ttl, callback) {
callback = callback || function () {};
this.client.expire(this.getKey(key), ttl, callback);
return this;
}
|
Expire the cache key at a certain date.
|
Cache.prototype.expireAt = function (key, when, callback) {
callback = callback || function () {};
if (typeof when.getTime === 'function') {
when = Math.round(when.getTime() / 1000); //ms => s
}
this.client.expireat(this.getKey(key), when, callback);
return this;
}
|
Get the number of seconds before the cache key expires.
|
Cache.prototype.ttl = function (key, callback) {
callback = callback || function () {};
this.client.ttl(this.getKey(key), callback);
return this;
}
|
Remove the cache key's associated expiry.
|
Cache.prototype.persist = function (key, callback) {
callback = callback || function () {};
this.client.persist(this.getKey(key), callback);
return this;
}
|