UNPKG

taggable-via-redis

Version:

Add tagging functionality backed by Redis. This is a rewrite of sintaxi's node-redis-tag.

224 lines (187 loc) 6.05 kB
var redis = require("redis") /** * constructor * @param {String} taggable * @param {uint} [redisPort] specify custom redis port * @param {String} [redisHost] specify custom redis host */ var Taggable = function(taggable, redisPort, redisHost){ this.redisClient = redis.createClient(redisPort, redisHost) this.taggable = taggable } Taggable.prototype.scopedSet = function(scope, id, tags, cb){ var that = this var newList = tags // get current tags that.redisClient.smembers(scope + ":" + that.taggable + ":" + id + ":tags", function(err, reply){ // keep record of old list var oldList = reply ? reply : [] // make array of tags that need to be removed var added = newList.filter(function(i){ return oldList.indexOf(i) == -1 }) // make array of tags that need to be added var removed = oldList.filter(function(i){ return newList.indexOf(i) == -1 }) // set counters var toAddCount = added.length var toRemoveCount = removed.length // nothing has been changed if(toAddCount === 0 && toRemoveCount === 0){ cb(); return } // add new tags added.forEach(function(tag){ that.redisClient.multi() .sadd(scope + ":" + that.taggable + ":" + id + ":tags", tag) .sadd(scope + ":" + that.taggable + ":tags:" + tag, id) .zincrby(scope + ":" + that.taggable + ":tags", 1, tag) .sadd(that.taggable + ":" + id + ":tags", tag) .sadd(that.taggable + ":tags:" + tag, id) .zincrby(that.taggable + ":tags", 1, tag) .exec(function (err, replies) { toAddCount -- if(toAddCount == 0 && toRemoveCount == 0) cb(true) }) }) // remove the rest removed.forEach(function(tag){ that.redisClient.multi() .srem(scope + ":" + that.taggable + ":" + id + ":tags", tag) .srem(scope + ":" + that.taggable + ":tags:" + tag, id) .zincrby(scope + ":" + that.taggable + ":tags", -1, tag) .srem(that.taggable + ":" + id + ":tags", tag) .srem(that.taggable + ":tags:" + tag, id) .zincrby(that.taggable + ":tags", -1, tag) .exec(function (err, replies) { if(replies[2] == "0") that.redisClient.zrem(scope + ":" + that.taggable + ":tags", tag) // remove tag from system if count is zero if(replies[5] == "0") that.redisClient.zrem(that.taggable + ":tags", tag) toRemoveCount -- if(toAddCount == 0 && toRemoveCount == 0) cb(true) }) }) }) } Taggable.prototype.unscopedSet = function(id, tags, cb){ var that = this var newList = tags // get current tags that.redisClient.smembers(that.taggable + ":" + id + ":tags", function(err, reply){ // keep record of old list var oldList = reply ? reply : [] // make array of tags that need to be added var removed = oldList.filter(function(i){ return newList.indexOf(i) == -1 }) // make array of tags that need to be removed var added = newList.filter(function(i){ return oldList.indexOf(i) == -1 }) // set counters var toAddCount = added.length var toRemoveCount = removed.length // nothing has been changed if(toAddCount === 0 && toRemoveCount === 0){ cb(); return } // add new tags added.forEach(function(tag){ that.redisClient.multi() .sadd(that.taggable + ":" + id + ":tags", tag) .sadd(that.taggable + ":tags:" + tag, id) .zincrby(that.taggable + ":tags", 1, tag) .exec(function (err, replies) { toAddCount -- if(toAddCount == 0 && toRemoveCount == 0) cb(true) }) }) // remove the rest removed.forEach(function(tag){ that.redisClient.multi() .srem(that.taggable + ":" + id + ":tags", tag) .srem(that.taggable + ":tags:" + tag, id) .zincrby(that.taggable + ":tags", -1, tag) .exec(function (err, replies) { // remove tag from system if count is zero if(replies[2] == "0"){ that.redisClient.zrem(that.taggable + ":tags", tag) } toRemoveCount -- if(toAddCount == 0 && toRemoveCount == 0) cb(true) }) }) }) } Taggable.prototype.set = function(scope, id, tags, cb){ if(cb){ this.scopedSet(scope, id, tags, cb) }else{ // cb = tags // tags = id // id = scope this.unscopedSet(scope, id, tags) } } Taggable.prototype.get = function(scope, id, cb){ // scope if(cb){ this.redisClient.smembers(scope + ":" + this.taggable + ":" + id + ":tags", function(err, reply){ cb(reply) }) }else{ // cb = id // id = scope this.redisClient.smembers(this.taggable + ":" + scope + ":tags", function(err, reply){ id(reply) }) } } Taggable.prototype.find = function(scope, tags, cb){ var sets = [] var that = this // set list of arguments // scope if(cb){ tags.forEach(function(tag){ sets.push(scope + ":" + that.taggable + ":tags:" + tag) }) }else{ // cb = tags // tags = scope scope.forEach(function(tag){ sets.push(that.taggable + ":tags:" + tag) }) cb = tags } this.redisClient.sinter(sets, function(err, reply){ cb(reply) }) } Taggable.prototype.popular = function(scope, count, cb){ // scoped if(cb){ var key = scope + ":" + this.taggable + ":tags" // unscoped }else{ cb = count count = scope var key = this.taggable + ":tags" } this.redisClient.zrevrange(key, 0, count -1, "WITHSCORES", function(err, reply){ var list = [] var type = "key" var tag = [] var counter = reply.length / 2 reply.forEach(function(item){ if(type == "key"){ type = "value" tag[0] = item }else{ type = "key" tag[1] = parseInt(item) list.push(tag) tag = [] counter -- if(counter == 0) cb(list) } }) }) } Taggable.prototype.quit = function(){ this.redisClient.quit() } exports.Taggable = Taggable