Fork me on GitHub Redback

Redback

A high-level Redis library

BloomFilter

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.

  • param: string item - Item to store into bloom filter

  • param: Function callback (optional)

  • return: this

  • api: public

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+1);
        multi.setbit(this.key, crc, 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 )

  • param: string item - Item to check for existence in bloom filter

  • param: Function callback (optional)

  • return: this

  • api: public

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+1);
        multi.getbit(this.key, crc);
    }

    multi.exec(function(err, results) {
        callback(err, results.indexOf(0) === -1);
    });

    return this;
}

Resets the Bloom filter.

  • param: Function callback (optional)

  • return: this

BloomFilter.prototype.reset = function (callback) {
    this.client.set(this.key, 0, callback || function () {});
    return this;
}

CappedList

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.

  • param: int pivot

  • param: string value

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: int pivot

  • param: string value

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string | array value(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string | array value(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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;
}

DensitySet

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.

  • param: string | Array element(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string | Array element(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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;
}

KeyPair

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.

  • param: string | Array value(s)

  • param: Function callback

  • return: this

  • api: public

KeyPair.prototype.add = function (value, callback) {
    //Pass on an array of values to addAll()
    if (Array.isArray(value)) {
        return this.addAll(value, callback);
    }

    var self = this, hashed_value = this.hashValue(value);
    //Check if the value already has an id
    this.client.hget(this.idkey, value, function (err, id) {
        if (err) return callback(err, null);
        if (null !== id) {
            callback(null, id);
        } else {
            //If not, create a new id
            self.autoincrement(function (err, id) {
                if (err) return callback(err, null);

                //Set the id and value simultaneously
                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);

                    //Another client may have add at exactly the same time, so do
                    //another get to get the actual stored id
                    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 {
                            //Another client did beat us! remove the bad key
                            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 | Array id(s)

  • param: Function callback

  • return: this

  • api: public

KeyPair.prototype.getById = function (id, callback) {
	if (Array.isArray(id))
    	this.client.hmget(this.key, id, callback);
	else
    	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.

  • param: string value

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: int id

  • param: Function callback (optional)

  • return: this

  • api: public

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).

  • param: string value

  • return: string hashed_value

  • api: public

KeyPair.prototype.hashValue = function (value) {
    return value;
}

Lock

lib/advanced_structures/Lock.js

Module dependencies.

var crypto = require('crypto');
var Structure = require('../Structure');

A distributed lock.

var Lock = exports.Lock = Structure.new();

Acquire a temporary lock on some key.

  • param: string key The unique key of the lock

  • param: number ttl The amount of time (in seconds) before the lock expires

  • param: Function callback Invoked when the process completes

  • param: Error callback.err An error that occurred, if any

  • param: string callback.token The token that was acquired if successful. If the lock was

    not acquired then this will be undefined

  • api: public

Lock.prototype.acquire = function(key, ttl, callback) {
    var client = this.client;

    _createToken(function(err, token) {
        if (err) {
            return callback(err);
        }

        client.setnx(key, token, function(err, wasSet) {
            if (err) {
                return callback(err);
            } else if (!wasSet) {
                // We did not successfully acquire the lock. Since a process can crash after it sets
                // the lock but before it sets the expiry, we need to avoid deadlocks by ensuring
                // the lock has a TTL associated to it
                _ensureTtl(client, key, ttl);
                return callback();
            }

            // Apply the expiry to the lock
            client.expire(key, ttl, function(err) {
                if (err) {
                    return callback(err);
                }

                // Return the token, which is used to release the lock
                return callback(null, token);
            });
        });
    });
};

Release a lock that was acquired with the provided key and token.

  • param: string key The key for the lock to release

  • param: string token The token that was generated for the lock acquisition

  • param: Function callback Invoked when the function completes

  • param: Error callback.err An error that occurred, if any

  • param: boolean callback.hadLock Determines whether or not we owned the lock at the time

    that we released it

  • api: public

Lock.prototype.release = function(key, token, callback) {
    var client = this.client;

    client.get(key, function(err, lockedToken) {
        if (err) {
            return callback(err);
        } else if (lockedToken !== token) {
            // The current token is not the one we acquired. It's possible we held the lock longer
            // than its expiry
            return callback(null, false);
        }

        // We have the token, simply delete the lock key
        client.del(key, function(err) {
            if (err) {
                return callback(err);
            }

            return callback(null, true);
        });
    });
};

Queue

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.

  • param: string | Array value(s)

  • param: Function callback (optional)

  • api: public

Queue.prototype.enqueue = Queue.prototype.add = function (values, callback) {
    this.list.unshift(values, callback);
    return this;
}

Remove the next element from the queue.

  • param: int wait (optional) - block for this many seconds

  • param: Function callback

  • api: public

Queue.prototype.dequeue = Queue.prototype.next = function (wait, callback) {
    this.list[this.fifo ? 'pop' : 'shift'](wait, callback);
    return this;
}

RateLimit

lib/advanced_structures/RateLimit.js

Module dependencies.

var Structure = require('../Structure');

See https://gist.github.com/chriso/54dd46b03155fcf555adccea822193da

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

https://gist.github.com/chriso/54dd46b03155fcf555adccea822193da 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.

  • param: string subject

  • param: Function callback (optional)

  • return: this

  • api: public

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;

    //Increment the current bucket
    multi.hincrby(subject, bucket, 1)

    //Clear the buckets ahead
    multi.hdel(subject, (bucket + 1) % this.bucket_count)
         .hdel(subject, (bucket + 2) % this.bucket_count)

    //Renew the key TTL
    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;

    //Get the counts from the previous `count` buckets
    multi.hget(subject, bucket);
    while (count--) {
        multi.hget(subject, (--bucket + this.bucket_count) % this.bucket_count);
    }

    //Add up the counts from each bucket
    multi.exec(function (err, counts) {
        if (err) return callback(err, null);
        for (var count = 0, i = 0, l = counts.length; i &lt; 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;

    //Increment the current bucket
    multi.hincrby(subject, bucket, 1)

    //Clear the buckets ahead
    multi.hdel(subject, (bucket + 1) % this.bucket_count)
         .hdel(subject, (bucket + 2) % this.bucket_count)

    //Renew the key TTL
    multi.expire(subject, this.subject_expiry);

    //Get the counts from the previous `count` buckets
    multi.hget(subject, bucket);
    while (count--) {
        multi.hget(subject, (--bucket + this.bucket_count) % this.bucket_count);
    }

    //Add up the counts from each bucket
    multi.exec(function (err, counts) {
        if (err) return callback(err, null);
        for (var count = 0, i = 4, l = counts.length; i &lt; l; i++) {
            if (counts[i]) {
                count += parseInt(counts[i], 10);
            }
        }
        callback(null, count);
    });

    return this;
}

SocialGraph

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.

  • param: int | SocialGraph | Array user(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: int | SocialGraph | Array user(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string | SocialGraph user

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string | SocialGraph user

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string | SocialGraph | Array user(s)

  • param: Function callback

  • return: this

  • api: public

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).

  • param: string | SocialGraph | Array user(s)

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string | SocialGraph | Array user(s)

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string | SocialGraph | Array user(s)

  • param: Function callback

  • return: this

  • api: public

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;
}

Bitfield

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

  • param: int bit

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: int bit

  • param: bool value

  • param: Function callback (optional)

  • return: this

  • api: public

Bitfield.prototype.set = function (bit, value, callback) {
    callback = callback || function () {};
    this.client.setbit(this.key, bit, value ? 1 : 0, callback);
    return this;
}

Hash

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.

  • param: string hash_key

  • param: Function callback (optional)

  • return: this

  • api: public

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);

  • param: string hash_key (optional)

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string hash_key

  • param: int amount (optional - default is 1)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string hash_key

  • param: int amount (optional - default is 1)

  • param: Function callback (optional)

  • return: this

  • api: public

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;
}

List

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.

  • param: int start

  • param: count end (optional - defaults to the last element)

  • param: Function callback

  • return: this

  • api: public

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.

  • param: int index

  • param: count count (optional - default is 1)

  • param: Function callback

  • return: this

  • api: public

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.

  • param: int length

  • param: bool keep_earliest (optional - default is false)

  • param: Function callback (optional)

  • return: this

  • api: public

List.prototype.cap = function (length, keep_earliest, callback) {
    callback = callback || function () {};
    var start = 0, end = -1;
    if (typeof keep_earliest === 'function') {
        //Keep the last `length` elements
        start = -1 * length;
        callback = keep_earliest;
    } else {
        //Keep the first `length` elements
        end = length - 1;
    }
    this.client.ltrim(this.key, start, end, callback);
    return this;
}

Remove one or more list elements matching the value.

  • param: string value

  • param: bool count (optional - default is 1)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: int start

  • param: int end

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: int pivot

  • param: string value

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: int pivot

  • param: string value

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: int index

  • param: string value

  • param: Function callback (optional)

  • return: this

  • api: public

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)

  • param: int wait (optional) - seconds to block

  • param: Function callback

  • return: this

  • api: public

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)

  • param: int wait (optional) - seconds to block

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string | array value(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string | array value(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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;
}

Set

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.

  • param: string | Array element(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string | Array element(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string | Set dest

  • param: string element

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: bool remove (optional - default is false)

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string | Set | Array set(s)

  • param: Function callback

  • return: this

  • api: public

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).

  • param: string | Set dest

  • param: string | Set | Array set(s)

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string | Set | Array set(s)

  • param: Function callback

  • return: this

  • api: public

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).

  • param: string | Set dest

  • param: string | Set | Array set(s)

  • param: Function callback

  • return: this

  • api: public

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.

  • param: string | Set | Array set(s)

  • param: Function callback

  • return: this

  • api: public

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).

  • param: string | Set dest

  • param: string | Set | Array set(s)

  • param: Function callback

  • return: this

  • api: public

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;
}

SortedSet

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.

  • param: string | Array element(s)

  • param: Function callback (optional)

  • return: this

  • api: public

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.

  • param: string element

  • param: int amount (optional - default is 1)

  • param: Function callback (optional)

  • return: this ;

  • api: public

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.

  • param: string element

  • param: int amount (optional - default is 1)

  • param: Function callback (optional)

  • return: this ;

  • api: public

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.

  • param: bool without_scores (optional - scores are included by default)

  • param: Function callback

  • return: this

  • api: public

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).

  • param: int start

  • param: int end

  • param: Function callback (optional)

  • return: this

  • api: public

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).

  • param: int start

  • param: int end

  • param: Function callback (optional)

  • return: this

  • api: public

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));

    //weights/aggregate are optional
    if (typeof weights === 'function') {
        callback = weights;
        weights = aggregate = false;
    } else if (typeof aggregate === 'function') {
        callback = aggregate;
        aggregate = false;
    }

    //ZINTERSTORE destination numkeys key [key ...]
    //    [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
    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));

    //weights/aggregate are optional
    if (typeof weights === 'function') {
        callback = weights;
        weights = aggregate = false;
    } else if (typeof aggregate === 'function') {
        callback = aggregate;
        aggregate = false;
    }

    //ZUNIONSTORE destination numkeys key [key ...]
    //    [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
    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;
}