diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index 583794a380..b9d511d94d 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -6,113 +6,10 @@ var utils = require('../../../public/src/utils'); module.exports = function (db, module) { var helpers = module.helpers.mongo; - module.sortedSetAdd = function (key, score, value, callback) { - callback = callback || helpers.noop; - if (!key) { - return callback(); - } - if (Array.isArray(score) && Array.isArray(value)) { - return sortedSetAddBulk(key, score, value, callback); - } - - value = helpers.valueToString(value); - - db.collection('objects').update({_key: key, value: value}, {$set: {score: parseFloat(score)}}, {upsert:true, w: 1}, function (err) { - if (err && err.message.startsWith('E11000 duplicate key error')) { - return process.nextTick(module.sortedSetAdd, key, score, value, callback); - } - callback(err); - }); - }; - - function sortedSetAddBulk(key, scores, values, callback) { - if (!scores.length || !values.length) { - return callback(); - } - if (scores.length !== values.length) { - return callback(new Error('[[error:invalid-data]]')); - } - - values = values.map(helpers.valueToString); - - var bulk = db.collection('objects').initializeUnorderedBulkOp(); - - for(var i = 0; i < scores.length; ++i) { - bulk.find({_key: key, value: values[i]}).upsert().updateOne({$set: {score: parseFloat(scores[i])}}); - } - - bulk.execute(function (err) { - callback(err); - }); - } - - module.sortedSetsAdd = function (keys, score, value, callback) { - callback = callback || helpers.noop; - if (!Array.isArray(keys) || !keys.length) { - return callback(); - } - value = helpers.valueToString(value); - - var bulk = db.collection('objects').initializeUnorderedBulkOp(); - - for(var i = 0; i < keys.length; ++i) { - bulk.find({_key: keys[i], value: value}).upsert().updateOne({$set: {score: parseFloat(score)}}); - } - - bulk.execute(function (err) { - callback(err); - }); - }; - - module.sortedSetRemove = function (key, value, callback) { - function done(err) { - callback(err); - } - callback = callback || helpers.noop; - if (!key) { - return callback(); - } - - if (Array.isArray(value)) { - value = value.map(helpers.valueToString); - db.collection('objects').remove({_key: key, value: {$in: value}}, done); - } else { - value = helpers.valueToString(value); - db.collection('objects').remove({_key: key, value: value}, done); - } - }; - - module.sortedSetsRemove = function (keys, value, callback) { - callback = callback || helpers.noop; - if (!Array.isArray(keys) || !keys.length) { - return callback(); - } - value = helpers.valueToString(value); - - db.collection('objects').remove({_key: {$in: keys}, value: value}, function (err) { - callback(err); - }); - }; - - module.sortedSetsRemoveRangeByScore = function (keys, min, max, callback) { - callback = callback || helpers.noop; - if (!Array.isArray(keys) || !keys.length) { - return callback(); - } - var query = {_key: {$in: keys}}; - - if (min !== '-inf') { - query.score = {$gte: min}; - } - if (max !== '+inf') { - query.score = query.score || {}; - query.score.$lte = max; - } - - db.collection('objects').remove(query, function (err) { - callback(err); - }); - }; + require('./sorted/add')(db, module); + require('./sorted/remove')(db, module); + require('./sorted/union')(db, module); + require('./sorted/intersect')(db, module); module.getSortedSetRange = function (key, start, stop, callback) { getSortedSetRange(key, start, stop, 1, false, callback); @@ -188,7 +85,7 @@ module.exports = function (db, module) { if (!key) { return callback(); } - if(parseInt(count, 10) === -1) { + if (parseInt(count, 10) === -1) { count = 0; } @@ -212,7 +109,7 @@ module.exports = function (db, module) { .skip(start) .sort({score: sort}) .toArray(function (err, data) { - if(err) { + if (err) { return callback(err); } @@ -477,86 +374,6 @@ module.exports = function (db, module) { }); }; - module.sortedSetUnionCard = function (keys, callback) { - if (!Array.isArray(keys) || !keys.length) { - return callback(null, 0); - } - - var pipeline = [ - { $match: { _key: {$in: keys} } }, - { $group: { _id: {value: '$value' } } }, - { $group: { _id: null, count: { $sum: 1 } } } - ]; - - var project = { _id: 0, count: '$count' }; - pipeline.push({ $project: project }); - - db.collection('objects').aggregate(pipeline, function (err, data) { - callback(err, Array.isArray(data) && data.length ? data[0].count : 0); - }); - }; - - module.getSortedSetUnion = function (params, callback) { - params.sort = 1; - getSortedSetUnion(params, callback); - }; - - module.getSortedSetRevUnion = function (params, callback) { - params.sort = -1; - getSortedSetUnion(params, callback); - }; - - function getSortedSetUnion(params, callback) { - if (!Array.isArray(params.sets) || !params.sets.length) { - return callback(); - } - var limit = params.stop - params.start + 1; - if (limit <= 0) { - limit = 0; - } - - var aggregate = {}; - if (params.aggregate) { - aggregate['$' + params.aggregate.toLowerCase()] = '$score'; - } else { - aggregate.$sum = '$score'; - } - - var pipeline = [ - { $match: { _key: {$in: params.sets}} }, - { $group: { _id: {value: '$value'}, totalScore: aggregate} }, - { $sort: { totalScore: params.sort} } - ]; - - if (params.start) { - pipeline.push({ $skip: params.start }); - } - - if (limit > 0) { - pipeline.push({ $limit: limit }); - } - - var project = { _id: 0, value: '$_id.value' }; - if (params.withScores) { - project.score = '$totalScore'; - } - pipeline.push({ $project: project }); - - db.collection('objects').aggregate(pipeline, function (err, data) { - if (err || !data) { - return callback(err); - } - - if (!params.withScores) { - data = data.map(function (item) { - return item.value; - }); - } - - callback(null, data); - }); - } - module.sortedSetIncrBy = function (key, increment, value, callback) { callback = callback || helpers.noop; if (!key) { @@ -600,26 +417,7 @@ module.exports = function (db, module) { } var query = {_key: key}; - - if (min !== '-') { - if (min.match(/^\(/)) { - query.value = {$gt: min.slice(1)}; - } else if (min.match(/^\[/)) { - query.value = {$gte: min.slice(1)}; - } else { - query.value = {$gte: min}; - } - } - if (max !== '+') { - query.value = query.value || {}; - if (max.match(/^\(/)) { - query.value.$lt = max.slice(1); - } else if (max.match(/^\[/)) { - query.value.$lte = max.slice(1); - } else { - query.value.$lte = max; - } - } + buildLexQuery(query, min, max); db.collection('objects').find(query, {_id: 0, value: 1}) .sort({value: sort}) @@ -640,7 +438,14 @@ module.exports = function (db, module) { callback = callback || helpers.noop; var query = {_key: key}; + buildLexQuery(query, min, max); + + db.collection('objects').remove(query, function (err) { + callback(err); + }); + }; + function buildLexQuery(query, min, max) { if (min !== '-') { if (min.match(/^\(/)) { query.value = {$gt: min.slice(1)}; @@ -660,11 +465,7 @@ module.exports = function (db, module) { query.value.$lte = max; } } - - db.collection('objects').remove(query, function (err) { - callback(err); - }); - }; + } module.processSortedSet = function (setKey, process, batch, callback) { var done = false; @@ -703,97 +504,4 @@ module.exports = function (db, module) { ); }; - module.sortedSetIntersectCard = function (keys, callback) { - if (!Array.isArray(keys) || !keys.length) { - return callback(null, 0); - } - - var pipeline = [ - { $match: { _key: {$in: keys}} }, - { $group: { _id: {value: '$value'}, count: {$sum: 1}} }, - { $match: { count: keys.length} }, - { $group: { _id: null, count: { $sum: 1 } } } - ]; - - db.collection('objects').aggregate(pipeline, function (err, data) { - callback(err, Array.isArray(data) && data.length ? data[0].count : 0); - }); - }; - - module.getSortedSetIntersect = function (params, callback) { - params.sort = 1; - getSortedSetRevIntersect(params, callback); - }; - - module.getSortedSetRevIntersect = function (params, callback) { - params.sort = -1; - getSortedSetRevIntersect(params, callback); - }; - - function getSortedSetRevIntersect(params, callback) { - var sets = params.sets; - var start = params.hasOwnProperty('start') ? params.start : 0; - var stop = params.hasOwnProperty('stop') ? params.stop : -1; - var weights = params.weights || []; - var aggregate = {}; - - if (params.aggregate) { - aggregate['$' + params.aggregate.toLowerCase()] = '$score'; - } else { - aggregate.$sum = '$score'; - } - - var limit = stop - start + 1; - if (limit <= 0) { - limit = 0; - } - - var pipeline = [{ $match: { _key: {$in: sets}} }]; - - weights.forEach(function (weight, index) { - if (weight !== 1) { - pipeline.push({ - $project: { - value: 1, - score: { - $cond: { if: { $eq: [ "$_key", sets[index] ] }, then: { $multiply: [ '$score', weight ] }, else: '$score' } - } - } - }); - } - }); - - pipeline.push({ $group: { _id: {value: '$value'}, totalScore: aggregate, count: {$sum: 1}} }); - pipeline.push({ $match: { count: sets.length} }); - pipeline.push({ $sort: { totalScore: params.sort} }); - - if (start) { - pipeline.push({ $skip: start }); - } - - if (limit > 0) { - pipeline.push({ $limit: limit }); - } - - var project = { _id: 0, value: '$_id.value'}; - if (params.withScores) { - project.score = '$totalScore'; - } - pipeline.push({ $project: project }); - - db.collection('objects').aggregate(pipeline, function (err, data) { - if (err || !data) { - return callback(err); - } - - if (!params.withScores) { - data = data.map(function (item) { - return item.value; - }); - } - - callback(null, data); - }); - } - }; diff --git a/src/database/mongo/sorted/add.js b/src/database/mongo/sorted/add.js new file mode 100644 index 0000000000..e03452b3df --- /dev/null +++ b/src/database/mongo/sorted/add.js @@ -0,0 +1,65 @@ +'use strict'; + +module.exports = function (db, module) { + + var helpers = module.helpers.mongo; + + module.sortedSetAdd = function (key, score, value, callback) { + callback = callback || helpers.noop; + if (!key) { + return callback(); + } + if (Array.isArray(score) && Array.isArray(value)) { + return sortedSetAddBulk(key, score, value, callback); + } + + value = helpers.valueToString(value); + + db.collection('objects').update({_key: key, value: value}, {$set: {score: parseFloat(score)}}, {upsert:true, w: 1}, function (err) { + if (err && err.message.startsWith('E11000 duplicate key error')) { + return process.nextTick(module.sortedSetAdd, key, score, value, callback); + } + callback(err); + }); + }; + + function sortedSetAddBulk(key, scores, values, callback) { + if (!scores.length || !values.length) { + return callback(); + } + if (scores.length !== values.length) { + return callback(new Error('[[error:invalid-data]]')); + } + + values = values.map(helpers.valueToString); + + var bulk = db.collection('objects').initializeUnorderedBulkOp(); + + for(var i = 0; i < scores.length; ++i) { + bulk.find({_key: key, value: values[i]}).upsert().updateOne({$set: {score: parseFloat(scores[i])}}); + } + + bulk.execute(function (err) { + callback(err); + }); + } + + module.sortedSetsAdd = function (keys, score, value, callback) { + callback = callback || helpers.noop; + if (!Array.isArray(keys) || !keys.length) { + return callback(); + } + value = helpers.valueToString(value); + + var bulk = db.collection('objects').initializeUnorderedBulkOp(); + + for(var i = 0; i < keys.length; ++i) { + bulk.find({_key: keys[i], value: value}).upsert().updateOne({$set: {score: parseFloat(score)}}); + } + + bulk.execute(function (err) { + callback(err); + }); + }; + +}; \ No newline at end of file diff --git a/src/database/mongo/sorted/intersect.js b/src/database/mongo/sorted/intersect.js new file mode 100644 index 0000000000..ed8ade3fb3 --- /dev/null +++ b/src/database/mongo/sorted/intersect.js @@ -0,0 +1,99 @@ +'use strict'; + +module.exports = function (db, module) { + + module.sortedSetIntersectCard = function (keys, callback) { + if (!Array.isArray(keys) || !keys.length) { + return callback(null, 0); + } + + var pipeline = [ + { $match: { _key: {$in: keys}} }, + { $group: { _id: {value: '$value'}, count: {$sum: 1}} }, + { $match: { count: keys.length} }, + { $group: { _id: null, count: { $sum: 1 } } } + ]; + + db.collection('objects').aggregate(pipeline, function (err, data) { + callback(err, Array.isArray(data) && data.length ? data[0].count : 0); + }); + }; + + + module.getSortedSetIntersect = function (params, callback) { + params.sort = 1; + getSortedSetRevIntersect(params, callback); + }; + + module.getSortedSetRevIntersect = function (params, callback) { + params.sort = -1; + getSortedSetRevIntersect(params, callback); + }; + + function getSortedSetRevIntersect(params, callback) { + var sets = params.sets; + var start = params.hasOwnProperty('start') ? params.start : 0; + var stop = params.hasOwnProperty('stop') ? params.stop : -1; + var weights = params.weights || []; + var aggregate = {}; + + if (params.aggregate) { + aggregate['$' + params.aggregate.toLowerCase()] = '$score'; + } else { + aggregate.$sum = '$score'; + } + + var limit = stop - start + 1; + if (limit <= 0) { + limit = 0; + } + + var pipeline = [{ $match: { _key: {$in: sets}} }]; + + weights.forEach(function (weight, index) { + if (weight !== 1) { + pipeline.push({ + $project: { + value: 1, + score: { + $cond: { if: { $eq: [ "$_key", sets[index] ] }, then: { $multiply: [ '$score', weight ] }, else: '$score' } + } + } + }); + } + }); + + pipeline.push({ $group: { _id: {value: '$value'}, totalScore: aggregate, count: {$sum: 1}} }); + pipeline.push({ $match: { count: sets.length} }); + pipeline.push({ $sort: { totalScore: params.sort} }); + + if (start) { + pipeline.push({ $skip: start }); + } + + if (limit > 0) { + pipeline.push({ $limit: limit }); + } + + var project = { _id: 0, value: '$_id.value'}; + if (params.withScores) { + project.score = '$totalScore'; + } + pipeline.push({ $project: project }); + + db.collection('objects').aggregate(pipeline, function (err, data) { + if (err || !data) { + return callback(err); + } + + if (!params.withScores) { + data = data.map(function (item) { + return item.value; + }); + } + + callback(null, data); + }); + } + +}; \ No newline at end of file diff --git a/src/database/mongo/sorted/remove.js b/src/database/mongo/sorted/remove.js new file mode 100644 index 0000000000..71320619c4 --- /dev/null +++ b/src/database/mongo/sorted/remove.js @@ -0,0 +1,57 @@ +'use strict'; + +module.exports = function (db, module) { + + var helpers = module.helpers.mongo; + + module.sortedSetRemove = function (key, value, callback) { + function done(err) { + callback(err); + } + callback = callback || helpers.noop; + if (!key) { + return callback(); + } + + if (Array.isArray(value)) { + value = value.map(helpers.valueToString); + db.collection('objects').remove({_key: key, value: {$in: value}}, done); + } else { + value = helpers.valueToString(value); + db.collection('objects').remove({_key: key, value: value}, done); + } + }; + + module.sortedSetsRemove = function (keys, value, callback) { + callback = callback || helpers.noop; + if (!Array.isArray(keys) || !keys.length) { + return callback(); + } + value = helpers.valueToString(value); + + db.collection('objects').remove({_key: {$in: keys}, value: value}, function (err) { + callback(err); + }); + }; + + module.sortedSetsRemoveRangeByScore = function (keys, min, max, callback) { + callback = callback || helpers.noop; + if (!Array.isArray(keys) || !keys.length) { + return callback(); + } + var query = {_key: {$in: keys}}; + + if (min !== '-inf') { + query.score = {$gte: min}; + } + if (max !== '+inf') { + query.score = query.score || {}; + query.score.$lte = max; + } + + db.collection('objects').remove(query, function (err) { + callback(err); + }); + }; + +}; \ No newline at end of file diff --git a/src/database/mongo/sorted/union.js b/src/database/mongo/sorted/union.js new file mode 100644 index 0000000000..49669bd830 --- /dev/null +++ b/src/database/mongo/sorted/union.js @@ -0,0 +1,85 @@ +'use strict'; + +module.exports = function (db, module) { + + module.sortedSetUnionCard = function (keys, callback) { + if (!Array.isArray(keys) || !keys.length) { + return callback(null, 0); + } + + var pipeline = [ + { $match: { _key: {$in: keys} } }, + { $group: { _id: {value: '$value' } } }, + { $group: { _id: null, count: { $sum: 1 } } } + ]; + + var project = { _id: 0, count: '$count' }; + pipeline.push({ $project: project }); + + db.collection('objects').aggregate(pipeline, function (err, data) { + callback(err, Array.isArray(data) && data.length ? data[0].count : 0); + }); + }; + + module.getSortedSetUnion = function (params, callback) { + params.sort = 1; + getSortedSetUnion(params, callback); + }; + + module.getSortedSetRevUnion = function (params, callback) { + params.sort = -1; + getSortedSetUnion(params, callback); + }; + + function getSortedSetUnion(params, callback) { + if (!Array.isArray(params.sets) || !params.sets.length) { + return callback(); + } + var limit = params.stop - params.start + 1; + if (limit <= 0) { + limit = 0; + } + + var aggregate = {}; + if (params.aggregate) { + aggregate['$' + params.aggregate.toLowerCase()] = '$score'; + } else { + aggregate.$sum = '$score'; + } + + var pipeline = [ + { $match: { _key: {$in: params.sets}} }, + { $group: { _id: {value: '$value'}, totalScore: aggregate} }, + { $sort: { totalScore: params.sort} } + ]; + + if (params.start) { + pipeline.push({ $skip: params.start }); + } + + if (limit > 0) { + pipeline.push({ $limit: limit }); + } + + var project = { _id: 0, value: '$_id.value' }; + if (params.withScores) { + project.score = '$totalScore'; + } + pipeline.push({ $project: project }); + + db.collection('objects').aggregate(pipeline, function (err, data) { + if (err || !data) { + return callback(err); + } + + if (!params.withScores) { + data = data.map(function (item) { + return item.value; + }); + } + + callback(null, data); + }); + } + +}; \ No newline at end of file diff --git a/src/database/redis/sorted.js b/src/database/redis/sorted.js index 9e50c158c7..978c8bc765 100644 --- a/src/database/redis/sorted.js +++ b/src/database/redis/sorted.js @@ -6,79 +6,10 @@ module.exports = function (redisClient, module) { var helpers = module.helpers.redis; - module.sortedSetAdd = function (key, score, value, callback) { - callback = callback || function () {}; - if (Array.isArray(score) && Array.isArray(value)) { - return sortedSetAddMulti(key, score, value, callback); - } - redisClient.zadd(key, score, value, function (err) { - callback(err); - }); - }; - - function sortedSetAddMulti(key, scores, values, callback) { - if (!scores.length || !values.length) { - return callback(); - } - - if (scores.length !== values.length) { - return callback(new Error('[[error:invalid-data]]')); - } - - var args = [key]; - - for(var i = 0; i < scores.length; ++i) { - args.push(scores[i], values[i]); - } - - redisClient.zadd(args, function (err) { - callback(err); - }); - } - - module.sortedSetsAdd = function (keys, score, value, callback) { - callback = callback || function () {}; - var multi = redisClient.multi(); - - for(var i = 0; i < keys.length; ++i) { - multi.zadd(keys[i], score, value); - } - - multi.exec(function (err) { - callback(err); - }); - }; - - module.sortedSetRemove = function (key, value, callback) { - callback = callback || function () {}; - if (!value) { - return callback(); - } - if (!Array.isArray(value)) { - value = [value]; - } - - helpers.multiKeyValues(redisClient, 'zrem', key, value, function (err) { - callback(err); - }); - }; - - module.sortedSetsRemove = function (keys, value, callback) { - helpers.multiKeysValue(redisClient, 'zrem', keys, value, function (err) { - callback(err); - }); - }; - - module.sortedSetsRemoveRangeByScore = function (keys, min, max, callback) { - callback = callback || function () {}; - var multi = redisClient.multi(); - for(var i = 0; i < keys.length; ++i) { - multi.zremrangebyscore(keys[i], min, max); - } - multi.exec(function (err) { - callback(err); - }); - }; + require('./sorted/add')(redisClient, module); + require('./sorted/remove')(redisClient, module); + require('./sorted/union')(redisClient, module); + require('./sorted/intersect')(redisClient, module); module.getSortedSetRange = function (key, start, stop, callback) { sortedSetRange('zrange', key, start, stop, false, callback); @@ -239,62 +170,6 @@ module.exports = function (redisClient, module) { multi.exec(callback); }; - - module.sortedSetUnionCard = function (keys, callback) { - var tempSetName = 'temp_' + Date.now(); - - var multi = redisClient.multi(); - multi.zunionstore([tempSetName, keys.length].concat(keys)); - multi.zcard(tempSetName); - multi.del(tempSetName); - multi.exec(function (err, results) { - if (err) { - return callback(err); - } - - callback(null, Array.isArray(results) && results.length ? results[1] : 0); - }); - }; - - module.getSortedSetUnion = function (params, callback) { - params.method = 'zrange'; - sortedSetUnion(params, callback); - }; - - module.getSortedSetRevUnion = function (params, callback) { - params.method = 'zrevrange'; - sortedSetUnion(params, callback); - }; - - function sortedSetUnion(params, callback) { - - var tempSetName = 'temp_' + Date.now(); - - var rangeParams = [tempSetName, params.start, params.stop]; - if (params.withScores) { - rangeParams.push('WITHSCORES'); - } - - var multi = redisClient.multi(); - multi.zunionstore([tempSetName, params.sets.length].concat(params.sets)); - multi[params.method](rangeParams); - multi.del(tempSetName); - multi.exec(function (err, results) { - if (err) { - return callback(err); - } - if (!params.withScores) { - return callback(null, results ? results[1] : null); - } - results = results[1] || []; - var objects = []; - for(var i = 0; i < results.length; i += 2) { - objects.push({value: results[i], score: parseFloat(results[i + 1])}); - } - callback(null, objects); - }); - } - module.sortedSetIncrBy = function (key, increment, value, callback) { redisClient.zincrby(key, increment, value, function (err, newValue) { callback(err, !err ? parseFloat(newValue) : undefined); @@ -332,11 +207,11 @@ module.exports = function (redisClient, module) { maxmax = '+'; } - if (min !== minmin) { - if (!min.match(/^[\[\(]/)) min = '[' + min; + if (min !== minmin && !min.match(/^[\[\(]/)) { + min = '[' + min; } - if (max !== maxmax) { - if (!max.match(/^[\[\(]/)) max = '[' + max; + if (max !== maxmax && !max.match(/^[\[\(]/)) { + max = '[' + max; } if (count) { @@ -345,78 +220,4 @@ module.exports = function (redisClient, module) { redisClient[method]([key, min, max], callback); } } - - module.sortedSetIntersectCard = function (keys, callback) { - if (!Array.isArray(keys) || !keys.length) { - return callback(null, 0); - } - var tempSetName = 'temp_' + Date.now(); - - var interParams = [tempSetName, keys.length].concat(keys); - - var multi = redisClient.multi(); - multi.zinterstore(interParams); - multi.zcard(tempSetName); - multi.del(tempSetName); - multi.exec(function (err, results) { - if (err) { - return callback(err); - } - - callback(null, results[1] || 0); - }); - }; - - module.getSortedSetIntersect = function (params, callback) { - params.method = 'zrange'; - getSortedSetRevIntersect(params, callback); - }; - - module.getSortedSetRevIntersect = function (params, callback) { - params.method = 'zrevrange'; - getSortedSetRevIntersect(params, callback); - }; - - function getSortedSetRevIntersect(params, callback) { - var sets = params.sets; - var start = params.hasOwnProperty('start') ? params.start : 0; - var stop = params.hasOwnProperty('stop') ? params.stop : -1; - var weights = params.weights || []; - - var tempSetName = 'temp_' + Date.now(); - - var interParams = [tempSetName, sets.length].concat(sets); - if (weights.length) { - interParams = interParams.concat(['WEIGHTS'].concat(weights)); - } - - if (params.aggregate) { - interParams = interParams.concat(['AGGREGATE', params.aggregate]); - } - - var rangeParams = [tempSetName, start, stop]; - if (params.withScores) { - rangeParams.push('WITHSCORES'); - } - - var multi = redisClient.multi(); - multi.zinterstore(interParams); - multi[params.method](rangeParams); - multi.del(tempSetName); - multi.exec(function (err, results) { - if (err) { - return callback(err); - } - - if (!params.withScores) { - return callback(null, results ? results[1] : null); - } - results = results[1] || []; - var objects = []; - for(var i = 0; i < results.length; i += 2) { - objects.push({value: results[i], score: parseFloat(results[i + 1])}); - } - callback(null, objects); - }); - } }; diff --git a/src/database/redis/sorted/add.js b/src/database/redis/sorted/add.js new file mode 100644 index 0000000000..b2eb710221 --- /dev/null +++ b/src/database/redis/sorted/add.js @@ -0,0 +1,49 @@ +'use strict'; + +module.exports = function (redisClient, module) { + + module.sortedSetAdd = function (key, score, value, callback) { + callback = callback || function () {}; + if (Array.isArray(score) && Array.isArray(value)) { + return sortedSetAddMulti(key, score, value, callback); + } + redisClient.zadd(key, score, value, function (err) { + callback(err); + }); + }; + + function sortedSetAddMulti(key, scores, values, callback) { + if (!scores.length || !values.length) { + return callback(); + } + + if (scores.length !== values.length) { + return callback(new Error('[[error:invalid-data]]')); + } + + var args = [key]; + + for(var i = 0; i < scores.length; ++i) { + args.push(scores[i], values[i]); + } + + redisClient.zadd(args, function (err) { + callback(err); + }); + } + + module.sortedSetsAdd = function (keys, score, value, callback) { + callback = callback || function () {}; + var multi = redisClient.multi(); + + for(var i = 0; i < keys.length; ++i) { + multi.zadd(keys[i], score, value); + } + + multi.exec(function (err) { + callback(err); + }); + }; + + +}; \ No newline at end of file diff --git a/src/database/redis/sorted/intersect.js b/src/database/redis/sorted/intersect.js new file mode 100644 index 0000000000..a4e3c131b4 --- /dev/null +++ b/src/database/redis/sorted/intersect.js @@ -0,0 +1,79 @@ + +'use strict'; + +module.exports = function (redisClient, module) { + + module.sortedSetIntersectCard = function (keys, callback) { + if (!Array.isArray(keys) || !keys.length) { + return callback(null, 0); + } + var tempSetName = 'temp_' + Date.now(); + + var interParams = [tempSetName, keys.length].concat(keys); + + var multi = redisClient.multi(); + multi.zinterstore(interParams); + multi.zcard(tempSetName); + multi.del(tempSetName); + multi.exec(function (err, results) { + if (err) { + return callback(err); + } + + callback(null, results[1] || 0); + }); + }; + + module.getSortedSetIntersect = function (params, callback) { + params.method = 'zrange'; + getSortedSetRevIntersect(params, callback); + }; + + module.getSortedSetRevIntersect = function (params, callback) { + params.method = 'zrevrange'; + getSortedSetRevIntersect(params, callback); + }; + + function getSortedSetRevIntersect(params, callback) { + var sets = params.sets; + var start = params.hasOwnProperty('start') ? params.start : 0; + var stop = params.hasOwnProperty('stop') ? params.stop : -1; + var weights = params.weights || []; + + var tempSetName = 'temp_' + Date.now(); + + var interParams = [tempSetName, sets.length].concat(sets); + if (weights.length) { + interParams = interParams.concat(['WEIGHTS'].concat(weights)); + } + + if (params.aggregate) { + interParams = interParams.concat(['AGGREGATE', params.aggregate]); + } + + var rangeParams = [tempSetName, start, stop]; + if (params.withScores) { + rangeParams.push('WITHSCORES'); + } + + var multi = redisClient.multi(); + multi.zinterstore(interParams); + multi[params.method](rangeParams); + multi.del(tempSetName); + multi.exec(function (err, results) { + if (err) { + return callback(err); + } + + if (!params.withScores) { + return callback(null, results ? results[1] : null); + } + results = results[1] || []; + var objects = []; + for(var i = 0; i < results.length; i += 2) { + objects.push({value: results[i], score: parseFloat(results[i + 1])}); + } + callback(null, objects); + }); + } +}; \ No newline at end of file diff --git a/src/database/redis/sorted/remove.js b/src/database/redis/sorted/remove.js new file mode 100644 index 0000000000..aa1d42a3ce --- /dev/null +++ b/src/database/redis/sorted/remove.js @@ -0,0 +1,38 @@ + +'use strict'; + +module.exports = function (redisClient, module) { + + var helpers = module.helpers.redis; + + module.sortedSetRemove = function (key, value, callback) { + callback = callback || function () {}; + if (!value) { + return callback(); + } + if (!Array.isArray(value)) { + value = [value]; + } + + helpers.multiKeyValues(redisClient, 'zrem', key, value, function (err) { + callback(err); + }); + }; + + module.sortedSetsRemove = function (keys, value, callback) { + helpers.multiKeysValue(redisClient, 'zrem', keys, value, function (err) { + callback(err); + }); + }; + + module.sortedSetsRemoveRangeByScore = function (keys, min, max, callback) { + callback = callback || function () {}; + var multi = redisClient.multi(); + for(var i = 0; i < keys.length; ++i) { + multi.zremrangebyscore(keys[i], min, max); + } + multi.exec(function (err) { + callback(err); + }); + }; +}; \ No newline at end of file diff --git a/src/database/redis/sorted/union.js b/src/database/redis/sorted/union.js new file mode 100644 index 0000000000..2a03f2f142 --- /dev/null +++ b/src/database/redis/sorted/union.js @@ -0,0 +1,60 @@ + +'use strict'; + +module.exports = function (redisClient, module) { + + module.sortedSetUnionCard = function (keys, callback) { + var tempSetName = 'temp_' + Date.now(); + + var multi = redisClient.multi(); + multi.zunionstore([tempSetName, keys.length].concat(keys)); + multi.zcard(tempSetName); + multi.del(tempSetName); + multi.exec(function (err, results) { + if (err) { + return callback(err); + } + + callback(null, Array.isArray(results) && results.length ? results[1] : 0); + }); + }; + + module.getSortedSetUnion = function (params, callback) { + params.method = 'zrange'; + sortedSetUnion(params, callback); + }; + + module.getSortedSetRevUnion = function (params, callback) { + params.method = 'zrevrange'; + sortedSetUnion(params, callback); + }; + + function sortedSetUnion(params, callback) { + + var tempSetName = 'temp_' + Date.now(); + + var rangeParams = [tempSetName, params.start, params.stop]; + if (params.withScores) { + rangeParams.push('WITHSCORES'); + } + + var multi = redisClient.multi(); + multi.zunionstore([tempSetName, params.sets.length].concat(params.sets)); + multi[params.method](rangeParams); + multi.del(tempSetName); + multi.exec(function (err, results) { + if (err) { + return callback(err); + } + if (!params.withScores) { + return callback(null, results ? results[1] : null); + } + results = results[1] || []; + var objects = []; + for(var i = 0; i < results.length; i += 2) { + objects.push({value: results[i], score: parseFloat(results[i + 1])}); + } + callback(null, objects); + }); + } +}; \ No newline at end of file