Browse files

Add support for sorting only those datasets that are in a given array…

… of ids
  • Loading branch information...
1 parent 388bff0 commit 54d24ec5d0adc063dd3396d85e0c3d7e87c8a61f @maritz committed Jan 17, 2012
Showing with 158 additions and 26 deletions.
  1. +4 −0 README.md
  2. +2 −2 lib/nohm.js
  3. +28 −15 lib/retrieve.js
  4. +124 −9 test/findTests.js
View
4 README.md
@@ -4,6 +4,10 @@
Nohm is an object relational mapper (ORM) written for node.js and redis.
+## Requirements
+
+* redis >= 2.4
+
## Install
### If you haven't done this yet: install npm
View
4 lib/nohm.js
@@ -62,9 +62,9 @@ Nohm.model = function (name, options) {
// this creates a few functions for short-form like: SomeModel.load(1, function (err, props) { /* `this` is someModelInstance here */ });
var shortFormFuncs = ['load', 'find', 'save', 'sort'];
shortFormFuncs.forEach(function (val) {
- obj[val] = function (arg1, arg2) {
+ obj[val] = function () {
var instance = new obj();
- instance[val](arg1, arg2);
+ instance[val].apply(instance, Array.prototype.slice.call(arguments, 0));
};
});
View
43 lib/retrieve.js
@@ -168,7 +168,7 @@ exports.find = function find(searches, callback) {
exports.sort = function (options, ids) {
var callback = h.getCallback(arguments);
- if ( ! Array.isArray(ids)) {
+ if ( ! Array.isArray(ids) || ids.length === 0) {
ids = false;
}
options = typeof(options) !== 'function' && typeof(options) === 'object' && Object.keys(options).length > 0 ? options : {};
@@ -200,35 +200,48 @@ exports.sort = function (options, ids) {
stop = options.limit[1] || stop;
}
}
-
+ var idset_key = Nohm.prefix.idsets+this.modelName;
+ var zset_key = Nohm.prefix.scoredindex+this.modelName+':'+options.field;
if (ids) {
// to get the intersection of the given ids and all ids on the server we first
// temporarily store the given ids either in a set or sorted set and then return the intersection
- console.log('NOT YET IMPLEMENTED PROPERLY');
- return false;
+
var multi = this.getClient().multi();
- var tmp_idset_key = Nohm.prefix.idsets+this.modelName+':'+(+ new Date()) + Math.ceil(Math.random()*1000);
+ var tmp_key;
if (scored) {
-
+ tmp_key = zset_key+':tmp_sort:'+(+ new Date()) + Math.ceil(Math.random()*1000);
+ var tmp_zadd_args = [tmp_key];
+ ids.forEach(function (id) {
+ tmp_zadd_args.push(0, id);
+ });
+ multi.zadd(tmp_zadd_args);
+ multi.zinterstore([tmp_key, 2, tmp_key, zset_key]);
+ sortScored.call(this, multi, tmp_key, direction, start, stop, callback);
} else {
- multi.SADD(idset_key, ids);
+ tmp_key = idset_key+':tmp_sort:'+(+ new Date()) + Math.ceil(Math.random()*1000);
+ ids.unshift(tmp_key);
+ multi.SADD(ids);
+ multi.SINTERSTORE([tmp_key, tmp_key, idset_key]);
+ sortNormal.call(this, multi, tmp_key, options.field, alpha, direction, start, stop, callback);
}
+ multi.del(tmp_key);
+ multi.exec(Nohm.logError);
+
} else {
// no ids provided
if (scored) {
- sortScored.call(this, options.field, direction, start, stop, callback);
+ sortScored.call(this, this.getClient(), zset_key, direction, start, stop, callback);
} else {
- sortNormal.call(this, options.field, alpha, direction, start, stop, callback);
+ sortNormal.call(this, this.getClient(), idset_key, options.field, alpha, direction, start, stop, callback);
}
}
};
-var sortNormal = function (field, alpha, direction, start, stop, callback) {
- var idset_key = Nohm.prefix.idsets+this.modelName;
+var sortNormal = function (client, idset_key, field, alpha, direction, start, stop, callback) {
var hash_key = Nohm.prefix.hash+this.modelName;
- this.getClient().sort([idset_key,
+ client.sort([idset_key,
'BY', hash_key+':*->'+field,
'LIMIT', start, stop,
direction,
@@ -237,16 +250,16 @@ var sortNormal = function (field, alpha, direction, start, stop, callback) {
};
-var sortScored = function (field, direction, start, stop, callback) {
+var sortScored = function (client, zset_key, direction, start, stop, callback) {
var method = direction && direction === 'DESC' ? 'ZREVRANGE' : 'ZRANGE';
if (start < 0 || stop < 0) {
Nohm.logError('Notice: tried to limit a scored sort with a negative start('+start+') or stop('+stop+').');
}
if (stop < start) {
Nohm.logError('Notice: tried to limit a scored sort with a higher start('+start+') than stop('+stop+').');
}
- this.getClient()[method](
- [Nohm.prefix.scoredindex+this.modelName+':'+field,
+ client[method](
+ [zset_key,
start, stop],
callback
);
View
133 test/findTests.js
@@ -750,7 +750,7 @@ loadArray: function (t) {
}).slice(3, 6)
.map(function (user) {
return ''+user.id;
- });
+ });
UserFindMockup.sort({
field: 'number',
@@ -760,17 +760,132 @@ loadArray: function (t) {
t.same(sorted_ids, ids, 'Sorting went wrong.');
t.done();
});
- }
- /*
- "provided and default": function (t) {
+ },
+ "provided by name": function (t) {
+ t.expect(2);
+
+ var sorted_ids = this.users.sort(function (a, b) {
+ a = a.p('name');
+ b = b.p('name');
+ return a > b ? 1 : (a < b ? -1 : 0);
+ }).map(function (user) {
+ return ''+user.id;
+ });
+
+ UserFindMockup.sort({
+ field: 'name'
+ }, this.userIds, function (err, ids) {
+ t.same(null, err, 'Sorting caused an error: '+err);
+ t.same(sorted_ids, ids, 'Sorting went wrong.');
+ t.done();
+ });
+ },
+
+ "provided by name DESC": function (t) {
+ t.expect(2);
+
+ var sorted_ids = this.users.sort(function (a, b) {
+ a = a.p('name');
+ b = b.p('name');
+ return a < b ? 1 : (a > b ? -1 : 0);
+ }).map(function (user) {
+ return ''+user.id;
+ });
+
+ UserFindMockup.sort({
+ field: 'name',
+ direction: 'DESC'
+ }, this.userIds, function (err, ids) {
+ t.same(null, err, 'Sorting caused an error: '+err);
+ t.same(sorted_ids, ids, 'Sorting went wrong.');
+ t.done();
+ });
+ },
+
+ "provided by name LIMIT 2, 3": function (t) {
+ t.expect(2);
+
+ var sorted_ids = this.users.sort(function (a, b) {
+ a = a.p('name');
+ b = b.p('name');
+ return a > b ? 1 : (a < b ? -1 : 0);
+ }).slice(2, 5)
+ .map(function (user) {
+ return ''+user.id;
+ });
+
+ UserFindMockup.sort({
+ field: 'name',
+ limit: [2,3]
+ }, this.userIds, function (err, ids) {
+ t.same(null, err, 'Sorting caused an error: '+err);
+ t.same(sorted_ids, ids, 'Sorting went wrong.');
+ t.done();
+ });
+ },
+
+ "provided by number": function (t) {
t.expect(2);
- var sort_ids = [5,1,6,2,3];
- UserFindMockup.sort({}, sort_ids, function (err, ids) {
- t.same(null, err, 'Sorting without options caused an error');
- t.same(sort_ids.sort(), ids, 'Sorting incremental model without options did not sort them by id.');
+ var sorted_ids = this.users.sort(function (a, b) {
+ a = a.p('number');
+ b = b.p('number');
+ return a > b ? 1 : (a < b ? -1 : 0);
+ }).map(function (user) {
+ return ''+user.id;
+ });
+
+ UserFindMockup.sort({
+ field: 'number'
+ }, this.userIds, function (err, ids) {
+ t.same(null, err, 'Sorting caused an error: '+err);
+ t.same(sorted_ids, ids, 'Sorting went wrong.');
t.done();
});
- }*/
+ },
+
+ "provided by number DESC": function (t) {
+ t.expect(2);
+
+ var sorted_ids = this.users.sort(function (a, b) {
+ var id_sort = a.id < b.id ? 1 : -1;
+ a = a.p('number');
+ b = b.p('number');
+ return a < b ? 1 : (a > b ? -1 : id_sort);
+ }).map(function (user) {
+ return ''+user.id;
+ });
+
+ UserFindMockup.sort({
+ field: 'number',
+ direction: 'DESC'
+ }, this.userIds, function (err, ids) {
+ t.same(null, err, 'Sorting caused an error: '+err);
+ t.same(sorted_ids, ids, 'Sorting went wrong.');
+ t.done();
+ });
+ },
+
+ "provided by number LIMIT 3, 3": function (t) {
+ t.expect(2);
+
+ var sorted_ids = this.users.sort(function (a, b) {
+ a = a.p('number');
+ b = b.p('number');
+ return a > b ? 1 : (a < b ? -1 : 0);
+ }).slice(3, 6)
+ .map(function (user) {
+ return ''+user.id;
+ });
+
+ UserFindMockup.sort({
+ field: 'number',
+ limit: [3,3]
+ }, this.userIds, function (err, ids) {
+ t.same(null, err, 'Sorting caused an error: '+err);
+ t.same(sorted_ids, ids, 'Sorting went wrong.');
+ t.done();
+ });
+ }
}
};

0 comments on commit 54d24ec

Please sign in to comment.