UNPKG

53.4 kBJavaScriptView Raw
1"use strict";
2
3
4 //// Error replies.
5
6var ERROR = function ( message )
7{
8 this.getError = function () { return message; };
9 this.toString = function () { return "<ERROR<" + message + ">>"; };
10};
11
12var BAD_TYPE = new ERROR ( 'Operation against a key holding the wrong kind of value' );
13var BAD_KEY = new ERROR ( 'no such key' );
14var BAD_INT = new ERROR ( 'value is not an integer or out of range' );
15var BAD_FLOAT = new ERROR ( 'value is not a valid float' );
16var BAD_ARGS = new ERROR ( 'wrong number of arguments' );
17var BAD_SYNTAX = new ERROR ( 'syntax error' );
18var BAD_INDEX = new ERROR ( 'index out of range' );
19var BAD_SORT = new ERROR ( 'One or more scores can\'t be converted into double' );
20
21var BAD_BIT1 = new ERROR ( 'bit offset is not an integer or out of range' );
22var BAD_BIT2 = new ERROR ( 'bit is not an integer or out of range' );
23var BAD_SETEX = new ERROR ( 'invalid expire time in SETEX' );
24var BAD_ZUIS = new ERROR ( 'at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE' );
25
26
27
28 //// Status replies.
29
30var STATUS = function ( message )
31{
32 this.getStatus = function () { return message; };
33 this.toString = function () { return "<STATUS<" + message + ">>"; };
34};
35
36var OK = new STATUS ( 'OK' );
37var PONG = new STATUS ( 'PONG' );
38var NONE = new STATUS ( 'none' );
39
40
41
42 //// Redis types.
43
44var VALID_TYPE = function () {};
45var TYPE = function ( type, makePrimitive )
46{
47 var Constr = function ( value )
48 {
49 if ( !( this instanceof VALID_TYPE ) )
50 return new Constr ( value );
51 if ( !value )
52 value = makePrimitive ();
53
54 this.value = value;
55 };
56
57 Constr.getStatus = function () { return type; };
58 Constr.prototype = new VALID_TYPE;
59 Constr.prototype.toString = function () { return "<TYPE<" + type + ">>"; };
60 Constr.prototype.TYPE = Constr;
61 return Constr;
62};
63
64var EMPTY_STR = { toString : function () { return ""; }, length : 0, copy : function () {} };
65var STRING = TYPE ( "string", function () { return EMPTY_STR; } );
66var LIST = TYPE ( "list", function () { return []; } );
67var HASH = TYPE ( "hash", function () { return {}; } );
68var SET = TYPE ( "set", function () { return {}; } );
69var ZSET = TYPE ( "zset", function () { return {}; } );
70
71
72
73 //// Utils.
74
75var arr = function ( obj )
76{
77 var i, n = obj.length, out = [];
78 for ( i = 0; i < n; i ++ )
79 out [ i ] = obj [ i ];
80
81 return out;
82};
83
84var range = function ( min, max )
85{
86 var xlo, xhi;
87
88 if (( xlo = min.substr ( 0, 1 ) === '(' ))
89 min = str2float ( min.substr ( 1 ) );
90 else
91 min = str2float ( min );
92
93 if ( min instanceof ERROR )
94 return min;
95
96 if (( xhi = max.substr ( 0, 1 ) === '(' ))
97 max = str2float ( max.substr ( 1 ) );
98 else
99 max = str2float ( max );
100
101 if ( max instanceof ERROR )
102 return max;
103
104 return function ( num )
105 {
106 return !( ( xlo && num <= min ) || ( num < min ) || ( xhi && num >= max ) || ( num > max ) );
107 };
108};
109
110var slice = function ( arr, start, stop, asCount )
111{
112 start = str2int ( start );
113 stop = str2int ( stop );
114 if ( start instanceof ERROR ) return start;
115 if ( stop instanceof ERROR ) return stop;
116
117 if ( arr.slice )
118 {
119 var n = arr.length;
120 if ( asCount )
121 {
122 if ( start < 0 )
123 {
124 start = 0; // Redis is inconsistent about this, ZRANGEBYSCORE will return an empty multibulk on negative offset
125 stop = 0; // whilst SORT will return as if the offset was 0. Best to lint these away with client-side errors.
126 }
127 else if ( stop < 0 ) stop = n;
128 else stop += start;
129 }
130 else
131 {
132 if ( start < 0 ) start = n + start;
133 if ( stop < 0 ) stop = n + stop;
134 stop ++;
135 }
136
137 if ( start >= stop )
138 return [];
139 else
140 return arr.slice ( start < 0 ? 0 : start, stop > n ? n : stop );
141 }
142
143 else
144 return arr;
145};
146
147var str2float = function ( string )
148{
149 var value = Number ( string );
150 if ( typeof string !== 'string' ) throw new Error ( "WOOT! str2float: '" + string + "' not a string." );
151 if ( string === '+inf' ) value = Number.POSITIVE_INFINITY;
152 else if ( string === '-inf' ) value = Number.NEGATIVE_INFINITY;
153 else if ( !string || ( !value && value !== 0 ) ) return BAD_FLOAT;
154 return value;
155};
156
157var str2int = function ( string )
158{
159 var value = str2float ( string );
160 if ( value instanceof ERROR || value % 1 !== 0 ) return BAD_INT;
161 return value;
162};
163
164var pattern = function ( string )
165{
166 string = string.replace ( /[+{($^|.\\]/g, '\\' + '$0' );
167 string = string.replace ( /(^|[^\\])([*?])/g, '$1.$2' );
168 string = '^' + string + '$';
169
170 var pattern = new RegExp ( string );
171 return pattern.test.bind ( pattern );
172};
173
174
175
176 //// Keyspace and pubsub.
177
178exports.Backend = function ()
179{
180 var state,
181 dbs = {},
182 delrev = {},
183 rev = 0,
184
185 subs = [],
186 call = [],
187 tick = false;
188
189
190 // Select.
191 // Selected keyspace is NOT relevant to pubsub.
192 this.selectDB = function (id) {
193 if (typeof id !== "number" || id % 1 !== 0)
194 throw new Error("Invalid database id: " + id);
195
196 // Select or instantiate.
197 var db = dbs[id] || (dbs[id] = {});
198 state = db;
199 };
200
201 // Connections start in database 0.
202 this.selectDB(0);
203
204
205 //// Typed getKey.
206
207 this.getKey = function ( type, key, make )
208 {
209 var entry = state [ key ];
210
211 if ( type && !type.getStatus )
212 throw new Error ( "WOOT! type param for getKey is not a valid type." );
213 if ( key === undefined )
214 throw new Error ( "WOOT! key param for getKey is undefined." );
215
216 if ( !entry || entry.expire < Date.now () )
217 {
218 delete state [ key ];
219 delrev [ key ] = ++ rev;
220 entry = null;
221 }
222 else if ( !( entry.value instanceof VALID_TYPE ) )
223 throw new Error ( "WOOT! keyspace entry value is not a valid type." );
224
225 if ( type )
226 {
227 if ( entry && !( entry.value instanceof type ) )
228 return BAD_TYPE;
229 if ( !entry && make )
230 return new type;
231 }
232
233 return ( entry && entry.value ) || null;
234 };
235
236 this.setKey = function ( key, value )
237 {
238 if ( value )
239 {
240 if ( !( value instanceof VALID_TYPE ) )
241 throw new Error ( "WOOT! Value doesn't have a valid type." );
242
243 rev ++;
244 state [ key ] = { value : value };
245 state [ key ].rev = rev;
246 delete delrev [ key ];
247
248 this.pub ( this.UPDATE, key );
249
250 return 1;
251 }
252
253 else if ( state [ key ] )
254 {
255 rev ++;
256 delrev [ key ] = rev;
257 delete state [ key ];
258
259 return 1;
260 }
261
262 return 0;
263 };
264
265 this.upsetKey = function ( key, value )
266 {
267 if ( !value )
268 throw new Error ( "WOOT! Update key with a falsy value." );
269 if ( !( value instanceof VALID_TYPE ) )
270 throw new Error ( "WOOT! Value doesn't have a valid type." );
271
272 if ( state [ key ] && state [ key ].expire >= Date.now () )
273 {
274 if ( state [ key ].value !== value )
275 throw new Error ( "WOOT! Chaning value containers during upsetKey." );
276
277 rev ++;
278 state [ key ].value = value;
279 state [ key ].rev = rev;
280
281 this.pub ( this.UPDATE, key );
282 }
283
284 else
285 this.setKey ( key, value );
286 };
287
288 this.getExpire = function ( key )
289 {
290 var entry = state [ key ];
291
292 if ( !entry || entry.expire < Date.now () )
293 {
294 delete state [ key ];
295 return null;
296 }
297
298 return entry.expire;
299 };
300
301 this.setExpire = function ( key, expire )
302 {
303 var entry = state [ key ];
304
305 if ( !entry || entry.expire < Date.now () )
306 {
307 delete state [ key ];
308 return 0;
309 }
310
311 else if ( expire )
312 {
313 entry.expire = expire;
314 return 1;
315 }
316
317 else if ( entry.expire )
318 {
319 delete entry.expire;
320 return 1;
321 }
322
323 return 0;
324 };
325
326 this.getKeys = function ()
327 {
328 var keys = [],
329 key;
330
331 for ( key in state )
332 if ( this.getKey ( null, key ) )
333 keys.push ( key );
334
335 return keys;
336 };
337
338 this.renameKey = function ( keyA, keyB )
339 {
340 if ( !this.getKey ( null, keyA ) )
341 return false;
342
343 rev ++;
344 state [ keyB ] = state [ keyA ];
345 state [ keyB ].rev = rev ++;
346 delete state [ keyA ];
347
348 this.pub ( this.UPDATE, keyB );
349
350 return true;
351 };
352
353
354 //// Keyspace change event.
355
356 this.UPDATE = new STATUS ( "Key value updated." );
357
358
359 //// For implementing watch and stuff.
360
361 this.getRevision = function ( key )
362 {
363 this.getKey ( null, key );
364 return ( state [ key ] && state [ key ].rev ) || delrev [ key ] || 0;
365 };
366
367
368 //// Publish / subscribe backend.
369
370 this.pub = function ( channel, message )
371 {
372 if ( !channel && channel !== '' ) throw new Error ( "WOOT! Publishing to a falsy, non-string channel : [" + channel + '] ' + message );
373 if ( !message && message !== '' ) throw new Error ( "WOOT! Publishing a falsy, non-string message : [" + channel + '] ' + message );
374
375 var i, n = subs.length, sub, x = 0;
376 for ( i = 0; i < n; i ++ )
377 {
378 sub = subs [ i ];
379
380 if ( sub.channel === channel || ( sub.pattern !== null && sub.channel ( channel ) ) )
381 {
382 if ( sub.pattern !== null )
383 call.push ( sub.client.pushMessage.bind ( sub.client, 'pmessage', sub.pattern, channel, message ) );
384 else
385 call.push ( sub.client.pushMessage.bind ( sub.client, 'message', channel, message ) );
386
387 x ++;
388 if ( !tick )
389 {
390 tick = true;
391 process.nextTick ( function ()
392 {
393 var c, func;
394 tick = false;
395 c = call.splice ( 0, call.length );
396 while (( func = c.shift () )) func ();
397 });
398 }
399 }
400 }
401
402 return x;
403 };
404
405 //// p - true/false
406 //// channel - string
407 //// client { push ( pattern, channel, message ) }
408
409 this.sub = function ( p, channel, client )
410 {
411 if ( !channel && channel !== '' ) throw new Error ( "WOOT! Subscribing to a falsy, non-string channel : [" + channel + ']' );
412 if ( !client || !client.pushMessage ) throw new Error ( "WOOT! Subscribing an invalid client : " + client );
413 if ( typeof channel === 'function' ) throw new Error ( "WOOT! Subscribing to a function : " + channel );
414
415 var i, n = subs.length, sub, found = false;
416 for ( i = 0; i < n; i ++ )
417 {
418 sub = subs [ i ];
419 if ( sub.client === client && ( ( p && sub.pattern === channel ) || ( !p && sub.channel === channel ) ) )
420 {
421 found = true;
422 break;
423 }
424 }
425
426 var x = this.numSubs ( client );
427
428 if ( !found )
429 {
430 x ++;
431
432 subs.push ({ pattern : p ? channel : null, channel : p ? pattern ( channel ) : channel, client : client });
433 process.nextTick ( client.pushMessage.bind ( client, p ? 'psubscribe' : 'subscribe', channel, x ) );
434 }
435
436 return x;
437 };
438
439 this.unsub = function ( p, channel, client )
440 {
441 if ( !channel && channel !== '' && channel !== null ) throw new Error ( "WOOT! Unsubscribing from a falsy, non-string, non-null channel : [" + channel + ']' );
442 if ( !client || !client.pushMessage ) throw new Error ( "WOOT! Unsubscribing an invalid client : " + client );
443
444 var x = this.numSubs ( client );
445
446 var i, n = subs.length, sub;
447 for ( i = 0; i < n; i ++ )
448 {
449 sub = subs [ i ];
450 if ( sub.client !== client )
451 continue;
452
453 if ( ( p && sub.pattern !== null && ( channel === null || sub.pattern === channel ) ) || ( !p && sub.pattern === null && ( channel === null || sub.channel === channel ) ) )
454 {
455 x --;
456 subs.splice ( i, 1 );
457 process.nextTick ( client.pushMessage.bind ( client, p ? 'punsubscribe' : 'unsubscribe', p ? sub.pattern : sub.channel, x ) );
458 i --; n --;
459 }
460 }
461
462 return x;
463 };
464
465 this.numSubs = function ( client )
466 {
467 var i, n = subs.length, x = 0;
468 for ( i = 0; i < n; i ++ )
469 if ( subs [ i ].client === client )
470 x ++;
471
472 return x;
473 }
474
475};
476
477
478
479 //// Redis commands.
480
481exports.Backend.prototype =
482{
483
484
485 //// Keys.
486
487 DEL : function ()
488 {
489 var i, n = arguments.length, x = 0;
490 if ( !n ) return BAD_ARGS;
491 for ( i = 0; i < n; i ++ )
492 if ( this.setKey ( arguments [ i ], null ) ) x ++;
493
494 return x;
495 },
496
497 EXISTS : function ( key )
498 {
499 return this.getKey ( null, key ) ? 1 : 0;
500 },
501
502 PEXPIREAT : function ( key, time )
503 {
504 time = str2int ( time );
505 if ( time instanceof ERROR ) return time;
506 return this.setExpire ( key, time );
507 },
508
509 EXPIREAT : function ( key, time )
510 {
511 time = str2int ( time );
512 if ( time instanceof ERROR ) return time;
513 return this.setExpire ( key, time * 1000 );
514 },
515
516 PEXPIRE : function ( key, time )
517 {
518 time = str2int ( time );
519 if ( time instanceof ERROR ) return time;
520 return this.setExpire ( key, time + Date.now () );
521 },
522
523 EXPIRE : function ( key, time )
524 {
525 time = str2int ( time );
526 if ( time instanceof ERROR ) return time;
527 return this.setExpire ( key, time * 1000 + Date.now () );
528 },
529
530 PERSIST : function ( key )
531 {
532 return this.PEXPIREAT ( key, "0" );
533 },
534
535 PTTL : function ( key )
536 {
537 var ttl = this.getExpire ( key );
538 if ( ttl ) return ttl - Date.now ();
539 else return -1;
540 },
541
542 RANDOMKEY : function ( key )
543 {
544 var keys = this.getKeys (), n = keys && keys.length;
545 if ( n ) return keys [ Math.floor ( Math.random () * n ) ];
546 else return null;
547 },
548
549 RENAME : function ( key, newkey )
550 {
551 return this.renameKey ( key, newkey ) ? OK : BAD_KEY;
552 },
553
554 RENAMENX : function ( key, newkey )
555 {
556 if ( !this.EXISTS ( key ) ) return BAD_KEY;
557 if ( this.EXISTS ( newkey ) ) return 0;
558 if ( !this.renameKey ( key, newkey ) ) throw new Error ( "WOOT! Couldn't rename." );
559 return 1;
560 },
561
562 TTL : function ( key )
563 {
564 var ttl = this.getExpire ( key );
565 if ( ttl ) return Math.ceil ( ( ttl - Date.now () ) / 1000 );
566 else return -1;
567 },
568
569 TYPE : function ( key )
570 {
571 var K = this.getKey ( null, key );
572 return K ? K.TYPE : NONE;
573 },
574
575 KEYS : function ( pat )
576 {
577 var keys = this.getKeys ().filter ( pattern ( pat ) );
578 keys.sort ();
579 return keys;
580 },
581
582
583
584 //// String setters.
585
586 SET : function ( key, value )
587 {
588 var buf = new Buffer ( Buffer.byteLength ( value ) );
589 buf.write ( value );
590
591 this.setKey ( key, new STRING ( buf ) );
592 return OK;
593 },
594
595 sIncrBy : function ( parse, key, incr )
596 {
597 var K = this.getKey ( STRING, key, true );
598 if ( K instanceof ERROR ) return K;
599
600 incr = parse ( incr );
601 if ( incr instanceof ERROR ) return incr;
602 var value = parse ( K.value.toString () || "0" );
603 if ( value instanceof ERROR ) return value;
604
605 value = ( value + incr ).toString ();
606 var buf = new Buffer ( Buffer.byteLength ( value ) );
607 buf.write ( value );
608
609 K.value = value;
610 this.upsetKey ( key, K );
611 return value;
612 },
613
614 sFit : function ( key, length )
615 {
616 var K = this.getKey ( STRING, key, true );
617 if ( K instanceof ERROR ) return ERROR;
618
619 if ( K.value.length < length )
620 {
621 var buf = new Buffer ( length );
622 buf.fill ( 0 );
623
624 K.value.copy ( buf );
625 K.value = buf;
626 }
627
628 return K;
629 },
630
631 SETBIT : function ( key, offset, state )
632 {
633 var offset = str2int ( offset );
634 if ( !( offset > -1 ) ) return BAD_BIT1;
635 var state = str2int ( state );
636 if ( !( state === 0 || state === 1 ) ) return BAD_BIT2;
637
638 var x = Math.floor ( offset / 8 );
639 var K = this.sFit ( key, x + 1 );
640 if ( K instanceof ERROR ) return K;
641
642 var mask = 1 << ( 7 - ( offset % 8 ) );
643 var current = K.value [ x ];
644 var old = current & mask ? 1 : 0;
645
646 if ( state && !old )
647 K.value [ x ] = current | mask;
648 else if ( !state && old )
649 K.value [ x ] = current & ~mask;
650
651 this.upsetKey ( key, K );
652 return old;
653 },
654
655 SETRANGE : function ( key, offset, value )
656 {
657 var offset = str2int ( offset );
658 if ( !( offset > -1 ) ) return BAD_BIT1;
659
660 var K = this.sFit ( key, offset + Buffer.byteLength ( value ) );
661 K.value.write ( value, offset );
662
663 this.upsetKey ( key, K );
664 return this.STRLEN ( key );
665 },
666
667 //// String getters.
668
669 GET : function ( key )
670 {
671 var K = this.getKey ( STRING, key );
672 if ( K instanceof ERROR ) return K;
673 return K ? K.value.toString () : null;
674 },
675
676 STRLEN : function ( key )
677 {
678 var K = this.getKey ( STRING, key );
679 if ( K instanceof ERROR ) return ERROR;
680 return K ? K.value.length : 0;
681 },
682
683 GETBIT : function ( key, offset )
684 {
685 var K = this.getKey ( STRING, key );
686 if ( K instanceof ERROR ) return ERROR;
687
688 var offset = str2int ( offset );
689 if ( !( offset > -1 ) ) return BAD_BIT1;
690 var x = Math.floor ( offset / 8 );
691 if ( !K || K.length < x + 1 ) return 0;
692
693 var mask = 1 << ( 7 - ( offset % 8 ) );
694 return ( K.value [ x ] & mask ) ? 1 : 0;
695 },
696
697 GETRANGE : function ( key, start, stop )
698 {
699 var K = this.getKey ( STRING, key );
700 if ( K instanceof ERROR ) return ERROR;
701 if ( !K ) return "";
702
703 var out = slice ( K.value, start, stop );
704 if ( out instanceof ERROR ) return out;
705 return out.toString ();
706 },
707
708 //// String ops.
709
710 APPEND : function ( key, value )
711 {
712 var strlen = this.STRLEN ( key );
713 if ( strlen instanceof ERROR ) return strlen;
714 return this.SETRANGE ( key, strlen.toString (), value );
715 },
716
717 DECR : function ( key )
718 {
719 return this.DECRBY ( key, "1" );
720 },
721
722 DECRBY : function ( key, decr )
723 {
724 var value = str2int ( decr );
725 if ( value instanceof ERROR ) return value;
726 return this.INCRBY ( key, ( - value ).toString () );
727 },
728
729 GETSET : function ( key, value )
730 {
731 var old = this.GET ( key );
732 if ( old instanceof ERROR ) return old;
733 this.SET ( key, value );
734 return old;
735 },
736
737 INCR : function ( key )
738 {
739 return this.INCRBY ( key, "1" );
740 },
741
742 INCRBY : function ( key, incr )
743 {
744 return this.sIncrBy ( str2int, key, incr );
745 },
746
747 INCRBYFLOAT : function ( key, incr )
748 {
749 return this.sIncrBy ( str2float, key, incr );
750 },
751
752 MGET : function ()
753 {
754 var out = [], i, n = arguments.length;
755 if ( !n ) return BAD_ARGS;
756
757 for ( i = 0; i < n; i ++ )
758 {
759 var value = this.GET ( arguments [ i ] );
760 out [ i ] = value instanceof ERROR ? null : value;
761 }
762
763 return out;
764 },
765
766 MSET : function ()
767 {
768 var key, value, i, n = arguments.length;
769 if ( !n || n % 2 ) return BAD_ARGS;
770
771 for ( i = 0; i < n; i += 2 )
772 {
773 key = arguments [ i ];
774 value = arguments [ i + 1 ];
775 this.SET ( key, value );
776 }
777
778 return OK;
779 },
780
781 MSETNX : function ()
782 {
783 var i, n = arguments.length;
784 for ( i = 0; i < n; i += 2 )
785 if ( this.EXISTS ( arguments [ i ] ) ) return 0;
786
787 this.MSET.apply ( this, arguments );
788 return 1;
789 },
790
791 PSETEX : function ( key, timediff, value )
792 {
793 if ( !( str2int ( timediff ) > 0 ) )
794 return BAD_SETEX;
795
796 this.SET ( key, value );
797 this.PEXPIRE ( key, timediff );
798 return OK;
799 },
800
801 SETEX : function ( key, timediff, value )
802 {
803 if ( !( str2int ( timediff ) > 0 ) )
804 return BAD_SETEX;
805
806 this.SET ( key, value );
807 this.EXPIRE ( key, timediff );
808 return OK;
809 },
810
811 SETNX : function ( key, value )
812 {
813 if ( this.EXISTS ( key ) ) return 0;
814 this.SET ( key, value );
815 return 1;
816 },
817
818
819
820 //// Lists, non-blocking.
821
822 lStore : function ( key, values )
823 {
824 //// Only used in SORT.
825
826 if ( values.length )
827 return this.setKey ( key, new LIST ( values ) );
828 else
829 return this.setKey ( key, null );
830 },
831
832 LINDEX : function ( key, index )
833 {
834 var K = this.getKey ( LIST, key );
835 if ( K instanceof ERROR ) return K;
836
837 index = str2int ( index );
838 if ( index instanceof ERROR )
839 return index;
840
841 return ( K && K.value [ index < 0 ? K.value.length + index : index ] ) || null;
842 },
843
844 upsetList : function ( key, K )
845 {
846 if ( K.value.length ) this.upsetKey ( key, K );
847 else this.setKey ( key, null );
848 },
849
850 LINSERT : function ( key, relpos, pivot, value )
851 {
852 var K = this.getKey ( LIST, key ), x;
853 if ( K instanceof ERROR ) return K;
854
855 relpos = relpos.toUpperCase ();
856 if ( relpos !== 'BEFORE' && relpos !== 'AFTER' ) return BAD_SYNTAX;
857 if ( !K ) return 0;
858 if ( ( x = K.value.indexOf ( pivot ) ) < 0 ) return 0;
859
860 K.value.splice ( relpos === 'AFTER' ? x + 1 : x, 0, value );
861 this.upsetList ( key, K );
862 return 1;
863 },
864
865 LLEN : function ( key )
866 {
867 var K = this.getKey ( LIST, key );
868 if ( K instanceof ERROR ) return K;
869
870 return ( K && K.value && K.value.length ) || 0;
871 },
872
873 lPopMany : function ( left, keys )
874 {
875 var K = [], value, i, n = keys.length;
876 if ( !n ) return BAD_ARGS;
877 for ( i = 0; i < n; i ++ )
878 {
879 K [ i ] = this.getKey ( LIST, keys [ i ] );
880 if ( K [ i ] instanceof ERROR ) return K [ i ];
881 }
882 for ( i = 0; i < n; i ++ )
883 if ( K [ i ] && K [ i ].value && K [ i ].value.length )
884 {
885 value = left ? K [ i ].value.shift () : K [ i ].value.pop ();
886 this.upsetList ( keys [ i ], K [ i ] );
887 return [ keys [ i ], value ];
888 }
889
890 return null;
891 },
892
893 lPop : function ( left, key )
894 {
895 var out = this.lPopMany ( left, [ key ] );
896 return out && out.length ? out [ 1 ] : out;
897 },
898
899 LPOP : function ( key )
900 {
901 return this.lPop ( true, key );
902 },
903
904 RPOP : function ( key )
905 {
906 return this.lPop ( false, key );
907 },
908
909 lPush : function ( left, make, args )
910 {
911 var i, n = args.length, key = args [ 0 ];
912 var K = this.getKey ( LIST, key, make );
913 if ( K instanceof ERROR ) return K;
914 if ( n < 2 ) return BAD_ARGS;
915 if ( !K ) return 0;
916
917 if ( left ) for ( i = 1; i < n; i ++ )
918 K.value.unshift ( args [ i ] );
919 else
920 K.value.push.apply ( K.value, args.slice ( 1 ) );
921
922 this.upsetList ( key, K );
923 return K.value.length;
924 },
925
926 LPUSH : function ()
927 {
928 return this.lPush ( true, true, arr ( arguments ) );
929 },
930
931 LPUSHX : function ()
932 {
933 return this.lPush ( true, false, arr ( arguments ) );
934 },
935
936 RPUSH : function ()
937 {
938 return this.lPush ( false, true, arr ( arguments ) );
939 },
940
941 RPUSHX : function ()
942 {
943 return this.lPush ( false, false, arr ( arguments ) );
944 },
945
946 RPOPLPUSH : function ( source, destination )
947 {
948 var dest = this.getKey ( LIST, destination );
949 if ( dest && dest instanceof ERROR ) return dest;
950 var value = this.RPOP ( source );
951 if ( value === null || value instanceof ERROR ) return value;
952
953 var len = this.LPUSH ( destination, value );
954 if ( !len || len instanceof ERROR ) throw new Error ( "WOOT! LPUSH failed in RPOPLPUSH." );
955
956 return value;
957 },
958
959 LRANGE : function ( key, start, stop )
960 {
961 var K = this.getKey ( LIST, key );
962 if ( K instanceof ERROR ) return K;
963 if ( !K ) return [];
964
965 return slice ( K.value, start, stop );
966 },
967
968 LREM : function ( key, count, value )
969 {
970 var K = this.getKey ( LIST, key );
971 if ( K instanceof ERROR ) return K;
972 var count = str2int ( count );
973 if ( count instanceof ERROR ) return count;
974 if( !K ) return 0;
975
976 var i, n = K.value.length, x = 0;
977 if ( count < 0 )
978 {
979 count *= -1;
980 for ( i = n - 1; i >= 0; i -- )
981 if ( K.value [ i ] === value && (!count || x < count) )
982 {
983 K.value.splice ( i, 1 );
984 x ++;
985 }
986 }
987 else for ( i = 0; i < n; i ++ )
988 if ( K.value [ i ] === value && (!count || x < count) )
989 {
990 K.value.splice ( i, 1 );
991 i --; n --; x ++;
992 }
993
994 if ( x > 0 ) this.upsetList ( key, K );
995 return x;
996 },
997
998 LSET : function ( key, index, value )
999 {
1000 var K = this.getKey ( LIST, key );
1001 if ( !K ) return BAD_KEY;
1002 if ( K instanceof ERROR ) return K;
1003 var index = str2int ( index );
1004 if ( index instanceof ERROR ) return index;
1005 if ( index < 0 || index > K.value.length ) return BAD_INDEX;
1006
1007 K.value [ index ] = value;
1008 this.upsetList ( key, K );
1009 return OK;
1010 },
1011
1012 LTRIM : function ( key, start, stop )
1013 {
1014 var range = this.LRANGE ( key, start, stop );
1015 if ( !range.join )
1016 return range;
1017
1018 var K = this.getKey ( LIST, key );
1019 if ( K )
1020 {
1021 K.value = range;
1022 this.upsetList ( key, K );
1023 }
1024
1025 return OK;
1026 },
1027
1028 //// Blocking list commands.
1029 //// The blocking part happens at the connection level,
1030 //// where in case the response is null the connection subscribes to the keyspace change event for the key and waits to retry.
1031
1032 //// So this only validates the parameter.
1033
1034 bArgs : function ( args )
1035 {
1036 args = arr ( args );
1037 var timeout = str2int ( args.pop () || "FAIL" );
1038 if ( timeout instanceof ERROR ) return timeout;
1039 if ( timeout < 0 ) return BAD_INT;
1040 return args;
1041 },
1042
1043 BLPOP : function ()
1044 {
1045 var a = this.bArgs ( arguments );
1046 if ( a instanceof ERROR ) return a;
1047 return this.lPopMany ( true, a );
1048 },
1049
1050 BRPOP : function ()
1051 {
1052 var a = this.bArgs ( arguments );
1053 if ( a instanceof ERROR ) return a;
1054 return this.lPopMany ( false, a );
1055 },
1056
1057 BRPOPLPUSH : function ()
1058 {
1059 var a = this.bArgs ( arguments );
1060 if ( a instanceof ERROR ) return a;
1061 return this.RPOPLPUSH.apply ( this, a );
1062 },
1063
1064
1065
1066 //// Hashes.
1067
1068 structPut : function ( type, validate, revArgs, args )
1069 {
1070 var key = args [ 0 ], i, n = args.length, x = 0;
1071
1072 if ( n < 3 || ( ( n - 1 ) % 2 ) ) return BAD_ARGS;
1073 var K = this.getKey ( type, key, true );
1074 if ( K instanceof ERROR ) return K;
1075
1076 for ( i = 1; i < n; i += 2 )
1077 {
1078 var member = args [ revArgs ? i + 1 : i ],
1079 value = validate ( args [ revArgs ? i : i + 1 ] );
1080 if ( value instanceof ERROR ) return value;
1081 if ( !( member in K.value ) ) x ++;
1082 K.value [ member ] = value;
1083 }
1084
1085 if ( x ) this.upsetKey ( key, K );
1086 return x;
1087 },
1088
1089 structDel : function ( type, args )
1090 {
1091 var key = args [ 0 ],
1092 i, n = args.length,
1093 x;
1094
1095 if ( n < 2 ) return BAD_ARGS;
1096 var K = this.getKey ( type, key );
1097 if ( K instanceof ERROR ) return K;
1098 if ( !K ) return 0;
1099
1100 x = 0;
1101 for ( i = 1; i < n; i ++ )
1102 {
1103 if ( args [ i ] in K.value ) x ++;
1104 delete K.value [ args [ i ] ];
1105 }
1106
1107 //// Remove the set if empty, upset otherwise.
1108
1109 var member;
1110 for ( member in K.value )
1111 {
1112 if ( x ) this.upsetKey ( key, K );
1113 return x;
1114 }
1115
1116 this.setKey ( key, null );
1117 return x;
1118 },
1119
1120 structGet : function ( type, key, member )
1121 {
1122 var K = this.getKey ( type, key );
1123 if ( K instanceof ERROR ) return K;
1124 if ( !K || !( member in K.value ) ) return null;
1125 return K.value [ member ];
1126 },
1127
1128 HDEL : function ()
1129 {
1130 return this.structDel ( HASH, arguments );
1131 },
1132
1133 HEXISTS : function ( key, field )
1134 {
1135 var fields = this.HKEYS ( key );
1136 if ( fields.indexOf ) return fields.indexOf ( field ) >= 0 ? 1 : 0;
1137 return fields;
1138 },
1139
1140 HGET : function ( key, field )
1141 {
1142 return this.structGet ( HASH, key, field );
1143 },
1144
1145 HGETALL : function ( key )
1146 {
1147 var fields = this.HKEYS ( key );
1148 var i, n = fields.length, out = [];
1149 for ( i = 0; i < n; i ++ )
1150 out.push ( fields [ i ], this.HGET ( key, fields [ i ] ) );
1151
1152 return out;
1153 },
1154
1155 hIncrBy : function ( parse, key, field, incr )
1156 {
1157 var K = this.getKey ( HASH, key, true );
1158 if ( K instanceof ERROR ) return K;
1159
1160 incr = parse ( incr );
1161 if ( incr instanceof ERROR ) return incr;
1162 var value = parse ( K.value [ field ] || "0" );
1163 if ( value instanceof ERROR ) return value;
1164
1165 K.value [ field ] = ( value + incr ).toString ();
1166 this.upsetKey ( key, K );
1167 return K.value [ field ];
1168 },
1169
1170 HINCRBY : function ( key, field, incr )
1171 {
1172 return this.hIncrBy ( str2int, key, field, incr );
1173 },
1174
1175 HINCRBYFLOAT : function ( key, field, incr )
1176 {
1177 return this.hIncrBy ( str2float, key, field, incr );
1178 },
1179
1180 HKEYS : function ( key )
1181 {
1182 var K = this.getKey ( HASH, key );
1183 if ( K instanceof ERROR ) return K;
1184
1185 var fields = [], field;
1186 if ( K ) for ( field in K.value )
1187 fields.push ( field );
1188
1189 fields.sort ();
1190 return fields;
1191 },
1192
1193 HLEN : function ( key )
1194 {
1195 var fields = this.HKEYS ( key );
1196 if ( fields.indexOf ) return fields.length;
1197 return fields;
1198 },
1199
1200 HMGET : function ()
1201 {
1202 var K = this.getKey ( HASH, arguments [ 0 ] );
1203 if ( K instanceof ERROR ) return K;
1204
1205 var i, n = arguments.length, values = [];
1206 if ( n < 2 ) return BAD_ARGS;
1207 for ( i = 1; i < n; i ++ )
1208 values.push ( K && arguments [ i ] in K.value ? K.value [ arguments [ i ] ] : null );
1209
1210 return values;
1211 },
1212
1213 HMSET : function ()
1214 {
1215 var x = this.structPut ( HASH, String, false, arguments );
1216 return x instanceof ERROR ? x : OK;
1217 },
1218
1219 HSET : function ( key, field, value )
1220 {
1221 return this.structPut ( HASH, String, false, arguments );
1222 },
1223
1224 HSETNX : function ( key, field, value )
1225 {
1226 var exists = this.HEXISTS ( key, field );
1227 if ( exists instanceof ERROR ) return exists;
1228 if ( exists ) return 0;
1229 return this.HSET ( key, field, value );
1230 },
1231
1232 HVALS : function ( key )
1233 {
1234 var out = this.HKEYS ( key ), self = this;
1235 if ( out instanceof ERROR ) return out;
1236
1237 if ( out.map )
1238 out = out.map ( function ( field )
1239 {
1240 return self.HGET ( key, field );
1241 });
1242
1243 out.sort ();
1244 return out;
1245 },
1246
1247
1248
1249 //// Sets.
1250
1251 SADD : function ()
1252 {
1253 var key = arguments [ 0 ],
1254 i, n = arguments.length,
1255 x = 0;
1256
1257 if ( n < 2 ) return BAD_ARGS;
1258 var K = this.getKey ( SET, key, true );
1259 if ( K instanceof ERROR ) return K;
1260
1261 for ( i = 1; i < n; i ++ )
1262 if ( !K.value [ arguments [ i ] ] )
1263 {
1264 K.value [ arguments [ i ] ] = true;
1265 x ++;
1266 }
1267
1268 if ( x ) this.upsetKey ( key, K );
1269 return x;
1270 },
1271
1272 SCARD : function ( key )
1273 {
1274 var members = this.SMEMBERS ( key );
1275 return members.join ? members.length : members;
1276 },
1277
1278 SISMEMBER : function ( key, member )
1279 {
1280 var members = this.SMEMBERS ( key );
1281 return members.indexOf ? members.indexOf ( member ) >= 0 ? 1 : 0 : members;
1282 },
1283
1284 SMEMBERS : function ( key )
1285 {
1286 return this.SUNION ( key );
1287 },
1288
1289 SPOP : function ( key )
1290 {
1291 var member = this.SRANDMEMBER ( key );
1292 if ( typeof member === 'string' )
1293 this.SREM ( key, member );
1294
1295 return member;
1296 },
1297
1298 SRANDMEMBER : function ( key )
1299 {
1300 var members = this.SMEMBERS ( key ),
1301 n = members.length, member;
1302 if ( !n )
1303 return n === 0 ? null : members;
1304
1305 member = members [ Math.floor ( Math.random () * n ) ];
1306 return member;
1307 },
1308
1309 SREM : function ()
1310 {
1311 return this.structDel ( SET, arguments );
1312 },
1313
1314 //// Set multikey ops.
1315 //// Set members come out sorted lexicographically to facilitate testing.
1316
1317 SMOVE : function ( source, destination, member )
1318 {
1319 var removed = this.SREM ( source, member );
1320 if ( removed === 1 )
1321 return this.SADD ( destination, member );
1322 else
1323 return removed;
1324 },
1325
1326 SUNION : function ()
1327 {
1328 var i, n = arguments.length, out = [];
1329 if ( !n ) return BAD_ARGS;
1330
1331 for ( i = 0; i < n; i ++ )
1332 {
1333 var K = this.getKey ( SET, arguments [ i ] );
1334 if ( K instanceof ERROR ) return K;
1335
1336 var member;
1337 if ( K ) for ( member in K.value )
1338 if ( out.indexOf ( member ) < 0 )
1339 out.push ( member );
1340 }
1341
1342 out.sort ();
1343 return out;
1344 },
1345
1346 sCombine : function ( diff, args )
1347 {
1348 var i, n = args.length;
1349 if ( !n )
1350 return BAD_ARGS;
1351
1352 var out = this.SUNION ( args [ 0 ] );
1353 if ( out instanceof ERROR ) return out;
1354 for ( i = 1; i < n; i ++ )
1355 {
1356 var K = this.getKey ( SET, args [ i ] );
1357 if ( K instanceof ERROR ) return K;
1358
1359 var j, m = out.length;
1360 if ( K ) for ( j = 0; j < m; j ++ )
1361 if ( ( diff && K.value [ out [ j ] ] ) || ( !diff && !K.value [ out [ j ] ] ) )
1362 {
1363 out.splice ( j, 1 );
1364 j --;
1365 m --;
1366 }
1367 }
1368
1369 out.sort ();
1370 return out;
1371 },
1372
1373 SDIFF : function ()
1374 {
1375 return this.sCombine ( true, arr ( arguments ) );
1376 },
1377
1378 SINTER : function ()
1379 {
1380 return this.sCombine ( false, arr ( arguments ) );
1381 },
1382
1383 sStore : function ( key, members )
1384 {
1385 var K, i, n = members.length;
1386 if ( n ) K = new SET ({});
1387 for ( i = 0; i < n; i ++ )
1388 K.value [ members [ i ] ] = true;
1389
1390 return this.setKey ( key, K || null );
1391 },
1392
1393 sStoreOp : function ( op, args )
1394 {
1395 if ( !args.length )
1396 return BAD_ARGS;
1397
1398 var key = args.shift (),
1399 members = op.apply ( this, args );
1400
1401 if ( members.join )
1402 {
1403 this.sStore ( key, members );
1404 return members.length;
1405 }
1406
1407 return members;
1408 },
1409
1410 SDIFFSTORE : function ()
1411 {
1412 return this.sStoreOp ( this.SDIFF, arr ( arguments ) );
1413 },
1414
1415 SINTERSTORE : function ()
1416 {
1417 return this.sStoreOp ( this.SINTER, arr ( arguments ) );
1418 },
1419
1420 SUNIONSTORE : function ()
1421 {
1422 return this.sStoreOp ( this.SUNION, arr ( arguments ) );
1423 },
1424
1425
1426
1427 //// Sorted sets.
1428
1429 ZADD : function ()
1430 {
1431 return this.structPut ( ZSET, str2float, true, arguments );
1432 },
1433
1434 ZCARD : function ( key )
1435 {
1436 return this.ZCOUNT ( key, '-inf', '+inf' );
1437 },
1438
1439 ZCOUNT : function ( key, min, max )
1440 {
1441 var members = this.ZRANGEBYSCORE ( key, min, max );
1442 return members.join ? members.length : members;
1443 },
1444
1445 ZINCRBY : function ( key, incr, member )
1446 {
1447 var K = this.getKey ( ZSET, key, true );
1448 if ( K instanceof ERROR ) return K;
1449
1450 var value = str2float ( incr );
1451 if ( value instanceof ERROR ) return value;
1452 value += Number ( K.value [ member ] || 0 );
1453
1454 K.value [ member ] = value;
1455 this.upsetKey ( key, K );
1456 return value;
1457 },
1458
1459 //// Sort set queries.
1460
1461 zSort : function ( rev, key, min, max )
1462 {
1463 var K = this.getKey ( ZSET, key );
1464 if ( K instanceof ERROR ) return K;
1465 if ( !K ) return [];
1466
1467 var R = range ( min, max ), member, out = [];
1468 if ( R instanceof ERROR ) return R;
1469
1470 for ( member in K.value )
1471 if ( R ( K.value [ member ] ) )
1472 out.push ({ member : member, score : K.value [ member ] });
1473
1474 //// First by score,
1475 //// then in lexicographic order.
1476
1477 if ( rev )
1478 out.sort ( function ( b, a )
1479 {
1480 return ( a.score - b.score ) || ( a.member < b.member ? -1 : 1 );
1481 });
1482
1483 else
1484 out.sort ( function ( a, b )
1485 {
1486 return ( a.score - b.score ) || ( a.member < b.member ? -1 : 1 );
1487 });
1488
1489 return out;
1490 },
1491
1492 zUnwrap : function ( range, scores )
1493 {
1494 var i, n = range.length, out = n ? [] : range;
1495 if ( n )
1496 for ( i = 0; i < n; i ++ )
1497 {
1498 out.push ( range [ i ].member );
1499 if ( scores )
1500 out.push ( range [ i ].score );
1501 }
1502
1503 return out;
1504 },
1505
1506 zGetRange : function ( rev, args )
1507 {
1508 var key = args [ 0 ], start = args [ 1 ], stop = args [ 2 ], scores = args [ 3 ];
1509
1510 if ( args.length < 3 || args.length > 4 )
1511 return BAD_ARGS;
1512 if ( scores && scores.toUpperCase () !== 'WITHSCORES' )
1513 return BAD_SYNTAX;
1514
1515 var range = this.zSort ( rev, key, '-inf', '+inf' );
1516
1517 return this.zUnwrap ( slice ( range, start, stop ), scores );
1518 },
1519
1520 zGetRangeByScore : function ( rev, args )
1521 {
1522 var key = args [ 0 ], min = args [ rev ? 2 : 1 ], max = args [ rev ? 1 : 2 ],
1523 scores, limit, offset, count;
1524
1525 if ( args.length < 3 )
1526 return BAD_ARGS;
1527
1528 else if ( args.length === 4 )
1529 scores = args [ 3 ];
1530
1531 else if ( args.length === 6 )
1532 {
1533 limit = args [ 3 ];
1534 offset = args [ 4 ];
1535 count = args [ 5 ];
1536 }
1537
1538 else if ( args.length === 7 )
1539 {
1540 scores = args [ 3 ];
1541 limit = args [ 4 ];
1542 offset = args [ 5 ];
1543 count = args [ 6 ];
1544 }
1545
1546 if ( scores && scores.toUpperCase () !== 'WITHSCORES' )
1547 return BAD_SYNTAX;
1548 if ( limit && limit.toUpperCase () !== 'LIMIT' )
1549 return BAD_SYNTAX;
1550
1551 var range = this.zSort ( rev, key, min, max );
1552 if ( limit )
1553 range = slice ( range, offset, count, true );
1554
1555 return this.zUnwrap ( range, scores );
1556 },
1557
1558 ZRANGE : function ()
1559 {
1560 return this.zGetRange ( false, arr ( arguments ) );
1561 },
1562
1563 ZREVRANGE : function ()
1564 {
1565 return this.zGetRange ( true, arr ( arguments ) );
1566 },
1567
1568 ZRANGEBYSCORE : function ()
1569 {
1570 return this.zGetRangeByScore ( false, arr ( arguments ) );
1571 },
1572
1573 ZREVRANGEBYSCORE : function ()
1574 {
1575 return this.zGetRangeByScore ( true, arr ( arguments ) );
1576 },
1577
1578 ZRANK : function ( key, member )
1579 {
1580 var out = this.zSort ( false, key, '-inf', '+inf' ),
1581 i, n = out.length;
1582
1583 for ( i = 0; i < n; i ++ )
1584 if ( out [ i ].member === member )
1585 return i;
1586
1587 return n || n === 0 ? null : out;
1588 },
1589
1590 ZREVRANK : function ( key, member )
1591 {
1592 var out = this.zSort ( false, key, '-inf', '+inf' ),
1593 i, n = out.length;
1594
1595 for ( i = n - 1; i >= 0; i -- )
1596 if ( out [ i ].member === member )
1597 return n - i - 1;
1598
1599 return n || n === 0 ? null : out;
1600 },
1601
1602 ZSCORE : function ( key, member )
1603 {
1604 return this.structGet ( ZSET, key, member );
1605 },
1606
1607 ZREM : function ()
1608 {
1609 return this.structDel ( ZSET, arguments );
1610 },
1611
1612 ZREMRANGEBYRANK : function ( key, start, stop )
1613 {
1614 var members = this.ZRANGE ( key, start, stop ), n = members.length;
1615 if ( n )
1616 n = this.ZREM.apply ( this, [ key ].concat ( members ) );
1617
1618 return n || n === 0 ? n : members;
1619 },
1620
1621 ZREMRANGEBYSCORE : function ( key, min, max )
1622 {
1623 var members = this.ZRANGEBYSCORE ( key, min, max ), n = members.length;
1624 if ( n )
1625 n = this.ZREM.apply ( this, [ key ].concat ( members ) );
1626
1627 return n || n === 0 ? n : members;
1628 },
1629
1630 //// Sorted set multikey ops.
1631
1632 zOpStore : function ( union, key, keys, weights, aggregate )
1633 {
1634 var K = this.getKey ( ZSET, keys [ 0 ] );
1635 if ( K instanceof ERROR ) return K;
1636
1637 var out = {}, member, x = 0, weight = ( weights === null ? 1 : weights [ 0 ] );
1638 if ( K ) for ( member in K.value )
1639 {
1640 out [ member ] = K.value [ member ] * weight;
1641 x ++;
1642 }
1643
1644 var i, n = keys.length;
1645 for ( i = 1; i < n; i ++ )
1646 {
1647 K = this.getKey ( ZSET, keys [ i ] );
1648 if ( K instanceof ERROR ) return K;
1649
1650 weight = ( weights !== null ? weights [ i ] : 1 );
1651 if ( !union )
1652 {
1653 if ( !K )
1654 {
1655 out = {};
1656 x = 0;
1657 }
1658
1659 else for ( member in out ) if ( !( member in K.value ) )
1660 {
1661 delete out [ member ];
1662 x --;
1663 }
1664 }
1665
1666 if ( K ) for ( member in K.value )
1667 if ( union || member in out )
1668 {
1669 if ( !( member in out ) )
1670 {
1671 x ++;
1672 out [ member ] = K.value [ member ] * weight;
1673 }
1674
1675 else
1676 out [ member ] = aggregate ( K.value [ member ] * weight, out [ member ] );
1677 }
1678 }
1679
1680 if ( x ) this.setKey ( key, new ZSET ( out ) );
1681 return x;
1682 },
1683
1684 zsum : function ( a, b ) { return a + b; },
1685 zmin : function ( a, b ) { return a < b ? a : b; },
1686 zmax : function ( a, b ) { return a > b ? a : b; },
1687
1688 zParseOpStore : function ( union, args )
1689 {
1690 var key = args [ 0 ], N = str2int ( args [ 1 ] );
1691 if ( N instanceof ERROR ) return N;
1692 if ( N < 1 ) return BAD_ZUIS;
1693 if ( args.length < N + 2 ) return BAD_ARGS;
1694
1695 var keys = args.splice ( 2, N ), weigh = ( args [ 2 ] || '' ).toUpperCase () === 'WEIGHTS', weights;
1696 if ( weigh )
1697 {
1698 if ( args.length < N + 3 ) return BAD_ARGS;
1699 weights = args.splice ( 3, N );
1700 if ( weights.map ( str2float ).some ( function ( w ) { return w instanceof ERROR; } ) ) return BAD_FLOAT;
1701 args.splice ( 2, 1 );
1702 }
1703
1704 var aggregate = ( args [ 2 ] || '' ).toUpperCase () === 'AGGREGATE' ? ( args [ 3 ] || '' ).toLowerCase () : null;
1705 if ( aggregate )
1706 {
1707 if ( aggregate !== 'sum' && aggregate !== 'min' && aggregate !== 'max' ) return BAD_SYNTAX;
1708 aggregate = this [ 'z' + aggregate ];
1709 if ( typeof aggregate !== 'function' )
1710 throw new Error ( "WOOT! Can't find the aggregate function for " + args [ 3 ] );
1711 args.splice ( 2, 2 );
1712 }
1713
1714 if ( args.length !== 2 )
1715 return BAD_ARGS;
1716
1717 return this.zOpStore ( union, key, keys, weights || null, aggregate || this.zsum );
1718 },
1719
1720 ZINTERSTORE : function ()
1721 {
1722 return this.zParseOpStore ( false, arr ( arguments ) );
1723 },
1724
1725 ZUNIONSTORE : function ()
1726 {
1727 return this.zParseOpStore ( true, arr ( arguments ) );
1728 },
1729
1730
1731
1732 //// Sort.
1733
1734 sortSelect : function ( pat, key )
1735 {
1736 var select = /^((?:.)*?)(?:->(.*))?$/.exec ( pat ),
1737 key = select [ 1 ].replace ( /\*/, key ), // no g flag, so only first occurence is replaced
1738 field = select [ 2 ];
1739
1740 if ( typeof field === 'string' )
1741 return this.HGET ( key, field );
1742 else
1743 return this.GET ( key );
1744 },
1745
1746 SORT : function ()
1747 {
1748 var self = this, args = arr ( arguments ), n = args.length;
1749 if ( !n ) return new BAD_ARGS;
1750
1751 //// Parse.
1752 //// SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]
1753
1754 var key = args.shift (),
1755 by, limit, offset, count, get, pat, desc, alpha, store;
1756
1757 if ( /^by$/i.test ( args [ 0 ] ) )
1758 {
1759 by = args [ 1 ];
1760 if ( typeof by !== 'string' ) return BAD_SYNTAX;
1761 args.splice ( 0, 2 );
1762 }
1763
1764 if ( /^limit$/i.test ( args [ 0 ] ) )
1765 {
1766 limit = true;
1767 if ( args.length < 3 ) return BAD_ARGS;
1768 offset = args [ 1 ]; // integer validation happens in slice()
1769 count = args [ 2 ];
1770 args.splice ( 0, 3 );
1771 }
1772
1773 while ( /^get$/i.test ( args [ 0 ] ) )
1774 {
1775 pat = args [ 1 ];
1776 if ( typeof pat !== 'string' ) return BAD_SYNTAX;
1777 if ( !get ) get = [];
1778 get.push ( pat );
1779 args.splice ( 0, 2 );
1780 }
1781
1782 if ( /^asc|desc$/i.test ( args [ 0 ] ) )
1783 {
1784 desc = /^desc$/i.test ( args [ 0 ] );
1785 args.splice ( 0, 1 );
1786 }
1787
1788 if ( /^alpha$/i.test ( args [ 0 ] ) )
1789 {
1790 alpha = true;
1791 args.splice ( 0, 1 );
1792 }
1793
1794 if ( /^store$/i.test ( args [ 0 ] ) )
1795 {
1796 store = args [ 1 ];
1797 if ( typeof store !== 'string' ) return BAD_SYNTAX;
1798 args.splice ( 0, 2 );
1799 }
1800
1801 //// Redis appears to accept params in any order,
1802 //// needs some tests before allowing this here.
1803
1804 if ( args.length ) return BAD_SYNTAX;
1805
1806 //// Collect data.
1807
1808 var type = this.TYPE ( key ), data, scoreFail = false;
1809
1810 if ( type === NONE )
1811 data = [];
1812 else if ( type === LIST )
1813 data = this.LRANGE ( key, '0', '-1' );
1814 else if ( type === SET )
1815 data = this.SMEMBERS ( key );
1816 else if ( type === ZSET )
1817 data = this.ZRANGE ( key, '0', '-1' );
1818 else
1819 return BAD_TYPE;
1820
1821 data = data.map ( function ( id )
1822 {
1823 var entry = { id : id };
1824 if ( by )
1825 {
1826 entry.by = self.sortSelect ( by, id );
1827 if ( !alpha )
1828 entry.num = str2float ( entry.by || '0' );
1829 }
1830 else if ( !alpha )
1831 entry.num = str2float ( id );
1832 else
1833 entry.num = 0;
1834
1835 if ( entry.num instanceof ERROR )
1836 scoreFail = true;
1837
1838 if ( get )
1839 entry.get = get.map ( function ( get )
1840 {
1841 if ( get === '#' ) return id;
1842 return self.sortSelect ( get, id );
1843 });
1844
1845 return entry;
1846 });
1847
1848 if ( scoreFail ) return BAD_SORT;
1849
1850 //// Sort.
1851
1852 data.sort ( function ( a, b )
1853 {
1854 var d = a.num - b.num;
1855 if ( !d && by ) d = a.by < b.by ? -1 : a.by > b.by ? 1 : 0;
1856 if ( !d ) d = a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
1857 return desc ? -d : d;
1858 });
1859
1860 //// Limit.
1861
1862 if ( parseInt ( offset ) < 0 )
1863 offset = '0'; // SORT treats negative offset limit differently from other redis commands.
1864
1865 if ( limit ) data = slice ( data, offset, count, true );
1866
1867 //// Format.
1868
1869 var out = [], i;
1870 n = data.length;
1871 for ( i = 0; i < n; i ++ )
1872 {
1873 if ( get ) out.push.apply ( out, data [ i ].get );
1874 else out [ i ] = data [ i ].id;
1875 }
1876
1877 //// Store or return.
1878
1879 if ( store )
1880 {
1881 this.lStore ( store, out );
1882 return this.LLEN ( store );
1883 }
1884 else
1885 return out;
1886 },
1887
1888
1889
1890 //// Pubsub.
1891
1892 PUBLISH : function ( channel, message )
1893 {
1894 return this.pub ( channel, message );
1895 },
1896
1897
1898
1899 //// Connection.
1900 //// Quit and select could be implemented on the connection object.
1901
1902 PING : function ()
1903 {
1904 if ( arguments.length )
1905 return BAD_ARGS;
1906
1907 return PONG;
1908 },
1909
1910 ECHO : function ( message )
1911 {
1912 return message;
1913 },
1914
1915
1916
1917 //// Server.
1918 //// FLUSHALL can be implemented on the connection object.
1919
1920 DBSIZE : function ()
1921 {
1922 return this.getKeys ().length;
1923 },
1924
1925 FLUSHDB : function ()
1926 {
1927 var keys = this.getKeys (), i, n = keys.length;
1928 for ( i = 0; i < n; i ++ )
1929 this.setKey ( keys [ i ], null );
1930
1931 return OK;
1932 },
1933
1934 TIME : function ()
1935 {
1936 var time = Date.now (),
1937 sec = Math.round ( time / 1000 ),
1938 msec = ( time % 1000 ) * 1000 + Math.floor ( Math.random () * 1000 );
1939
1940 return [ sec, msec ];
1941 },
1942
1943
1944
1945 //// Helper commands.
1946/*
1947 FAKE_MISS : function ()
1948 {
1949 var implemented = this;
1950
1951 return require ( "../lib/commands" ).filter ( function ( command )
1952 {
1953 return !( command.toUpperCase () in implemented );
1954 });
1955 },
1956
1957 FAKE_AVAIL : function ()
1958 {
1959 var implemented = this;
1960
1961 return require ( "../lib/commands" ).filter ( function ( command )
1962 {
1963 return ( command.toUpperCase () in implemented );
1964 });
1965 },
1966*/
1967 FAKE_DUMP : function ( pattern )
1968 {
1969 var keys = this.KEYS ( pattern ), i, n = keys.length, out = [], key, type;
1970
1971 for ( i = 0; i < n; i ++ )
1972 {
1973 key = keys [ i ];
1974 type = this.TYPE ( key );
1975 out.push ( key, this.TTL ( key ), type.getStatus () );
1976
1977 if ( type === STRING )
1978 out.push ( this.GET ( key ) );
1979 else if ( type === LIST )
1980 out.push ( this.LRANGE ( key, '0', '-1' ) );
1981 else if ( type === HASH )
1982 out.push ( this.HGETALL ( key ) );
1983 else if ( type === SET )
1984 out.push ( this.SMEMBERS ( key ) );
1985 else if ( type === ZSET )
1986 out.push ( this.ZRANGE ( key, '0', '-1', 'withscores' ) );
1987 else
1988 throw new Error ( "WOOT! Key type is " + type );
1989 }
1990
1991 return out;
1992 }
1993};
1994
1995
1996
1997 //// These don't have an effect on the dataset, so dummies are safe for tests.
1998
1999exports.Backend.prototype.AUTH =
2000exports.Backend.prototype.BGREWRITEAOF =
2001exports.Backend.prototype.SAVE =
2002exports.Backend.prototype.BGSAVE = function () { return OK; };
2003
2004
2005
2006 //// All of these are implemented at the connection level.
2007
2008exports.Backend.prototype.QUIT =
2009
2010exports.Backend.prototype.SUBSCRIBE =
2011exports.Backend.prototype.PSUBSCRIBE =
2012exports.Backend.prototype.UNSUBSCRIBE =
2013exports.Backend.prototype.PUNSUBSCRIBE =
2014
2015exports.Backend.prototype.MULTI =
2016exports.Backend.prototype.EXEC =
2017exports.Backend.prototype.WATCH =
2018exports.Backend.prototype.UNWATCH =
2019exports.Backend.prototype.SELECT =
2020exports.Backend.prototype.DISCARD = function () { throw new Error ( "WOOT! This command shouldn't have reached the backend." ); };
2021
2022
2023