RedbackA high-level Redis library for Node.JS | |
| 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;
}
|
Checks whether the structure has an expiry.
param: Function callback return: this api: public
|
Structure.prototype.isVolatile = function (callback) {
this.client.ttl(this.key, function (err, ttl) {
if (err) return callback(err, null);
callback(null, ttl != -1);
});
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;
}
|
Create a random key for temporary use.
|
Structure.prototype.randomKey = function () {
return Math.random();
}
|
Get the type of the current structure.
param: Function callback return: this api: public
|
Structure.prototype.type = function (callback) {
this.client.type(this.key, callback);
return this;
}
|
Rename the structure (change the Redis key).
param: string new_key param: Function callback return: this api: public
|
Structure.prototype.rename = function (new_key, callback) {
var self = this;
new_key = this.namespaceKey(new_key);
this.client.rename(this.key, new_key, function (err) {
if (err) return callback(err, null);
self.key = new_key;
callback();
});
return this;
}
|
Sort all elements in the structure.
Options
limit, offset, by, get, alpha, desc, store
Reference
http://redis.io/commands/sort
param: object options param: Function callback return: this api: public
|
Structure.prototype.sort = function (options, callback) {
var args = [this.key];
if (typeof options.by !== 'undefined') {
args.push('BY', options.by);
}
if (typeof options.limit !== 'undefined') {
args.push('LIMIT');
if (typeof options.offset !== 'undefined') {
args.push(options.offset);
}
args.push(options.limit);
}
if (typeof options.get !== 'undefined') {
if (Array.isArray(options.get)) {
options.get.forEach(function (pattern) {
args.push('GET', pattern);
});
} else {
args.push('GET', options.get);
}
}
if (typeof options.desc !== 'undefined' && options.desc) {
args.push('DESC');
}
if (typeof options.alpha !== 'undefined' && options.alpha) {
args.push('ALPHA');
}
if (typeof options.store !== 'undefined') {
args.push('STORE', options.store);
}
this.client.sort.apply(this.client, args);
return this;
}
|
| 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',
'FullText', 'Queue', 'RateLimit', 'BloomFilter'];
|
The Redback object wraps the Redis client and acts as a factory
for structures.
or
param: string host (optional) param: int port (optional) param: Object options (optional) api: public
|
var Redback = exports.Redback = function () {
this.client = arguments[0] instanceof redis.RedisClient ?
arguments[0] : redis.createClient.apply(this, arguments);
var options = arguments[1] instanceof Object ?
arguments[1] : arguments[2];
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;
}
|
Close the connection to Redis.
|
Redback.prototype.quit = function () {
this.client.quit();
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);
}
|
Wrap a Redis client with Redback.
|
exports.use = function (client, options) {
return new Redback(client, 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/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 last element in the list. The first param can be used
to block the process and wait until list elements are available. If the list
is empty in both examples below, the first example will return null , while
the second will wait for up to 3 seconds. If a list element becomes available
during the 3 seconds it will be returned, otherwise null will be returned.
Example
list.shift(callback);
Blocking Example
list.shift(3, callback)
|
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. The first param can be used
to block the process and wait until list elements are available. If the list
is empty in both examples below, the first example will return null , while
the second will wait for up to 3 seconds. If a list element becomes available
during the 3 seconds it will be returned, otherwise null will be returned.
Example
list.pop(callback);
Blocking Example
list.pop(3, callback)
|
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/BloomFilter.js |
Module dependencies.
|
var Structure = require('../Structure'),
crc32 = require('../Utils').crc32;
|
A Simple BloomFilter. Bloomfilter is a probabilistic data structure used to
determine if an element is present in a set. There may be false positives,
but there cannot be false negatives.
Usage
redback.createBloomFilter(key [, size, hashes]);
Options
size - Size of the bloom filter , default is 100 bits.
hashes - Number of hashes to perform. default is 2.
Reference
http://redis.io/commands#string
http://en.wikipedia.org/wiki/Bloom_filter
http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
Redis Structure
(namespace:)key = string(bits)
|
var BloomFilter = exports.BloomFilter = Structure.new();
|
Adds an element to the bloom filter.
|
BloomFilter.prototype.add = function(item, callback) {
var multi = this.client.multi(), crc;
for (var hash_index = 0; hash_index < this.num_hashes; hash_index++) {
crc = (crc32(item, hash_index) % this.size).toString(2);
for (var i =0; i <crc.length; i++) {
if (crc[i] === '1') {
multi.setbit(this.key, i, 1);
}
}
}
multi.exec(callback || function () {});
return this;
}
|
Checks if the element exists in the bloom filter.
This can return false positives( i.e An element does not exist but it returns true)
But this can never return false negatives. (i.e an element )
|
BloomFilter.prototype.exists = function(item, callback) {
var multi = this.client.multi(), crc;
callback = callback || function () {};
for (var hash_index = 0; hash_index < this.num_hashes; hash_index++) {
crc = (crc32(item, hash_index) % this.size).toString(2);
for (var i =0; i < crc.length; i++) {
if (crc[i] === '1') {
multi.getbit(this.key, i);
}
}
}
multi.exec(function(err, results) {
callback(err, results.indexOf(0) === -1);
});
return this;
}
|
Resets the Bloom filter.
|
BloomFilter.prototype.reset = function (callback) {
this.client.set(this.key, 0, callback || function () {});
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/FullText.js |
Module dependencies.
|
var Structure = require('../Structure'),
EventEmitter = require('events').EventEmitter,
fs = require('fs');
|
Stop words - words that shouldn't be indexed.
|
var stopwords = {
'a':1,'able':1,'about':1,'across':1,'after':1,'all':1,'almost':1,'also':1,'am':1,'among':1,
'an':1,'and':1,'any':1,'are':1,'as':1,'at':1,'be':1,'because':1,'been':1,'but':1,'by':1,'can':1,
'cannot':1,'could':1,'dear':1,'did':1,'do':1,'does':1,'either':1,'else':1,'ever':1,'every':1,
'for':1,'from':1,'get':1,'got':1,'had':1,'has':1,'have':1,'he':1,'her':1,'hers':1,'him':1,'his':1,
'how':1,'however':1,'i':1,'if':1,'in':1,'into':1,'is':1,'it':1,'its':1,'just':1,'least':1,'let':1,
'like':1,'likely':1,'may':1,'me':1,'might':1,'most':1,'must':1,'my':1,'neither':1,'no':1,
'nor':1,'not':1,'of':1,'off':1,'often':1,'on':1,'only':1,'or':1,'other':1,'our':1,'own':1,
'rather':1,'said':1,'say':1,'says':1,'she':1,'should':1,'since':1,'so':1,'some':1,'than':1,
'that':1,'the':1,'their':1,'them':1,'then':1,'there':1,'these':1,'they':1,'this':1,'tis':1,
'to':1,'too':1,'twas':1,'us':1,'wants':1,'was':1,'we':1,'were':1,'what':1,'when':1,'where':1,
'which':1,'while':1,'who':1,'whom':1,'why':1,'will':1,'with':1,'would':1,'yet':1,'you':1,
'your':1,'':1
}
|
A full text index with support for stop words, stemming and
a basic boolean search syntax.
Usage
redback.createFullText(key);
Reference
http://redis.io/topics/data-types#sets
Redis Structure
(namespace:)key:<word1> = set(docs)
(namespace:)key:<word2> = set(docs)
(namespace:)key:<wordN> = set(docs)
|
var FullText = exports.FullText = Structure.new();
|
Index one or more documents.
Index One Document
text.indexFile(1, 'document string ...', callback);
Index Many Documents
text.indexFile({1:'docstr1', 2:'docstr2'}, callback);
param: int | string id - the document's unique identifier param: string | ReadableStream | Buffer document param: Function callback (optional) return: this api: public
|
FullText.prototype.index = function (id, document, callback) {
if (typeof id === 'object') {
return this.indexAll(id, document);
} else if (document instanceof EventEmitter &&
typeof document.readable !== 'undefined' && document.readable === true) {
return this.indexStream(id, document, callback);
} else if (document instanceof Buffer) {
document = document.toString();
}
this.indexed_bytes += document.length;
var stemmed = this.stemWords(this.extractWords(document));
this.buildIndex(id, stemmed, callback || function () {});
}
|
Index one or more files.
Index One Document
text.indexFile(1, '/path/to/file', callback);
Index Many Documents
text.indexFile({1:'file1', 2:'file2'}, callback);
param: int | string | Object id param: string filename (optional) param: Function callback (optional) return: this api: public
|
FullText.prototype.indexFile =
FullText.prototype.indexFiles = function (id, filename, callback) {
if (typeof id === 'object') {
callback = filename;
var self = this, files = id, ids = Object.keys(files),
failed = false, remaining = ids.length;
ids.forEach(function (id) {
self.indexStream(id, fs.createReadStream(files[id]), function (err) {
if (failed) {
return;
} else if (err) {
return callback(err);
} else {
if (!--remaining) callback();
}
});
});
return this;
} else {
return this.indexStream(id, fs.createReadStream(filename), callback);
}
}
|
Search the full text index. Words will be extracted from the
search string and used to filter search results. To exclude certain
words, prefix them with a hyphen "-".
Basic Search
index.search('foo bar', callback);
Excluding Words
index.search('foo -bar -cool', callback);
param: string search param: Function callback api: public
|
FullText.prototype.search = function (search, callback) {
var include = [], exclude = [];
this.stemWords(this.extractWords(search)).forEach(function (word) {
if (word[0] === '-') {
exclude.push(word.substr(1));
} else {
include.push(word);
}
});
return this._search(include, exclude, callback);
}
|
Clear the index.
|
FullText.prototype.clear = function (callback) {
var self = this;
callback = callback || function () {};
this.client.keys(this.key + ':*', function (err, keys) {
if (err) return callback(err, null);
var rem_count = 0, failed = false, remaining = keys.length;
keys.forEach(function (key) {
self.client.del(key, function (err, removed) {
if (failed) {
return;
} else if (err) {
failed = true;
return callback(err);
} else {
if (removed) rem_count++;
if (!--remaining) callback(null, rem_count);
}
});
});
});
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/Queue.js |
Module dependencies.
|
var Structure = require('../Structure'),
List = require('../base_structures/List').List;
|
A simple FIFO/LIFO queue.
Usage
redback.createQueue(key [, is_fifo]);
Reference
http://redis.io/topics/data-types#lists
http://en.wikipedia.org/wiki/Queue(datastructure)
Redis Structure
(namespace:)key = list(values)
|
var Queue = exports.Queue = Structure.new();
|
Add one or more elements to the queue.
|
Queue.prototype.enqueue = Queue.prototype.add = function (values, callback) {
this.list.unshift(values, callback);
return this;
}
|
Remove the next element from the queue.
|
Queue.prototype.dequeue = Queue.prototype.next = function (wait, callback) {
this.list[this.fifo ? 'pop' : 'shift'](wait, callback);
return this;
}
|
| lib/advanced_structures/RateLimit.js |
Module dependencies.
|
var Structure = require('../Structure');
|
See http://chris6f.com/rate-limiting-with-redis
Count the number of times a subject performs an action over an interval
in the immediate past - this can be used to rate limit the subject if
the count goes over a certain threshold. For example, you could track
how many times an IP (the subject) has viewed a page (the action) over
a certain time frame and limit them accordingly.
Usage
redback.createRateLimit(action [, options]);
Options
bucket_interval - default is 5 seconds
bucket_span - default is 10 minutes
subject_expiry - default is 20 minutes
Reference
http://chris6f.com/rate-limiting-with-redis
http://redis.io/topics/data-types#hash
Redis Structure
(namespace:)action:<subject1> = hash(bucket => count)
(namespace:)action:<subject2> = hash(bucket => count)
(namespace:)action:<subjectN> = hash(bucket => count)
|
var RateLimit = exports.RateLimit = Structure.new();
|
Increment the count for the specified subject.
|
RateLimit.prototype.add = function (subject, callback) {
if (Array.isArray(subject)) {
return this.addAll(subject, callback);
}
var bucket = this.getBucket(), multi = this.client.multi();
subject = this.key + ':' + subject;
multi.hincrby(subject, bucket, 1)
multi.hdel(subject, (bucket + 1) % this.bucket_count)
.hdel(subject, (bucket + 2) % this.bucket_count)
multi.expire(subject, this.subject_expiry);
multi.exec(function (err) {
if (!callback) return;
if (err) return callback(err);
callback(null);
});
return this;
}
|
Count the number of times the subject has performed an action
in the last interval seconds.
param: string subject param: int interval param: Function callback return: this api: public
|
RateLimit.prototype.count = function (subject, interval, callback) {
var bucket = this.getBucket(),
multi = this.client.multi(),
count = Math.floor(interval / this.bucket_interval);
subject = this.key + ':' + subject;
multi.hget(subject, bucket);
while (count--) {
multi.hget(subject, (--bucket + this.bucket_count) % this.bucket_count);
}
multi.exec(function (err, counts) {
if (err) return callback(err, null);
for (var count = i = 0, l = counts.length; i < l; i++) {
if (counts[i]) {
count += parseInt(counts[i], 10);
}
}
callback(null, count);
});
return this;
}
|
An alias for ratelimit.add(subject).count(subject, interval);
param: string subject param: int interval param: Function callback return: this api: public
|
RateLimit.prototype.addCount = function (subject, interval, callback) {
var bucket = this.getBucket(),
multi = this.client.multi(),
count = Math.floor(interval / this.bucket_interval);
subject = this.key + ':' + subject;
multi.hincrby(subject, bucket, 1)
multi.hdel(subject, (bucket + 1) % this.bucket_count)
.hdel(subject, (bucket + 2) % this.bucket_count)
multi.expire(subject, this.subject_expiry);
multi.hget(subject, bucket);
while (count--) {
multi.hget(subject, (--bucket + this.bucket_count) % this.bucket_count);
}
multi.exec(function (err, counts) {
if (err) return callback(err, null);
for (var count = 0, i = 4, l = counts.length; i < l; i++) {
if (counts[i]) {
count += parseInt(counts[i], 10);
}
}
callback(null, count);
});
return this;
}
|
| lib/advanced_structures/SocialGraph.js |
Module dependencies.
|
var Structure = require('../Structure');
|
Build a social graph similar to Twitter's. User ID can be a string or
integer, as long as they're unique.
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();
if (typeof users[users.length-1] === 'function') {
callback = users.pop();
} else {
callback = function () {};
}
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();
if (typeof users[users.length-1] === 'function') {
callback = users.pop();
} else {
callback = function () {};
}
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/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;
}
|
Checks whether a cache key has an expiry.
param: string key param: Function callback return: this api: public
|
Cache.prototype.isVolatile = function (key, callback) {
this.client.ttl(this.getKey(key), function (err, ttl) {
if (err) return callback(err, null);
callback(null, ttl != -1);
});
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;
}
|
| 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;
}
|