UNPKG

taggable-via-redis

Version:

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

225 lines (209 loc) 8.48 kB
// Generated by CoffeeScript 1.6.3 (function() { var EMPTY_ARRAY, EMPTY_STRING, PREFIX, REDIS_CLIENT, assert, debuglog, redis, scopedSet, unscopedSet; redis = require("redis"); debuglog = require("debug")("Taggable"); assert = require("assert"); EMPTY_STRING = ''; EMPTY_ARRAY = []; PREFIX = "_T"; REDIS_CLIENT = null; scopedSet = function(type, scope, id, tags, callback) { var newList, _this = this; debuglog("[scopedSet] type:" + type + ", scope:" + scope + ", id:" + id + ", tags:" + tags); newList = tags; REDIS_CLIENT.smembers("" + PREFIX + ":" + scope + ":" + type + ":" + id + ":tags", function(err, oldList) { var added, removed, toAddCount, toRemoveCount; if (err != null) { return typeof callback === "function" ? callback(err) : void 0; } oldList = oldList || []; added = newList.filter(function(i) { return oldList.indexOf(i) === -1; }); removed = oldList.filter(function(i) { return newList.indexOf(i) === -1; }); toAddCount = added.length; toRemoveCount = removed.length; if (toAddCount === 0 && toRemoveCount === 0) { return typeof callback === "function" ? callback() : void 0; } added.forEach(function(tag) { REDIS_CLIENT.multi().sadd("" + PREFIX + ":" + scope + ":" + type + ":" + id + ":tags", tag).sadd("" + PREFIX + ":" + scope + ":" + type + ":tags:" + tag, id).zincrby("" + PREFIX + ":" + scope + ":" + type + ":tags", 1, tag).sadd("" + PREFIX + ":" + type + ":" + id + ":tags", tag).sadd("" + PREFIX + ":" + type + ":tags:" + tag, id).zincrby("" + PREFIX + ":" + type + ":tags", 1, tag).exec(function(err, replies) { if (err != null) { return typeof callback === "function" ? callback(err) : void 0; } toAddCount--; if (toAddCount <= 0 && toRemoveCount <= 0) { return typeof callback === "function" ? callback() : void 0; } }); }); removed.forEach(function(tag) { REDIS_CLIENT.multi().srem("" + PREFIX + ":" + scope + ":" + type + ":" + id + ":tags", tag).srem("" + PREFIX + ":" + scope + ":" + type + ":tags:" + tag, id).zincrby("" + PREFIX + ":" + scope + ":" + type + ":tags", -1, tag).srem("" + PREFIX + ":" + type + ":" + id + ":tags", tag).srem("" + PREFIX + ":" + type + ":tags:" + tag, id).zincrby("" + PREFIX + ":" + type + ":tags", -1, tag).exec(function(err, replies) { if (err != null) { return typeof callback === "function" ? callback(err) : void 0; } if (replies[2] === "0") { REDIS_CLIENT.zrem("" + PREFIX + ":" + scope + ":" + type + ":tags", tag); } if (replies[5] === "0") { REDIS_CLIENT.zrem("" + PREFIX + ":" + type + ":tags", tag); } toRemoveCount--; if (toAddCount <= 0 && toRemoveCount <= 0) { if (typeof callback === "function") { callback(); } } }); }); }); }; unscopedSet = function(type, id, tags, callback) { var newList, _this = this; debuglog("[unscopedSet] type:" + type + ", id:" + id + ", tags:" + tags); newList = tags; REDIS_CLIENT.smembers("" + PREFIX + ":" + type + ":" + id + ":tags", function(err, oldList) { var added, removed, toAddCount, toRemoveCount; oldList = oldList || []; removed = oldList.filter(function(i) { return newList.indexOf(i) === -1; }); added = newList.filter(function(i) { return oldList.indexOf(i) === -1; }); toAddCount = added.length; toRemoveCount = removed.length; if (toAddCount === 0 && toRemoveCount === 0) { return typeof callback === "function" ? callback() : void 0; } added.forEach(function(tag) { REDIS_CLIENT.multi().sadd("" + PREFIX + ":" + type + ":" + id + ":tags", tag).sadd("" + PREFIX + ":" + type + ":tags:" + tag, id).zincrby("" + PREFIX + ":" + type + ":tags", 1, tag).exec(function(err, replies) { if (err != null) { return typeof callback === "function" ? callback(err) : void 0; } toAddCount--; if (toAddCount === 0 && toRemoveCount === 0) { if (typeof callback === "function") { callback(); } } }); }); removed.forEach(function(tag) { REDIS_CLIENT.multi().srem("" + PREFIX + ":" + type + ":" + id + ":tags", tag).srem("" + PREFIX + ":" + type + ":tags:" + tag, id).zincrby("" + PREFIX + ":" + type + ":tags", -1, tag).exec(function(err, replies) { if (replies[2] === "0") { REDIS_CLIENT.zrem("" + PREFIX + ":" + type + ":tags", tag); } toRemoveCount--; if (toAddCount === 0 && toRemoveCount === 0) { if (typeof callback === "function") { callback(); } } }); }); }); }; exports.init = function(redisClient, prefix) { REDIS_CLIENT = redisClient || REDIS_CLIENT; PREFIX = prefix || PREFIX; }; exports.set = function(type, id, tags, scope, callback) { assert(REDIS_CLIENT, "redis client not init"); type = String(type || EMPTY_STRING); assert(type, "bad argument type:" + type + ")"); id = String(id || EMPTY_STRING); assert(id, "bad argument id:" + id + ")"); if ('function' === typeof scope) { callback = scope; scope = null; } tags = tags || EMPTY_ARRAY; debuglog("[set] type:" + type + ", id:" + id + ", tags:" + tags + ", scope:" + scope + ","); if (scope) { scopedSet(type, scope, id, tags, callback); } else { unscopedSet(type, id, tags, callback); } }; exports.get = function(type, ids, scope, callback) { var id, proc, _i, _len; if ('function' === typeof scope) { callback = scope; scope = ""; } else { scope = scope != null ? "" + scope + ":" : EMPTY_STRING; } debuglog("[get] type:" + type + ", ids:" + ids + ", scope:" + scope); if (!ids) { return typeof callback === "function" ? callback(null, []) : void 0; } if (!(Array.isArray(ids) && ids.length > 0)) { REDIS_CLIENT.smembers("" + PREFIX + ":" + scope + type + ":" + ids + ":tags", callback); } else { proc = REDIS_CLIENT.multi(); for (_i = 0, _len = ids.length; _i < _len; _i++) { id = ids[_i]; proc = proc.smembers("" + PREFIX + ":" + scope + type + ":" + id + ":tags"); } proc.exec(callback); } }; exports.find = function(type, tags, scope, callback) { var i, sets, tag, _i, _len; assert(REDIS_CLIENT, "redis client not init"); if ('function' === typeof scope) { callback = scope; scope = EMPTY_STRING; } else { scope = scope != null ? "" + scope + ":" : EMPTY_STRING; } debuglog("[find] type:" + type + ", tags:" + tags + ", scope:" + scope); if (!(tags || EMPTY_STRING).toString()) { return callback(null, []); } sets = []; if (Array.isArray(tags)) { for (i = _i = 0, _len = tags.length; _i < _len; i = ++_i) { tag = tags[i]; sets.push("" + PREFIX + ":" + scope + type + ":tags:" + tag); } } else { sets.push("" + PREFIX + ":" + scope + type + ":tags:" + tags); } REDIS_CLIENT.sinter(sets, callback); }; exports.popular = function(type, count, scope, callback) { var key; assert(REDIS_CLIENT, "redis client not init"); count = parseInt(count, 10); assert(count > 0, "bad argument count:" + count); if ('function' === typeof scope) { callback = scope; scope = EMPTY_STRING; } else { scope = scope != null ? "" + scope + ":" : EMPTY_STRING; } debuglog("[popular] type:" + type + ", count:" + count + ", scope:" + scope); key = "" + PREFIX + ":" + scope + type + ":tags"; REDIS_CLIENT.zrevrange(key, 0, count - 1, "WITHSCORES", function(err, reply) { var i, item, results, _i, _len; if (err != null) { return typeof callback === "function" ? callback(err) : void 0; } results = []; for (i = _i = 0, _len = reply.length; _i < _len; i = _i += 2) { item = reply[i]; results.push([reply[i], parseInt(reply[i + 1], 10)]); } if (typeof callback === "function") { callback(null, results); } }); }; }).call(this);