1 /* 2 * Hunt - A redis client library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.redis.BinaryRedis; 13 14 import hunt.redis.BinaryRedisPubSub; 15 import hunt.redis.BitOP; 16 import hunt.redis.BitPosParams; 17 import hunt.redis.Client; 18 import hunt.redis.BuilderFactory; 19 import hunt.redis.GeoCoordinate; 20 import hunt.redis.GeoRadiusResponse; 21 import hunt.redis.GeoUnit; 22 import hunt.redis.HostAndPort; 23 import hunt.redis.ListPosition; 24 import hunt.redis.Pipeline; 25 import hunt.redis.Protocol; 26 import hunt.redis.RedisMonitor; 27 import hunt.redis.RedisShardInfo; 28 import hunt.redis.ScanParams; 29 import hunt.redis.ScanResult; 30 import hunt.redis.SortingParams; 31 import hunt.redis.Transaction; 32 import hunt.redis.Tuple; 33 import hunt.redis.ZParams; 34 35 import hunt.redis.commands.AdvancedBinaryRedisCommands; 36 import hunt.redis.commands.BasicCommands; 37 import hunt.redis.commands.BinaryRedisCommands; 38 import hunt.redis.commands.BinaryScriptingCommands; 39 import hunt.redis.commands.MultiKeyBinaryCommands; 40 import hunt.redis.Protocol; 41 import hunt.redis.Exceptions; 42 import hunt.redis.params.ClientKillParams; 43 import hunt.redis.params.GeoRadiusParam; 44 import hunt.redis.params.MigrateParams; 45 import hunt.redis.params.SetParams; 46 import hunt.redis.params.ZAddParams; 47 import hunt.redis.params.ZIncrByParams; 48 import hunt.redis.util.RedisByteHashMap; 49 import hunt.redis.util.RedisURIHelper; 50 51 import hunt.net.util.HttpURI; 52 import hunt.collection; 53 import hunt.Exceptions; 54 import hunt.util.Common; 55 56 import hunt.Byte; 57 import hunt.Double; 58 import hunt.Long; 59 60 import std.format; 61 import std.range; 62 63 // MultiKeyBinaryCommands, 64 /** 65 * 66 */ 67 class BinaryRedis : BasicCommands, BinaryRedisCommands, 68 AdvancedBinaryRedisCommands, BinaryScriptingCommands, Closeable { 69 protected Client client = null; 70 protected Transaction transaction = null; 71 protected Pipeline pipeline = null; 72 73 this() { 74 client = new Client(); 75 } 76 77 this(string host) { 78 HttpURI uri = new HttpURI(host); 79 if (RedisURIHelper.isValid(uri)) { 80 initializeClientFromURI(uri); 81 } else { 82 client = new Client(host); 83 } 84 } 85 86 this(HostAndPort hp) { 87 this(hp.getHost(), hp.getPort()); 88 } 89 90 this(string host, int port) { 91 client = new Client(host, port); 92 } 93 94 this(string host, int port, bool ssl) { 95 client = new Client(host, port, ssl); 96 } 97 98 // this(string host, int port, bool ssl, 99 // SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, 100 // HostnameVerifier hostnameVerifier) { 101 // client = new Client(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier); 102 // } 103 104 this(string host, int port, int timeout) { 105 this(host, port, timeout, timeout); 106 } 107 108 this(string host, int port, int timeout, bool ssl) { 109 this(host, port, timeout, timeout, ssl); 110 } 111 112 // this(string host, int port, int timeout, bool ssl, 113 // SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, 114 // HostnameVerifier hostnameVerifier) { 115 // this(host, port, timeout, timeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier); 116 // } 117 118 this(string host, int port, int connectionTimeout, 119 int soTimeout) { 120 client = new Client(host, port); 121 client.setConnectionTimeout(connectionTimeout); 122 client.setSoTimeout(soTimeout); 123 } 124 125 this(string host, int port, int connectionTimeout, 126 int soTimeout, bool ssl) { 127 client = new Client(host, port, ssl); 128 client.setConnectionTimeout(connectionTimeout); 129 client.setSoTimeout(soTimeout); 130 } 131 132 // this(string host, int port, int connectionTimeout, 133 // int soTimeout, bool ssl, SSLSocketFactory sslSocketFactory, 134 // SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { 135 // client = new Client(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier); 136 // client.setConnectionTimeout(connectionTimeout); 137 // client.setSoTimeout(soTimeout); 138 // } 139 140 this(RedisShardInfo shardInfo) { 141 // client = new Client(shardInfo.getHost(), shardInfo.getPort(), shardInfo.getSsl(), 142 // shardInfo.getSslSocketFactory(), shardInfo.getSslParameters(), 143 // shardInfo.getHostnameVerifier()); 144 client = new Client(shardInfo.getHost(), shardInfo.getPort(), shardInfo.getSsl()); 145 client.setConnectionTimeout(shardInfo.getConnectionTimeout()); 146 client.setSoTimeout(shardInfo.getSoTimeout()); 147 client.setPassword(shardInfo.getPassword()); 148 client.setDb(shardInfo.getDb()); 149 } 150 151 this(HttpURI uri) { 152 initializeClientFromURI(uri); 153 } 154 155 // this(HttpURI uri, SSLSocketFactory sslSocketFactory, 156 // SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { 157 // initializeClientFromURI(uri, sslSocketFactory, sslParameters, hostnameVerifier); 158 // } 159 160 this(HttpURI uri, int timeout) { 161 this(uri, timeout, timeout); 162 } 163 164 // this(HttpURI uri, int timeout, SSLSocketFactory sslSocketFactory, 165 // SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { 166 // this(uri, timeout, timeout, sslSocketFactory, sslParameters, hostnameVerifier); 167 // } 168 169 this(HttpURI uri, int connectionTimeout, int soTimeout) { 170 initializeClientFromURI(uri); 171 client.setConnectionTimeout(connectionTimeout); 172 client.setSoTimeout(soTimeout); 173 } 174 175 // this(HttpURI uri, int connectionTimeout, int soTimeout, 176 // SSLSocketFactory sslSocketFactory,SSLParameters sslParameters, 177 // HostnameVerifier hostnameVerifier) { 178 // initializeClientFromURI(uri, sslSocketFactory, sslParameters, hostnameVerifier); 179 // client.setConnectionTimeout(connectionTimeout); 180 // client.setSoTimeout(soTimeout); 181 // } 182 183 override string toString() { 184 return format("%s:%d", client.getHost(), client.getPort()); 185 } 186 187 private void initializeClientFromURI(HttpURI uri) { 188 // initializeClientFromURI(uri, null, null, null); 189 if (!RedisURIHelper.isValid(uri)) { 190 throw new InvalidURIException(format( 191 "Cannot open Redis connection due invalid HttpURI. %s", uri.toString())); 192 } 193 194 client = new Client(uri.getHost(), uri.getPort(), false); 195 196 string password = RedisURIHelper.getPassword(uri); 197 if (password !is null) { 198 client.auth(password); 199 client.getStatusCodeReply(); 200 } 201 202 int dbIndex = RedisURIHelper.getDBIndex(uri); 203 if (dbIndex > 0) { 204 client.select(dbIndex); 205 client.getStatusCodeReply(); 206 client.setDb(dbIndex); 207 } 208 } 209 210 // private void initializeClientFromURI(HttpURI uri, SSLSocketFactory sslSocketFactory, 211 // SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { 212 // if (!RedisURIHelper.isValid(uri)) { 213 // throw new InvalidURIException(format( 214 // "Cannot open Redis connection due invalid HttpURI. %s", uri.toString())); 215 // } 216 217 // client = new Client(uri.getHost(), uri.getPort(), RedisURIHelper.isRedisSSLScheme(uri), 218 // sslSocketFactory, sslParameters, hostnameVerifier); 219 220 // string password = RedisURIHelper.getPassword(uri); 221 // if (password !is null) { 222 // client.auth(password); 223 // client.getStatusCodeReply(); 224 // } 225 226 // int dbIndex = RedisURIHelper.getDBIndex(uri); 227 // if (dbIndex > 0) { 228 // client.select(dbIndex); 229 // client.getStatusCodeReply(); 230 // client.setDb(dbIndex); 231 // } 232 // } 233 234 // override 235 string ping() { 236 checkIsInMultiOrPipeline(); 237 client.ping(); 238 return client.getStatusCodeReply(); 239 } 240 241 /** 242 * Works same as <tt>ping()</tt> but returns argument message instead of <tt>PONG</tt>. 243 * @param message 244 * @return message 245 */ 246 const(ubyte)[] ping(const(ubyte)[] message) { 247 checkIsInMultiOrPipeline(); 248 client.ping(message); 249 return client.getBinaryBulkReply(); 250 } 251 252 /** 253 * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 254 * GB). 255 * <p> 256 * Time complexity: O(1) 257 * @param key 258 * @param value 259 * @return Status code reply 260 */ 261 // override 262 string set(const(ubyte)[] key, const(ubyte)[] value) { 263 checkIsInMultiOrPipeline(); 264 client.set(key, value); 265 return client.getStatusCodeReply(); 266 } 267 268 /** 269 * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 270 * GB). 271 * @param key 272 * @param value 273 * @param params 274 * @return Status code reply 275 */ 276 // override 277 string set(const(ubyte)[] key, const(ubyte)[] value, SetParams params) { 278 checkIsInMultiOrPipeline(); 279 client.set(key, value, params); 280 return client.getStatusCodeReply(); 281 } 282 283 /** 284 * Get the value of the specified key. If the key does not exist the special value 'nil' is 285 * returned. If the value stored at key is not a string an error is returned because GET can only 286 * handle string values. 287 * <p> 288 * Time complexity: O(1) 289 * @param key 290 * @return Bulk reply 291 */ 292 // override 293 const(ubyte)[] get(const(ubyte)[] key) { 294 checkIsInMultiOrPipeline(); 295 client.get(key); 296 return client.getBinaryBulkReply(); 297 } 298 299 /** 300 * Ask the server to silently close the connection. 301 */ 302 // override 303 string quit() { 304 checkIsInMultiOrPipeline(); 305 client.quit(); 306 string quitReturn = client.getStatusCodeReply(); 307 client.close(); 308 return quitReturn; 309 } 310 311 /** 312 * Test if the specified keys exist. The command returns the number of keys exist. 313 * Time complexity: O(N) 314 * @param keys 315 * @return Integer reply, specifically: an integer greater than 0 if one or more keys exist, 316 * 0 if none of the specified keys exist. 317 */ 318 // override 319 Long exists(const(ubyte)[][] keys...) { 320 checkIsInMultiOrPipeline(); 321 client.exists(keys); 322 return client.getIntegerReply(); 323 } 324 325 /** 326 * Test if the specified key exists. The command returns true if the key exists, otherwise false is 327 * returned. Note that even keys set with an empty string as value will return true. Time 328 * complexity: O(1) 329 * @param key 330 * @return bool reply, true if the key exists, otherwise false 331 */ 332 // override 333 bool exists(const(ubyte)[] key) { 334 checkIsInMultiOrPipeline(); 335 client.exists(key); 336 return client.getIntegerReply() == 1; 337 } 338 339 /** 340 * Remove the specified keys. If a given key does not exist no operation is performed for this 341 * key. The command returns the number of keys removed. Time complexity: O(1) 342 * @param keys 343 * @return Integer reply, specifically: an integer greater than 0 if one or more keys were removed 344 * 0 if none of the specified key existed 345 */ 346 // override 347 Long del(const(ubyte)[][] keys...) { 348 checkIsInMultiOrPipeline(); 349 client.del(keys); 350 return client.getIntegerReply(); 351 } 352 353 // override 354 Long del(const(ubyte)[] key) { 355 checkIsInMultiOrPipeline(); 356 client.del(key); 357 return client.getIntegerReply(); 358 } 359 360 /** 361 * This command is very similar to DEL: it removes the specified keys. Just like DEL a key is 362 * ignored if it does not exist. However the command performs the actual memory reclaiming in a 363 * different thread, so it is not blocking, while DEL is. This is where the command name comes 364 * from: the command just unlinks the keys from the keyspace. The actual removal will happen later 365 * asynchronously. 366 * <p> 367 * Time complexity: O(1) for each key removed regardless of its size. Then the command does O(N) 368 * work in a different thread in order to reclaim memory, where N is the number of allocations the 369 * deleted objects where composed of. 370 * @param keys 371 * @return Integer reply: The number of keys that were unlinked 372 */ 373 // override 374 Long unlink(const(ubyte)[][] keys...) { 375 checkIsInMultiOrPipeline(); 376 client.unlink(keys); 377 return client.getIntegerReply(); 378 } 379 380 // override 381 Long unlink(const(ubyte)[] key) { 382 checkIsInMultiOrPipeline(); 383 client.unlink(key); 384 return client.getIntegerReply(); 385 } 386 387 /** 388 * Return the type of the value stored at key in form of a string. The type can be one of "none", 389 * "string", "list", "set". "none" is returned if the key does not exist. Time complexity: O(1) 390 * @param key 391 * @return Status code reply, specifically: "none" if the key does not exist "string" if the key 392 * contains a string value "list" if the key contains a List value "set" if the key 393 * contains a Set value "zset" if the key contains a Sorted Set value "hash" if the key 394 * contains a Hash value 395 */ 396 // override 397 string type(const(ubyte)[] key) { 398 checkIsInMultiOrPipeline(); 399 client.type(key); 400 return client.getStatusCodeReply(); 401 } 402 403 /** 404 * Delete all the keys of the currently selected DB. This command never fails. 405 * @return Status code reply 406 */ 407 // override 408 string flushDB() { 409 checkIsInMultiOrPipeline(); 410 client.flushDB(); 411 return client.getStatusCodeReply(); 412 } 413 414 /** 415 * Returns all the keys matching the glob-style pattern as space separated strings. For example if 416 * you have in the database the keys "foo" and "foobar" the command "KEYS foo*" will return 417 * "foo foobar". 418 * <p> 419 * Note that while the time complexity for this operation is O(n) the constant times are pretty 420 * low. For example Redis running on an entry level laptop can scan a 1 million keys database in 421 * 40 milliseconds. <b>Still it's better to consider this one of the slow commands that may ruin 422 * the DB performance if not used with care.</b> 423 * <p> 424 * In other words this command is intended only for debugging and special operations like creating 425 * a script to change the DB schema. Don't use it in your normal code. Use Redis Sets in order to 426 * group together a subset of objects. 427 * <p> 428 * Glob style patterns examples: 429 * <ul> 430 * <li>h?llo will match hello hallo hhllo 431 * <li>h*llo will match hllo heeeello 432 * <li>h[ae]llo will match hello and hallo, but not hillo 433 * </ul> 434 * <p> 435 * Use \ to escape special chars if you want to match them verbatim. 436 * <p> 437 * Time complexity: O(n) (with n being the number of keys in the DB, and assuming keys and pattern 438 * of limited length) 439 * @param pattern 440 * @return Multi bulk reply 441 */ 442 // override 443 Set!(const(ubyte)[]) keys(const(ubyte)[] pattern) { 444 checkIsInMultiOrPipeline(); 445 client.keys(pattern); 446 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 447 } 448 449 /** 450 * Return a randomly selected key from the currently selected DB. 451 * <p> 452 * Time complexity: O(1) 453 * @return Single line reply, specifically the randomly selected key or an empty string is the 454 * database is empty 455 */ 456 // override 457 const(ubyte)[] randomBinaryKey() { 458 checkIsInMultiOrPipeline(); 459 client.randomKey(); 460 return client.getBinaryBulkReply(); 461 } 462 463 /** 464 * Atomically renames the key oldkey to newkey. If the source and destination name are the same an 465 * error is returned. If newkey already exists it is overwritten. 466 * <p> 467 * Time complexity: O(1) 468 * @param oldkey 469 * @param newkey 470 * @return Status code repy 471 */ 472 // override 473 string rename(const(ubyte)[] oldkey, const(ubyte)[] newkey) { 474 checkIsInMultiOrPipeline(); 475 client.rename(oldkey, newkey); 476 return client.getStatusCodeReply(); 477 } 478 479 /** 480 * Rename oldkey into newkey but fails if the destination key newkey already exists. 481 * <p> 482 * Time complexity: O(1) 483 * @param oldkey 484 * @param newkey 485 * @return Integer reply, specifically: 1 if the key was renamed 0 if the target key already exist 486 */ 487 // override 488 Long renamenx(const(ubyte)[] oldkey, const(ubyte)[] newkey) { 489 checkIsInMultiOrPipeline(); 490 client.renamenx(oldkey, newkey); 491 return client.getIntegerReply(); 492 } 493 494 /** 495 * Return the number of keys in the currently selected database. 496 * @return Integer reply 497 */ 498 // override 499 Long dbSize() { 500 checkIsInMultiOrPipeline(); 501 client.dbSize(); 502 return client.getIntegerReply(); 503 } 504 505 /** 506 * Set a timeout on the specified key. After the timeout the key will be automatically deleted by 507 * the server. A key with an associated timeout is said to be volatile in Redis terminology. 508 * <p> 509 * Volatile keys are stored on disk like the other keys, the timeout is persistent too like all the 510 * other aspects of the dataset. Saving a dataset containing expires and stopping the server does 511 * not stop the flow of time as Redis stores on disk the time when the key will no longer be 512 * available as Unix time, and not the remaining seconds. 513 * <p> 514 * Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire 515 * set. It is also possible to undo the expire at all turning the key into a normal key using the 516 * {@link #persist(const(ubyte)[]) PERSIST} command. 517 * <p> 518 * Time complexity: O(1) 519 * @see <a href="http://redis.io/commands/expire">Expire Command</a> 520 * @param key 521 * @param seconds 522 * @return Integer reply, specifically: 1: the timeout was set. 0: the timeout was not set since 523 * the key already has an associated timeout (this may happen only in Redis versions < 524 * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. 525 */ 526 // override 527 Long expire(const(ubyte)[] key, int seconds) { 528 checkIsInMultiOrPipeline(); 529 client.expire(key, seconds); 530 return client.getIntegerReply(); 531 } 532 533 /** 534 * EXPIREAT works exactly like {@link #expire(const(ubyte)[], int) EXPIRE} but instead to get the number of 535 * seconds representing the Time To Live of the key as a second argument (that is a relative way 536 * of specifying the TTL), it takes an absolute one in the form of a UNIX timestamp (Number of 537 * seconds elapsed since 1 Gen 1970). 538 * <p> 539 * EXPIREAT was introduced in order to implement the Append Only File persistence mode so that 540 * EXPIRE commands are automatically translated into EXPIREAT commands for the append only file. 541 * Of course EXPIREAT can also used by programmers that need a way to simply specify that a given 542 * key should expire at a given time in the future. 543 * <p> 544 * Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire 545 * set. It is also possible to undo the expire at all turning the key into a normal key using the 546 * {@link #persist(const(ubyte)[]) PERSIST} command. 547 * <p> 548 * Time complexity: O(1) 549 * @see <a href="http://redis.io/commands/expire">Expire Command</a> 550 * @param key 551 * @param unixTime 552 * @return Integer reply, specifically: 1: the timeout was set. 0: the timeout was not set since 553 * the key already has an associated timeout (this may happen only in Redis versions < 554 * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. 555 */ 556 // override 557 Long expireAt(const(ubyte)[] key, long unixTime) { 558 checkIsInMultiOrPipeline(); 559 client.expireAt(key, unixTime); 560 return client.getIntegerReply(); 561 } 562 563 /** 564 * The TTL command returns the remaining time to live in seconds of a key that has an 565 * {@link #expire(const(ubyte)[], int) EXPIRE} set. This introspection capability allows a Redis client to 566 * check how many seconds a given key will continue to be part of the dataset. 567 * @param key 568 * @return Integer reply, returns the remaining time to live in seconds of a key that has an 569 * EXPIRE. If the Key does not exists or does not have an associated expire, -1 is 570 * returned. 571 */ 572 // override 573 Long ttl(const(ubyte)[] key) { 574 checkIsInMultiOrPipeline(); 575 client.ttl(key); 576 return client.getIntegerReply(); 577 } 578 579 /** 580 * Alters the last access time of a key(s). A key is ignored if it does not exist. 581 * Time complexity: O(N) where N is the number of keys that will be touched. 582 * @param keys 583 * @return Integer reply: The number of keys that were touched. 584 */ 585 // override 586 Long touch(const(ubyte)[][] keys...) { 587 checkIsInMultiOrPipeline(); 588 client.touch(keys); 589 return client.getIntegerReply(); 590 } 591 592 // override 593 Long touch(const(ubyte)[] key) { 594 checkIsInMultiOrPipeline(); 595 client.touch(key); 596 return client.getIntegerReply(); 597 } 598 599 /** 600 * Select the DB with having the specified zero-based numeric index. For default every new client 601 * connection is automatically selected to DB 0. 602 * @param index 603 * @return Status code reply 604 */ 605 // override 606 string select(int index) { 607 checkIsInMultiOrPipeline(); 608 client.select(index); 609 string statusCodeReply = client.getStatusCodeReply(); 610 client.setDb(index); 611 612 return statusCodeReply; 613 } 614 615 // override 616 string swapDB(int index1, int index2) { 617 checkIsInMultiOrPipeline(); 618 client.swapDB(index1, index2); 619 return client.getStatusCodeReply(); 620 } 621 622 /** 623 * Move the specified key from the currently selected DB to the specified destination DB. Note 624 * that this command returns 1 only if the key was successfully moved, and 0 if the target key was 625 * already there or if the source key was not found at all, so it is possible to use MOVE as a 626 * locking primitive. 627 * @param key 628 * @param dbIndex 629 * @return Integer reply, specifically: 1 if the key was moved 0 if the key was not moved because 630 * already present on the target DB or was not found in the current DB. 631 */ 632 // override 633 Long move(const(ubyte)[] key, int dbIndex) { 634 checkIsInMultiOrPipeline(); 635 client.move(key, dbIndex); 636 return client.getIntegerReply(); 637 } 638 639 /** 640 * Delete all the keys of all the existing databases, not just the currently selected one. This 641 * command never fails. 642 * @return Status code reply 643 */ 644 // override 645 string flushAll() { 646 checkIsInMultiOrPipeline(); 647 client.flushAll(); 648 return client.getStatusCodeReply(); 649 } 650 651 /** 652 * GETSET is an atomic set this value and return the old value command. Set key to the string 653 * value and return the old value stored at key. The string can't be longer than 1073741824 bytes 654 * (1 GB). 655 * <p> 656 * Time complexity: O(1) 657 * @param key 658 * @param value 659 * @return Bulk reply 660 */ 661 // override 662 const(ubyte)[] getSet(const(ubyte)[] key, const(ubyte)[] value) { 663 checkIsInMultiOrPipeline(); 664 client.getSet(key, value); 665 return client.getBinaryBulkReply(); 666 } 667 668 /** 669 * Get the values of all the specified keys. If one or more keys don't exist or is not of type 670 * string, a 'nil' value is returned instead of the value of the specified key, but the operation 671 * never fails. 672 * <p> 673 * Time complexity: O(1) for every key 674 * @param keys 675 * @return Multi bulk reply 676 */ 677 // override 678 List!(const(ubyte)[]) mget(const(ubyte)[][] keys...) { 679 checkIsInMultiOrPipeline(); 680 client.mget(keys); 681 return client.getBinaryMultiBulkReply(); 682 } 683 684 /** 685 * SETNX works exactly like {@link #set(const(ubyte)[], const(ubyte)[]) SET} with the only difference that if the 686 * key already exists no operation is performed. SETNX actually means "SET if Not eXists". 687 * <p> 688 * Time complexity: O(1) 689 * @param key 690 * @param value 691 * @return Integer reply, specifically: 1 if the key was set 0 if the key was not set 692 */ 693 // override 694 Long setnx(const(ubyte)[] key, const(ubyte)[] value) { 695 checkIsInMultiOrPipeline(); 696 client.setnx(key, value); 697 return client.getIntegerReply(); 698 } 699 700 /** 701 * The command is exactly equivalent to the following group of commands: 702 * {@link #set(const(ubyte)[], const(ubyte)[]) SET} + {@link #expire(const(ubyte)[], int) EXPIRE}. The operation is 703 * atomic. 704 * <p> 705 * Time complexity: O(1) 706 * @param key 707 * @param seconds 708 * @param value 709 * @return Status code reply 710 */ 711 // override 712 string setex(const(ubyte)[] key, int seconds, const(ubyte)[] value) { 713 checkIsInMultiOrPipeline(); 714 client.setex(key, seconds, value); 715 return client.getStatusCodeReply(); 716 } 717 718 /** 719 * Set the the respective keys to the respective values. MSET will replace old values with new 720 * values, while {@link #msetnx(const(ubyte)[]...) MSETNX} will not perform any operation at all even if 721 * just a single key already exists. 722 * <p> 723 * Because of this semantic MSETNX can be used in order to set different keys representing 724 * different fields of an unique logic object in a way that ensures that either all the fields or 725 * none at all are set. 726 * <p> 727 * Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B 728 * are modified, another client talking to Redis can either see the changes to both A and B at 729 * once, or no modification at all. 730 * @see #msetnx(const(ubyte)[]...) 731 * @param keysvalues 732 * @return Status code reply Basically +OK as MSET can't fail 733 */ 734 // override 735 string mset(const(ubyte)[][] keysvalues...) { 736 checkIsInMultiOrPipeline(); 737 client.mset(keysvalues); 738 return client.getStatusCodeReply(); 739 } 740 741 /** 742 * Set the the respective keys to the respective values. {@link #mset(const(ubyte)[]...) MSET} will 743 * replace old values with new values, while MSETNX will not perform any operation at all even if 744 * just a single key already exists. 745 * <p> 746 * Because of this semantic MSETNX can be used in order to set different keys representing 747 * different fields of an unique logic object in a way that ensures that either all the fields or 748 * none at all are set. 749 * <p> 750 * Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B 751 * are modified, another client talking to Redis can either see the changes to both A and B at 752 * once, or no modification at all. 753 * @see #mset(const(ubyte)[]...) 754 * @param keysvalues 755 * @return Integer reply, specifically: 1 if the all the keys were set 0 if no key was set (at 756 * least one key already existed) 757 */ 758 // override 759 Long msetnx(const(ubyte)[][] keysvalues...) { 760 checkIsInMultiOrPipeline(); 761 client.msetnx(keysvalues); 762 return client.getIntegerReply(); 763 } 764 765 /** 766 * DECRBY work just like {@link #decr(const(ubyte)[]) INCR} but instead to decrement by 1 the decrement is 767 * integer. 768 * <p> 769 * INCR commands are limited to 64 bit signed integers. 770 * <p> 771 * Note: this is actually a string operation, that is, in Redis there are not "integer" types. 772 * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, 773 * and then converted back as a string. 774 * <p> 775 * Time complexity: O(1) 776 * @see #incr(const(ubyte)[]) 777 * @see #decr(const(ubyte)[]) 778 * @see #incrBy(const(ubyte)[], long) 779 * @param key 780 * @param decrement 781 * @return Integer reply, this commands will reply with the new value of key after the increment. 782 */ 783 // override 784 Long decrBy(const(ubyte)[] key, long decrement) { 785 checkIsInMultiOrPipeline(); 786 client.decrBy(key, decrement); 787 return client.getIntegerReply(); 788 } 789 790 /** 791 * Decrement the number stored at key by one. If the key does not exist or contains a value of a 792 * wrong type, set the key to the value of "0" before to perform the decrement operation. 793 * <p> 794 * INCR commands are limited to 64 bit signed integers. 795 * <p> 796 * Note: this is actually a string operation, that is, in Redis there are not "integer" types. 797 * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, 798 * and then converted back as a string. 799 * <p> 800 * Time complexity: O(1) 801 * @see #incr(const(ubyte)[]) 802 * @see #incrBy(const(ubyte)[], long) 803 * @see #decrBy(const(ubyte)[], long) 804 * @param key 805 * @return Integer reply, this commands will reply with the new value of key after the increment. 806 */ 807 // override 808 Long decr(const(ubyte)[] key) { 809 checkIsInMultiOrPipeline(); 810 client.decr(key); 811 return client.getIntegerReply(); 812 } 813 814 /** 815 * INCRBY work just like {@link #incr(const(ubyte)[]) INCR} but instead to increment by 1 the increment is 816 * integer. 817 * <p> 818 * INCR commands are limited to 64 bit signed integers. 819 * <p> 820 * Note: this is actually a string operation, that is, in Redis there are not "integer" types. 821 * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, 822 * and then converted back as a string. 823 * <p> 824 * Time complexity: O(1) 825 * @see #incr(const(ubyte)[]) 826 * @see #decr(const(ubyte)[]) 827 * @see #decrBy(const(ubyte)[], long) 828 * @param key 829 * @param increment 830 * @return Integer reply, this commands will reply with the new value of key after the increment. 831 */ 832 // override 833 Long incrBy(const(ubyte)[] key, long increment) { 834 checkIsInMultiOrPipeline(); 835 client.incrBy(key, increment); 836 return client.getIntegerReply(); 837 } 838 839 /** 840 * INCRBYFLOAT work just like {@link #incrBy(const(ubyte)[], long)} INCRBY} but increments by floats 841 * instead of integers. 842 * <p> 843 * INCRBYFLOAT commands are limited to double precision floating point values. 844 * <p> 845 * Note: this is actually a string operation, that is, in Redis there are not "double" types. 846 * Simply the string stored at the key is parsed as a base double precision floating point value, 847 * incremented, and then converted back as a string. There is no DECRYBYFLOAT but providing a 848 * negative value will work as expected. 849 * <p> 850 * Time complexity: O(1) 851 * @see #incr(const(ubyte)[]) 852 * @see #decr(const(ubyte)[]) 853 * @see #decrBy(const(ubyte)[], long) 854 * @param key the key to increment 855 * @param increment the value to increment by 856 * @return Integer reply, this commands will reply with the new value of key after the increment. 857 */ 858 // override 859 Double incrByFloat(const(ubyte)[] key, double increment) { 860 checkIsInMultiOrPipeline(); 861 client.incrByFloat(key, increment); 862 string dval = client.getBulkReply(); 863 return (dval !is null ? new Double(dval) : null); 864 } 865 866 /** 867 * Increment the number stored at key by one. If the key does not exist or contains a value of a 868 * wrong type, set the key to the value of "0" before to perform the increment operation. 869 * <p> 870 * INCR commands are limited to 64 bit signed integers. 871 * <p> 872 * Note: this is actually a string operation, that is, in Redis there are not "integer" types. 873 * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, 874 * and then converted back as a string. 875 * <p> 876 * Time complexity: O(1) 877 * @see #incrBy(const(ubyte)[], long) 878 * @see #decr(const(ubyte)[]) 879 * @see #decrBy(const(ubyte)[], long) 880 * @param key 881 * @return Integer reply, this commands will reply with the new value of key after the increment. 882 */ 883 // override 884 Long incr(const(ubyte)[] key) { 885 checkIsInMultiOrPipeline(); 886 client.incr(key); 887 return client.getIntegerReply(); 888 } 889 890 /** 891 * If the key already exists and is a string, this command appends the provided value at the end 892 * of the string. If the key does not exist it is created and set as an empty string, so APPEND 893 * will be very similar to SET in this special case. 894 * <p> 895 * Time complexity: O(1). The amortized time complexity is O(1) assuming the appended value is 896 * small and the already present value is of any size, since the dynamic string library used by 897 * Redis will double the free space available on every reallocation. 898 * @param key 899 * @param value 900 * @return Integer reply, specifically the total length of the string after the append operation. 901 */ 902 // override 903 Long append(const(ubyte)[] key, const(ubyte)[] value) { 904 checkIsInMultiOrPipeline(); 905 client.append(key, value); 906 return client.getIntegerReply(); 907 } 908 909 /** 910 * Return a subset of the string from offset start to offset end (both offsets are inclusive). 911 * Negative offsets can be used in order to provide an offset starting from the end of the string. 912 * So -1 means the last char, -2 the penultimate and so forth. 913 * <p> 914 * The function handles out of range requests without raising an error, but just limiting the 915 * resulting range to the actual length of the string. 916 * <p> 917 * Time complexity: O(start+n) (with start being the start index and n the total length of the 918 * requested range). Note that the lookup part of this command is O(1) so for small strings this 919 * is actually an O(1) command. 920 * @param key 921 * @param start 922 * @param end 923 * @return Bulk reply 924 */ 925 // override 926 const(ubyte)[] substr(const(ubyte)[] key, int start, int end) { 927 checkIsInMultiOrPipeline(); 928 client.substr(key, start, end); 929 return client.getBinaryBulkReply(); 930 } 931 932 /** 933 * Set the specified hash field to the specified value. 934 * <p> 935 * If key does not exist, a new key holding a hash is created. 936 * <p> 937 * <b>Time complexity:</b> O(1) 938 * @param key 939 * @param field 940 * @param value 941 * @return If the field already exists, and the HSET just produced an update of the value, 0 is 942 * returned, otherwise if a new field is created 1 is returned. 943 */ 944 // override 945 Long hset(const(ubyte)[] key, const(ubyte)[] field, const(ubyte)[] value) { 946 checkIsInMultiOrPipeline(); 947 client.hset(key, field, value); 948 return client.getIntegerReply(); 949 } 950 951 // override 952 Long hset(const(ubyte)[] key, Map!(const(ubyte)[], const(ubyte)[]) hash) { 953 checkIsInMultiOrPipeline(); 954 client.hset(key, hash); 955 return client.getIntegerReply(); 956 } 957 958 /** 959 * If key holds a hash, retrieve the value associated to the specified field. 960 * <p> 961 * If the field is not found or the key does not exist, a special 'nil' value is returned. 962 * <p> 963 * <b>Time complexity:</b> O(1) 964 * @param key 965 * @param field 966 * @return Bulk reply 967 */ 968 // // override 969 const(ubyte)[] hget(const(ubyte)[] key, const(ubyte)[] field) { 970 checkIsInMultiOrPipeline(); 971 client.hget(key, field); 972 return client.getBinaryBulkReply(); 973 } 974 975 /** 976 * Set the specified hash field to the specified value if the field not exists. <b>Time 977 * complexity:</b> O(1) 978 * @param key 979 * @param field 980 * @param value 981 * @return If the field already exists, 0 is returned, otherwise if a new field is created 1 is 982 * returned. 983 */ 984 // // override 985 Long hsetnx(const(ubyte)[] key, const(ubyte)[] field, const(ubyte)[] value) { 986 checkIsInMultiOrPipeline(); 987 client.hsetnx(key, field, value); 988 return client.getIntegerReply(); 989 } 990 991 /** 992 * Set the respective fields to the respective values. HMSET replaces old values with new values. 993 * <p> 994 * If key does not exist, a new key holding a hash is created. 995 * <p> 996 * <b>Time complexity:</b> O(N) (with N being the number of fields) 997 * @param key 998 * @param hash 999 * @return Always OK because HMSET can't fail 1000 */ 1001 // override 1002 string hmset(const(ubyte)[] key, Map!(const(ubyte)[], const(ubyte)[]) hash) { 1003 checkIsInMultiOrPipeline(); 1004 client.hmset(key, hash); 1005 return client.getStatusCodeReply(); 1006 } 1007 1008 /** 1009 * Retrieve the values associated to the specified fields. 1010 * <p> 1011 * If some of the specified fields do not exist, nil values are returned. Non existing keys are 1012 * considered like empty hashes. 1013 * <p> 1014 * <b>Time complexity:</b> O(N) (with N being the number of fields) 1015 * @param key 1016 * @param fields 1017 * @return Multi Bulk Reply specifically a list of all the values associated with the specified 1018 * fields, in the same order of the request. 1019 */ 1020 // override 1021 List!(const(ubyte)[]) hmget(const(ubyte)[] key, const(ubyte)[][] fields...) { 1022 checkIsInMultiOrPipeline(); 1023 client.hmget(key, fields); 1024 return client.getBinaryMultiBulkReply(); 1025 } 1026 1027 /** 1028 * Increment the number stored at field in the hash at key by value. If key does not exist, a new 1029 * key holding a hash is created. If field does not exist or holds a string, the value is set to 0 1030 * before applying the operation. Since the value argument is signed you can use this command to 1031 * perform both increments and decrements. 1032 * <p> 1033 * The range of values supported by HINCRBY is limited to 64 bit signed integers. 1034 * <p> 1035 * <b>Time complexity:</b> O(1) 1036 * @param key 1037 * @param field 1038 * @param value 1039 * @return Integer reply The new value at field after the increment operation. 1040 */ 1041 // override 1042 Long hincrBy(const(ubyte)[] key, const(ubyte)[] field, long value) { 1043 checkIsInMultiOrPipeline(); 1044 client.hincrBy(key, field, value); 1045 return client.getIntegerReply(); 1046 } 1047 1048 /** 1049 * Increment the number stored at field in the hash at key by a double precision floating point 1050 * value. If key does not exist, a new key holding a hash is created. If field does not exist or 1051 * holds a string, the value is set to 0 before applying the operation. Since the value argument 1052 * is signed you can use this command to perform both increments and decrements. 1053 * <p> 1054 * The range of values supported by HINCRBYFLOAT is limited to double precision floating point 1055 * values. 1056 * <p> 1057 * <b>Time complexity:</b> O(1) 1058 * @param key 1059 * @param field 1060 * @param value 1061 * @return Double precision floating point reply The new value at field after the increment 1062 * operation. 1063 */ 1064 // override 1065 Double hincrByFloat(const(ubyte)[] key, const(ubyte)[] field, double value) { 1066 checkIsInMultiOrPipeline(); 1067 client.hincrByFloat(key, field, value); 1068 string dval = client.getBulkReply(); 1069 return (dval !is null ? new Double(dval) : null); 1070 } 1071 1072 /** 1073 * Test for existence of a specified field in a hash. <b>Time complexity:</b> O(1) 1074 * @param key 1075 * @param field 1076 * @return Return true if the hash stored at key contains the specified field. Return false if the key is 1077 * not found or the field is not present. 1078 */ 1079 // override 1080 bool hexists(const(ubyte)[] key, const(ubyte)[] field) { 1081 checkIsInMultiOrPipeline(); 1082 client.hexists(key, field); 1083 return client.getIntegerReply() == 1; 1084 } 1085 1086 /** 1087 * Remove the specified field from an hash stored at key. 1088 * <p> 1089 * <b>Time complexity:</b> O(1) 1090 * @param key 1091 * @param fields 1092 * @return If the field was present in the hash it is deleted and 1 is returned, otherwise 0 is 1093 * returned and no operation is performed. 1094 */ 1095 // override 1096 Long hdel(const(ubyte)[] key, const(ubyte)[][] fields...) { 1097 checkIsInMultiOrPipeline(); 1098 client.hdel(key, fields); 1099 return client.getIntegerReply(); 1100 } 1101 1102 /** 1103 * Return the number of items in a hash. 1104 * <p> 1105 * <b>Time complexity:</b> O(1) 1106 * @param key 1107 * @return The number of entries (fields) contained in the hash stored at key. If the specified 1108 * key does not exist, 0 is returned assuming an empty hash. 1109 */ 1110 // override 1111 Long hlen(const(ubyte)[] key) { 1112 checkIsInMultiOrPipeline(); 1113 client.hlen(key); 1114 return client.getIntegerReply(); 1115 } 1116 1117 /** 1118 * Return all the fields in a hash. 1119 * <p> 1120 * <b>Time complexity:</b> O(N), where N is the total number of entries 1121 * @param key 1122 * @return All the fields names contained into a hash. 1123 */ 1124 // override 1125 Set!(const(ubyte)[]) hkeys(const(ubyte)[] key) { 1126 checkIsInMultiOrPipeline(); 1127 client.hkeys(key); 1128 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 1129 } 1130 1131 /** 1132 * Return all the values in a hash. 1133 * <p> 1134 * <b>Time complexity:</b> O(N), where N is the total number of entries 1135 * @param key 1136 * @return All the fields values contained into a hash. 1137 */ 1138 // override 1139 List!(const(ubyte)[]) hvals(const(ubyte)[] key) { 1140 checkIsInMultiOrPipeline(); 1141 client.hvals(key); 1142 return client.getBinaryMultiBulkReply(); 1143 } 1144 1145 /** 1146 * Return all the fields and associated values in a hash. 1147 * <p> 1148 * <b>Time complexity:</b> O(N), where N is the total number of entries 1149 * @param key 1150 * @return All the fields and values contained into a hash. 1151 */ 1152 // override 1153 Map!(const(ubyte)[], const(ubyte)[]) hgetAll(const(ubyte)[] key) { 1154 checkIsInMultiOrPipeline(); 1155 client.hgetAll(key); 1156 List!(const(ubyte)[]) flatHash = client.getBinaryMultiBulkReply(); 1157 Map!(const(ubyte)[], const(ubyte)[]) hash = new RedisByteHashMap(); 1158 InputRange!(const(ubyte)[]) iterator = flatHash.iterator(); 1159 while(!iterator.empty()) { 1160 const(ubyte)[] k = iterator.front(); 1161 iterator.popFront(); 1162 const(ubyte)[] v = iterator.front(); 1163 iterator.popFront(); 1164 hash.put(k, v); 1165 } 1166 1167 return hash; 1168 } 1169 1170 /** 1171 * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key 1172 * does not exist an empty list is created just before the append operation. If the key exists but 1173 * is not a List an error is returned. 1174 * <p> 1175 * Time complexity: O(1) 1176 * @see BinaryRedis#rpush(const(ubyte)[], const(ubyte)[]...) 1177 * @param key 1178 * @param strings 1179 * @return Integer reply, specifically, the number of elements inside the list after the push 1180 * operation. 1181 */ 1182 // override 1183 Long rpush(const(ubyte)[] key, const(ubyte)[][] strings...) { 1184 checkIsInMultiOrPipeline(); 1185 client.rpush(key, strings); 1186 return client.getIntegerReply(); 1187 } 1188 1189 /** 1190 * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key 1191 * does not exist an empty list is created just before the append operation. If the key exists but 1192 * is not a List an error is returned. 1193 * <p> 1194 * Time complexity: O(1) 1195 * @see BinaryRedis#rpush(const(ubyte)[], const(ubyte)[]...) 1196 * @param key 1197 * @param strings 1198 * @return Integer reply, specifically, the number of elements inside the list after the push 1199 * operation. 1200 */ 1201 // override 1202 Long lpush(const(ubyte)[] key, const(ubyte)[][] strings...) { 1203 checkIsInMultiOrPipeline(); 1204 client.lpush(key, strings); 1205 return client.getIntegerReply(); 1206 } 1207 1208 /** 1209 * Return the length of the list stored at the specified key. If the key does not exist zero is 1210 * returned (the same behaviour as for empty lists). If the value stored at key is not a list an 1211 * error is returned. 1212 * <p> 1213 * Time complexity: O(1) 1214 * @param key 1215 * @return The length of the list. 1216 */ 1217 // override 1218 Long llen(const(ubyte)[] key) { 1219 checkIsInMultiOrPipeline(); 1220 client.llen(key); 1221 return client.getIntegerReply(); 1222 } 1223 1224 /** 1225 * Return the specified elements of the list stored at the specified key. Start and end are 1226 * zero-based indexes. 0 is the first element of the list (the list head), 1 the next element and 1227 * so on. 1228 * <p> 1229 * For example LRANGE foobar 0 2 will return the first three elements of the list. 1230 * <p> 1231 * start and end can also be negative numbers indicating offsets from the end of the list. For 1232 * example -1 is the last element of the list, -2 the penultimate element and so on. 1233 * <p> 1234 * <b>Consistency with range functions in various programming languages</b> 1235 * <p> 1236 * Note that if you have a list of numbers from 0 to 100, LRANGE 0 10 will return 11 elements, 1237 * that is, rightmost item is included. This may or may not be consistent with behavior of 1238 * range-related functions in your programming language of choice (think Ruby's Range.new, 1239 * Array#slice or Python's range() function). 1240 * <p> 1241 * LRANGE behavior is consistent with one of Tcl. 1242 * <p> 1243 * <b>Out-of-range indexes</b> 1244 * <p> 1245 * Indexes out of range will not produce an error: if start is over the end of the list, or start 1246 * > end, an empty list is returned. If end is over the end of the list Redis will threat it 1247 * just like the last element of the list. 1248 * <p> 1249 * Time complexity: O(start+n) (with n being the length of the range and start being the start 1250 * offset) 1251 * @param key 1252 * @param start 1253 * @param stop 1254 * @return Multi bulk reply, specifically a list of elements in the specified range. 1255 */ 1256 // override 1257 List!(const(ubyte)[]) lrange(const(ubyte)[] key, long start, long stop) { 1258 checkIsInMultiOrPipeline(); 1259 client.lrange(key, start, stop); 1260 return client.getBinaryMultiBulkReply(); 1261 } 1262 1263 /** 1264 * Trim an existing list so that it will contain only the specified range of elements specified. 1265 * Start and end are zero-based indexes. 0 is the first element of the list (the list head), 1 the 1266 * next element and so on. 1267 * <p> 1268 * For example LTRIM foobar 0 2 will modify the list stored at foobar key so that only the first 1269 * three elements of the list will remain. 1270 * <p> 1271 * start and end can also be negative numbers indicating offsets from the end of the list. For 1272 * example -1 is the last element of the list, -2 the penultimate element and so on. 1273 * <p> 1274 * Indexes out of range will not produce an error: if start is over the end of the list, or start 1275 * > end, an empty list is left as value. If end over the end of the list Redis will threat it 1276 * just like the last element of the list. 1277 * <p> 1278 * Hint: the obvious use of LTRIM is together with LPUSH/RPUSH. For example: 1279 * <p> 1280 * {@code lpush("mylist", "someelement"); ltrim("mylist", 0, 99); * } 1281 * <p> 1282 * The above two commands will push elements in the list taking care that the list will not grow 1283 * without limits. This is very useful when using Redis to store logs for example. It is important 1284 * to note that when used in this way LTRIM is an O(1) operation because in the average case just 1285 * one element is removed from the tail of the list. 1286 * <p> 1287 * Time complexity: O(n) (with n being len of list - len of range) 1288 * @param key 1289 * @param start 1290 * @param stop 1291 * @return Status code reply 1292 */ 1293 // override 1294 string ltrim(const(ubyte)[] key, long start, long stop) { 1295 checkIsInMultiOrPipeline(); 1296 client.ltrim(key, start, stop); 1297 return client.getStatusCodeReply(); 1298 } 1299 1300 /** 1301 * Return the specified element of the list stored at the specified key. 0 is the first element, 1 1302 * the second and so on. Negative indexes are supported, for example -1 is the last element, -2 1303 * the penultimate and so on. 1304 * <p> 1305 * If the value stored at key is not of list type an error is returned. If the index is out of 1306 * range a 'nil' reply is returned. 1307 * <p> 1308 * Note that even if the average time complexity is O(n) asking for the first or the last element 1309 * of the list is O(1). 1310 * <p> 1311 * Time complexity: O(n) (with n being the length of the list) 1312 * @param key 1313 * @param index 1314 * @return Bulk reply, specifically the requested element 1315 */ 1316 // override 1317 const(ubyte)[] lindex(const(ubyte)[] key, long index) { 1318 checkIsInMultiOrPipeline(); 1319 client.lindex(key, index); 1320 return client.getBinaryBulkReply(); 1321 } 1322 1323 /** 1324 * Set a new value as the element at index position of the List at key. 1325 * <p> 1326 * Out of range indexes will generate an error. 1327 * <p> 1328 * Similarly to other list commands accepting indexes, the index can be negative to access 1329 * elements starting from the end of the list. So -1 is the last element, -2 is the penultimate, 1330 * and so forth. 1331 * <p> 1332 * <b>Time complexity:</b> 1333 * <p> 1334 * O(N) (with N being the length of the list), setting the first or last elements of the list is 1335 * O(1). 1336 * @see #lindex(const(ubyte)[], long) 1337 * @param key 1338 * @param index 1339 * @param value 1340 * @return Status code reply 1341 */ 1342 // override 1343 string lset(const(ubyte)[] key, long index, const(ubyte)[] value) { 1344 checkIsInMultiOrPipeline(); 1345 client.lset(key, index, value); 1346 return client.getStatusCodeReply(); 1347 } 1348 1349 /** 1350 * Remove the first count occurrences of the value element from the list. If count is zero all the 1351 * elements are removed. If count is negative elements are removed from tail to head, instead to 1352 * go from head to tail that is the normal behaviour. So for example LREM with count -2 and hello 1353 * as value to remove against the list (a,b,c,hello,x,hello,hello) will leave the list 1354 * (a,b,c,hello,x). The number of removed elements is returned as an integer, see below for more 1355 * information about the returned value. Note that non existing keys are considered like empty 1356 * lists by LREM, so LREM against non existing keys will always return 0. 1357 * <p> 1358 * Time complexity: O(N) (with N being the length of the list) 1359 * @param key 1360 * @param count 1361 * @param value 1362 * @return Integer Reply, specifically: The number of removed elements if the operation succeeded 1363 */ 1364 // override 1365 Long lrem(const(ubyte)[] key, long count, const(ubyte)[] value) { 1366 checkIsInMultiOrPipeline(); 1367 client.lrem(key, count, value); 1368 return client.getIntegerReply(); 1369 } 1370 1371 /** 1372 * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example 1373 * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become 1374 * "b","c". 1375 * <p> 1376 * If the key does not exist or the list is already empty the special value 'nil' is returned. 1377 * @see #rpop(const(ubyte)[]) 1378 * @param key 1379 * @return Bulk reply 1380 */ 1381 // override 1382 const(ubyte)[] lpop(const(ubyte)[] key) { 1383 checkIsInMultiOrPipeline(); 1384 client.lpop(key); 1385 return client.getBinaryBulkReply(); 1386 } 1387 1388 /** 1389 * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example 1390 * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become 1391 * "b","c". 1392 * <p> 1393 * If the key does not exist or the list is already empty the special value 'nil' is returned. 1394 * @see #lpop(const(ubyte)[]) 1395 * @param key 1396 * @return Bulk reply 1397 */ 1398 // override 1399 const(ubyte)[] rpop(const(ubyte)[] key) { 1400 checkIsInMultiOrPipeline(); 1401 client.rpop(key); 1402 return client.getBinaryBulkReply(); 1403 } 1404 1405 /** 1406 * Atomically return and remove the last (tail) element of the srckey list, and push the element 1407 * as the first (head) element of the dstkey list. For example if the source list contains the 1408 * elements "a","b","c" and the destination list contains the elements "foo","bar" after an 1409 * RPOPLPUSH command the content of the two lists will be "a","b" and "c","foo","bar". 1410 * <p> 1411 * If the key does not exist or the list is already empty the special value 'nil' is returned. If 1412 * the srckey and dstkey are the same the operation is equivalent to removing the last element 1413 * from the list and pushing it as first element of the list, so it's a "list rotation" command. 1414 * <p> 1415 * Time complexity: O(1) 1416 * @param srckey 1417 * @param dstkey 1418 * @return Bulk reply 1419 */ 1420 // override 1421 const(ubyte)[] rpoplpush(const(ubyte)[] srckey, const(ubyte)[] dstkey) { 1422 checkIsInMultiOrPipeline(); 1423 client.rpoplpush(srckey, dstkey); 1424 return client.getBinaryBulkReply(); 1425 } 1426 1427 /** 1428 * Add the specified member to the set value stored at key. If member is already a member of the 1429 * set no operation is performed. If key does not exist a new set with the specified member as 1430 * sole member is created. If the key exists but does not hold a set value an error is returned. 1431 * <p> 1432 * Time complexity O(1) 1433 * @param key 1434 * @param members 1435 * @return Integer reply, specifically: 1 if the new element was added 0 if the element was 1436 * already a member of the set 1437 */ 1438 // override 1439 Long sadd(const(ubyte)[] key, const(ubyte)[][] members...) { 1440 checkIsInMultiOrPipeline(); 1441 client.sadd(key, members); 1442 return client.getIntegerReply(); 1443 } 1444 1445 /** 1446 * Return all the members (elements) of the set value stored at key. This is just syntax glue for 1447 * {@link #sinter(const(ubyte)[]...)} SINTER}. 1448 * <p> 1449 * Time complexity O(N) 1450 * @param key the key of the set 1451 * @return Multi bulk reply 1452 */ 1453 // override 1454 Set!(const(ubyte)[]) smembers(const(ubyte)[] key) { 1455 checkIsInMultiOrPipeline(); 1456 client.smembers(key); 1457 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 1458 } 1459 1460 /** 1461 * Remove the specified member from the set value stored at key. If member was not a member of the 1462 * set no operation is performed. If key does not hold a set value an error is returned. 1463 * <p> 1464 * Time complexity O(1) 1465 * @param key the key of the set 1466 * @param member the set member to remove 1467 * @return Integer reply, specifically: 1 if the new element was removed 0 if the new element was 1468 * not a member of the set 1469 */ 1470 // override 1471 Long srem(const(ubyte)[] key, const(ubyte)[][] member...) { 1472 checkIsInMultiOrPipeline(); 1473 client.srem(key, member); 1474 return client.getIntegerReply(); 1475 } 1476 1477 /** 1478 * Remove a random element from a Set returning it as return value. If the Set is empty or the key 1479 * does not exist, a nil object is returned. 1480 * <p> 1481 * The {@link #srandmember(const(ubyte)[])} command does a similar work but the returned element is not 1482 * removed from the Set. 1483 * <p> 1484 * Time complexity O(1) 1485 * @param key 1486 * @return Bulk reply 1487 */ 1488 // override 1489 const(ubyte)[] spop(const(ubyte)[] key) { 1490 checkIsInMultiOrPipeline(); 1491 client.spop(key); 1492 return client.getBinaryBulkReply(); 1493 } 1494 1495 // override 1496 Set!(const(ubyte)[]) spop(const(ubyte)[] key, long count) { 1497 checkIsInMultiOrPipeline(); 1498 client.spop(key, count); 1499 List!(const(ubyte)[]) members = client.getBinaryMultiBulkReply(); 1500 if (members is null) return null; 1501 return new SetFromList!(const(ubyte)[])(members); 1502 } 1503 1504 /** 1505 * Move the specified member from the set at srckey to the set at dstkey. This operation is 1506 * atomic, in every given moment the element will appear to be in the source or destination set 1507 * for accessing clients. 1508 * <p> 1509 * If the source set does not exist or does not contain the specified element no operation is 1510 * performed and zero is returned, otherwise the element is removed from the source set and added 1511 * to the destination set. On success one is returned, even if the element was already present in 1512 * the destination set. 1513 * <p> 1514 * An error is raised if the source or destination keys contain a non Set value. 1515 * <p> 1516 * Time complexity O(1) 1517 * @param srckey 1518 * @param dstkey 1519 * @param member 1520 * @return Integer reply, specifically: 1 if the element was moved 0 if the element was not found 1521 * on the first set and no operation was performed 1522 */ 1523 // override 1524 Long smove(const(ubyte)[] srckey, const(ubyte)[] dstkey, const(ubyte)[] member) { 1525 checkIsInMultiOrPipeline(); 1526 client.smove(srckey, dstkey, member); 1527 return client.getIntegerReply(); 1528 } 1529 1530 /** 1531 * Return the set cardinality (number of elements). If the key does not exist 0 is returned, like 1532 * for empty sets. 1533 * @param key 1534 * @return Integer reply, specifically: the cardinality (number of elements) of the set as an 1535 * integer. 1536 */ 1537 // override 1538 Long scard(const(ubyte)[] key) { 1539 checkIsInMultiOrPipeline(); 1540 client.scard(key); 1541 return client.getIntegerReply(); 1542 } 1543 1544 /** 1545 * Return true if member is a member of the set stored at key, otherwise false is returned. 1546 * <p> 1547 * Time complexity O(1) 1548 * @param key 1549 * @param member 1550 * @return bool reply, specifically: true if the element is a member of the set false if the element 1551 * is not a member of the set OR if the key does not exist 1552 */ 1553 // override 1554 bool sismember(const(ubyte)[] key, const(ubyte)[] member) { 1555 checkIsInMultiOrPipeline(); 1556 client.sismember(key, member); 1557 return client.getIntegerReply() == 1; 1558 } 1559 1560 /** 1561 * Return the members of a set resulting from the intersection of all the sets hold at the 1562 * specified keys. Like in {@link #lrange(const(ubyte)[], long, long)} LRANGE} the result is sent to the 1563 * client as a multi-bulk reply (see the protocol specification for more information). If just a 1564 * single key is specified, then this command produces the same result as 1565 * {@link #smembers(const(ubyte)[]) SMEMBERS}. Actually SMEMBERS is just syntax sugar for SINTER. 1566 * <p> 1567 * Non existing keys are considered like empty sets, so if one of the keys is missing an empty set 1568 * is returned (since the intersection with an empty set always is an empty set). 1569 * <p> 1570 * Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the 1571 * number of sets 1572 * @param keys 1573 * @return Multi bulk reply, specifically the list of common elements. 1574 */ 1575 // override 1576 Set!(const(ubyte)[]) sinter(const(ubyte)[][] keys...) { 1577 checkIsInMultiOrPipeline(); 1578 client.sinter(keys); 1579 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 1580 } 1581 1582 /** 1583 * This commanad works exactly like {@link #sinter(const(ubyte)[]...) SINTER} but instead of being returned 1584 * the resulting set is stored as dstkey. 1585 * <p> 1586 * Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the 1587 * number of sets 1588 * @param dstkey 1589 * @param keys 1590 * @return Status code reply 1591 */ 1592 // override 1593 Long sinterstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...) { 1594 checkIsInMultiOrPipeline(); 1595 client.sinterstore(dstkey, keys); 1596 return client.getIntegerReply(); 1597 } 1598 1599 /** 1600 * Return the members of a set resulting from the union of all the sets hold at the specified 1601 * keys. Like in {@link #lrange(const(ubyte)[], long, long)} LRANGE} the result is sent to the client as a 1602 * multi-bulk reply (see the protocol specification for more information). If just a single key is 1603 * specified, then this command produces the same result as {@link #smembers(const(ubyte)[]) SMEMBERS}. 1604 * <p> 1605 * Non existing keys are considered like empty sets. 1606 * <p> 1607 * Time complexity O(N) where N is the total number of elements in all the provided sets 1608 * @param keys 1609 * @return Multi bulk reply, specifically the list of common elements. 1610 */ 1611 // override 1612 Set!(const(ubyte)[]) sunion(const(ubyte)[][] keys...) { 1613 checkIsInMultiOrPipeline(); 1614 client.sunion(keys); 1615 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 1616 } 1617 1618 /** 1619 * This command works exactly like {@link #sunion(const(ubyte)[]...) SUNION} but instead of being returned 1620 * the resulting set is stored as dstkey. Any existing value in dstkey will be over-written. 1621 * <p> 1622 * Time complexity O(N) where N is the total number of elements in all the provided sets 1623 * @param dstkey 1624 * @param keys 1625 * @return Status code reply 1626 */ 1627 // override 1628 Long sunionstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...) { 1629 checkIsInMultiOrPipeline(); 1630 client.sunionstore(dstkey, keys); 1631 return client.getIntegerReply(); 1632 } 1633 1634 /** 1635 * Return the difference between the Set stored at key1 and all the Sets key2, ..., keyN 1636 * <p> 1637 * <b>Example:</b> 1638 * 1639 * <pre> 1640 * key1 = [x, a, b, c] 1641 * key2 = [c] 1642 * key3 = [a, d] 1643 * SDIFF key1,key2,key3 => [x, b] 1644 * </pre> 1645 * 1646 * Non existing keys are considered like empty sets. 1647 * <p> 1648 * <b>Time complexity:</b> 1649 * <p> 1650 * O(N) with N being the total number of elements of all the sets 1651 * @param keys 1652 * @return Return the members of a set resulting from the difference between the first set 1653 * provided and all the successive sets. 1654 */ 1655 // override 1656 Set!(const(ubyte)[]) sdiff(const(ubyte)[][] keys...) { 1657 checkIsInMultiOrPipeline(); 1658 client.sdiff(keys); 1659 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 1660 } 1661 1662 /** 1663 * This command works exactly like {@link #sdiff(const(ubyte)[]...) SDIFF} but instead of being returned 1664 * the resulting set is stored in dstkey. 1665 * @param dstkey 1666 * @param keys 1667 * @return Status code reply 1668 */ 1669 // override 1670 Long sdiffstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...) { 1671 checkIsInMultiOrPipeline(); 1672 client.sdiffstore(dstkey, keys); 1673 return client.getIntegerReply(); 1674 } 1675 1676 /** 1677 * Return a random element from a Set, without removing the element. If the Set is empty or the 1678 * key does not exist, a nil object is returned. 1679 * <p> 1680 * The SPOP command does a similar work but the returned element is popped (removed) from the Set. 1681 * <p> 1682 * Time complexity O(1) 1683 * @param key 1684 * @return Bulk reply 1685 */ 1686 // override 1687 const(ubyte)[] srandmember(const(ubyte)[] key) { 1688 checkIsInMultiOrPipeline(); 1689 client.srandmember(key); 1690 return client.getBinaryBulkReply(); 1691 } 1692 1693 // override 1694 List!(const(ubyte)[]) srandmember(const(ubyte)[] key, int count) { 1695 checkIsInMultiOrPipeline(); 1696 client.srandmember(key, count); 1697 return client.getBinaryMultiBulkReply(); 1698 } 1699 1700 /** 1701 * Add the specified member having the specified score to the sorted set stored at key. If member 1702 * is already a member of the sorted set the score is updated, and the element reinserted in the 1703 * right position to ensure sorting. If key does not exist a new sorted set with the specified 1704 * member as sole member is created. If the key exists but does not hold a sorted set value an 1705 * error is returned. 1706 * <p> 1707 * The score value can be the string representation of a double precision floating point number. 1708 * <p> 1709 * Time complexity O(log(N)) with N being the number of elements in the sorted set 1710 * @param key 1711 * @param score 1712 * @param member 1713 * @return Integer reply, specifically: 1 if the new element was added 0 if the element was 1714 * already a member of the sorted set and the score was updated 1715 */ 1716 // override 1717 Long zadd(const(ubyte)[] key, double score, const(ubyte)[] member) { 1718 checkIsInMultiOrPipeline(); 1719 client.zadd(key, score, member); 1720 return client.getIntegerReply(); 1721 } 1722 1723 // override 1724 Long zadd(const(ubyte)[] key, double score, const(ubyte)[] member, ZAddParams params) { 1725 checkIsInMultiOrPipeline(); 1726 client.zadd(key, score, member, params); 1727 return client.getIntegerReply(); 1728 } 1729 1730 // override 1731 Long zadd(const(ubyte)[] key, Map!(const(ubyte)[], double) scoreMembers) { 1732 checkIsInMultiOrPipeline(); 1733 client.zadd(key, scoreMembers); 1734 return client.getIntegerReply(); 1735 } 1736 1737 // override 1738 Long zadd(const(ubyte)[] key, Map!(const(ubyte)[], double) scoreMembers, ZAddParams params) { 1739 checkIsInMultiOrPipeline(); 1740 client.zadd(key, scoreMembers, params); 1741 return client.getIntegerReply(); 1742 } 1743 1744 // override 1745 const(ubyte)[][] zrange(const(ubyte)[] key, long start, long stop) { 1746 checkIsInMultiOrPipeline(); 1747 client.zrange(key, start, stop); 1748 // return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 1749 List!(const(ubyte)[]) r = client.getBinaryMultiBulkReply(); 1750 return r.toArray(); 1751 } 1752 1753 /** 1754 * Remove the specified member from the sorted set value stored at key. If member was not a member 1755 * of the set no operation is performed. If key does not not hold a set value an error is 1756 * returned. 1757 * <p> 1758 * Time complexity O(log(N)) with N being the number of elements in the sorted set 1759 * @param key 1760 * @param members 1761 * @return Integer reply, specifically: 1 if the new element was removed 0 if the new element was 1762 * not a member of the set 1763 */ 1764 // override 1765 Long zrem(const(ubyte)[] key, const(ubyte)[][] members...) { 1766 checkIsInMultiOrPipeline(); 1767 client.zrem(key, members); 1768 return client.getIntegerReply(); 1769 } 1770 1771 /** 1772 * If member already exists in the sorted set adds the increment to its score and updates the 1773 * position of the element in the sorted set accordingly. If member does not already exist in the 1774 * sorted set it is added with increment as score (that is, like if the previous score was 1775 * virtually zero). If key does not exist a new sorted set with the specified member as sole 1776 * member is created. If the key exists but does not hold a sorted set value an error is returned. 1777 * <p> 1778 * The score value can be the string representation of a double precision floating point number. 1779 * It's possible to provide a negative value to perform a decrement. 1780 * <p> 1781 * For an introduction to sorted sets check the Introduction to Redis data types page. 1782 * <p> 1783 * Time complexity O(log(N)) with N being the number of elements in the sorted set 1784 * @param key 1785 * @param increment 1786 * @param member 1787 * @return The new score 1788 */ 1789 // override 1790 double zincrby(const(ubyte)[] key, double increment, const(ubyte)[] member) { 1791 checkIsInMultiOrPipeline(); 1792 client.zincrby(key, increment, member); 1793 Double r = BuilderFactory.DOUBLE.build(client.getOne()); 1794 return r.value(); 1795 } 1796 1797 // override 1798 double zincrby(const(ubyte)[] key, double increment, const(ubyte)[] member, ZIncrByParams params) { 1799 checkIsInMultiOrPipeline(); 1800 client.zincrby(key, increment, member, params); 1801 Double r = BuilderFactory.DOUBLE.build(client.getOne()); 1802 return r.value(); 1803 } 1804 1805 /** 1806 * Return the rank (or index) or member in the sorted set at key, with scores being ordered from 1807 * low to high. 1808 * <p> 1809 * When the given member does not exist in the sorted set, the special value 'nil' is returned. 1810 * The returned rank (or index) of the member is 0-based for both commands. 1811 * <p> 1812 * <b>Time complexity:</b> 1813 * <p> 1814 * O(log(N)) 1815 * @see #zrevrank(const(ubyte)[], const(ubyte)[]) 1816 * @param key 1817 * @param member 1818 * @return Integer reply or a nil bulk reply, specifically: the rank of the element as an integer 1819 * reply if the element exists. A nil bulk reply if there is no such element. 1820 */ 1821 // override 1822 Long zrank(const(ubyte)[] key, const(ubyte)[] member) { 1823 checkIsInMultiOrPipeline(); 1824 client.zrank(key, member); 1825 return client.getIntegerReply(); 1826 } 1827 1828 /** 1829 * Return the rank (or index) or member in the sorted set at key, with scores being ordered from 1830 * high to low. 1831 * <p> 1832 * When the given member does not exist in the sorted set, the special value 'nil' is returned. 1833 * The returned rank (or index) of the member is 0-based for both commands. 1834 * <p> 1835 * <b>Time complexity:</b> 1836 * <p> 1837 * O(log(N)) 1838 * @see #zrank(const(ubyte)[], const(ubyte)[]) 1839 * @param key 1840 * @param member 1841 * @return Integer reply or a nil bulk reply, specifically: the rank of the element as an integer 1842 * reply if the element exists. A nil bulk reply if there is no such element. 1843 */ 1844 // override 1845 Long zrevrank(const(ubyte)[] key, const(ubyte)[] member) { 1846 checkIsInMultiOrPipeline(); 1847 client.zrevrank(key, member); 1848 return client.getIntegerReply(); 1849 } 1850 1851 // override 1852 const(ubyte)[][] zrevrange(const(ubyte)[] key, long start, long stop) { 1853 checkIsInMultiOrPipeline(); 1854 client.zrevrange(key, start, stop); 1855 // return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 1856 auto r = client.getBinaryMultiBulkReply(); 1857 return r.toArray(); 1858 1859 } 1860 1861 // override 1862 Set!(Tuple) zrangeWithScores(const(ubyte)[] key, long start, long stop) { 1863 checkIsInMultiOrPipeline(); 1864 client.zrangeWithScores(key, start, stop); 1865 return getTupledSet(); 1866 } 1867 1868 // override 1869 Set!(Tuple) zrevrangeWithScores(const(ubyte)[] key, long start, long stop) { 1870 checkIsInMultiOrPipeline(); 1871 client.zrevrangeWithScores(key, start, stop); 1872 return getTupledSet(); 1873 } 1874 1875 /** 1876 * Return the sorted set cardinality (number of elements). If the key does not exist 0 is 1877 * returned, like for empty sorted sets. 1878 * <p> 1879 * Time complexity O(1) 1880 * @param key 1881 * @return the cardinality (number of elements) of the set as an integer. 1882 */ 1883 // override 1884 Long zcard(const(ubyte)[] key) { 1885 checkIsInMultiOrPipeline(); 1886 client.zcard(key); 1887 return client.getIntegerReply(); 1888 } 1889 1890 /** 1891 * Return the score of the specified element of the sorted set at key. If the specified element 1892 * does not exist in the sorted set, or the key does not exist at all, a special 'nil' value is 1893 * returned. 1894 * <p> 1895 * <b>Time complexity:</b> O(1) 1896 * @param key 1897 * @param member 1898 * @return the score 1899 */ 1900 // override 1901 Double zscore(const(ubyte)[] key, const(ubyte)[] member) { 1902 checkIsInMultiOrPipeline(); 1903 client.zscore(key, member); 1904 string score = client.getBulkReply(); 1905 return (score !is null ? new Double(score) : null); 1906 } 1907 1908 Transaction multi() { 1909 client.multi(); 1910 client.getOne(); // expected OK 1911 transaction = new Transaction(client); 1912 return transaction; 1913 } 1914 1915 protected void checkIsInMultiOrPipeline() { 1916 if (client.isInMulti()) { 1917 throw new RedisDataException( 1918 "Cannot use Redis when in Multi. Please use Transaction or reset jedis state."); 1919 } else if (pipeline !is null && pipeline.hasPipelinedResponse()) { 1920 throw new RedisDataException( 1921 "Cannot use Redis when in Pipeline. Please use Pipeline or reset jedis state ."); 1922 } 1923 } 1924 1925 void connect() { 1926 client.connect(); 1927 } 1928 1929 // void disconnect() { 1930 // client.disconnect(); 1931 // } 1932 1933 void resetState() { 1934 if (client.isConnected()) { 1935 if (transaction !is null) { 1936 transaction.close(); 1937 } 1938 1939 if (pipeline !is null) { 1940 pipeline.close(); 1941 } 1942 1943 client.resetState(); 1944 } 1945 1946 transaction = null; 1947 pipeline = null; 1948 } 1949 1950 // override 1951 string watch(const(ubyte)[][] keys...) { 1952 client.watch(keys); 1953 return client.getStatusCodeReply(); 1954 } 1955 1956 // override 1957 string unwatch() { 1958 client.unwatch(); 1959 return client.getStatusCodeReply(); 1960 } 1961 1962 // override 1963 void close() { 1964 client.close(); 1965 } 1966 1967 /** 1968 * Sort a Set or a List. 1969 * <p> 1970 * Sort the elements contained in the List, Set, or Sorted Set value at key. By default sorting is 1971 * numeric with elements being compared as double precision floating point numbers. This is the 1972 * simplest form of SORT. 1973 * @see #sort(const(ubyte)[], const(ubyte)[]) 1974 * @see #sort(const(ubyte)[], SortingParams) 1975 * @see #sort(const(ubyte)[], SortingParams, const(ubyte)[]) 1976 * @param key 1977 * @return Assuming the Set/List at key contains a list of numbers, the return value will be the 1978 * list of numbers ordered from the smallest to the biggest number. 1979 */ 1980 // override 1981 List!(const(ubyte)[]) sort(const(ubyte)[] key) { 1982 checkIsInMultiOrPipeline(); 1983 client.sort(key); 1984 return client.getBinaryMultiBulkReply(); 1985 } 1986 1987 /** 1988 * Sort a Set or a List accordingly to the specified parameters. 1989 * <p> 1990 * <b>examples:</b> 1991 * <p> 1992 * Given are the following sets and key/values: 1993 * 1994 * <pre> 1995 * x = [1, 2, 3] 1996 * y = [a, b, c] 1997 * 1998 * k1 = z 1999 * k2 = y 2000 * k3 = x 2001 * 2002 * w1 = 9 2003 * w2 = 8 2004 * w3 = 7 2005 * </pre> 2006 * 2007 * Sort Order: 2008 * 2009 * <pre> 2010 * sort(x) or sort(x, sp.asc()) 2011 * -> [1, 2, 3] 2012 * 2013 * sort(x, sp.desc()) 2014 * -> [3, 2, 1] 2015 * 2016 * sort(y) 2017 * -> [c, a, b] 2018 * 2019 * sort(y, sp.alpha()) 2020 * -> [a, b, c] 2021 * 2022 * sort(y, sp.alpha().desc()) 2023 * -> [c, a, b] 2024 * </pre> 2025 * 2026 * Limit (e.g. for Pagination): 2027 * 2028 * <pre> 2029 * sort(x, sp.limit(0, 2)) 2030 * -> [1, 2] 2031 * 2032 * sort(y, sp.alpha().desc().limit(1, 2)) 2033 * -> [b, a] 2034 * </pre> 2035 * 2036 * Sorting by external keys: 2037 * 2038 * <pre> 2039 * sort(x, sb.by(w*)) 2040 * -> [3, 2, 1] 2041 * 2042 * sort(x, sb.by(w*).desc()) 2043 * -> [1, 2, 3] 2044 * </pre> 2045 * 2046 * Getting external keys: 2047 * 2048 * <pre> 2049 * sort(x, sp.by(w*).get(k*)) 2050 * -> [x, y, z] 2051 * 2052 * sort(x, sp.by(w*).get(#).get(k*)) 2053 * -> [3, x, 2, y, 1, z] 2054 * </pre> 2055 * @see #sort(const(ubyte)[]) 2056 * @see #sort(const(ubyte)[], SortingParams, const(ubyte)[]) 2057 * @param key 2058 * @param sortingParameters 2059 * @return a list of sorted elements. 2060 */ 2061 // override 2062 List!(const(ubyte)[]) sort(const(ubyte)[] key, SortingParams sortingParameters) { 2063 checkIsInMultiOrPipeline(); 2064 client.sort(key, sortingParameters); 2065 return client.getBinaryMultiBulkReply(); 2066 } 2067 2068 /** 2069 * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking 2070 * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty 2071 * lists. 2072 * <p> 2073 * The following is a description of the exact semantic. We describe BLPOP but the two commands 2074 * are identical, the only difference is that BLPOP pops the element from the left (head) of the 2075 * list, and BRPOP pops from the right (tail). 2076 * <p> 2077 * <b>Non blocking behavior</b> 2078 * <p> 2079 * When BLPOP is called, if at least one of the specified keys contain a non empty list, an 2080 * element is popped from the head of the list and returned to the caller together with the name 2081 * of the key (BLPOP returns a two elements array, the first element is the key, the second the 2082 * popped value). 2083 * <p> 2084 * Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 2085 * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP 2086 * guarantees to return an element from the list stored at list2 (since it is the first non empty 2087 * list starting from the left). 2088 * <p> 2089 * <b>Blocking behavior</b> 2090 * <p> 2091 * If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other 2092 * client performs a LPUSH or an RPUSH operation against one of the lists. 2093 * <p> 2094 * Once new data is present on one of the lists, the client finally returns with the name of the 2095 * key unblocking it and the popped value. 2096 * <p> 2097 * When blocking, if a non-zero timeout is specified, the client will unblock returning a nil 2098 * special value if the specified amount of seconds passed without a push operation against at 2099 * least one of the specified keys. 2100 * <p> 2101 * The timeout argument is interpreted as an integer value. A timeout of zero means instead to 2102 * block forever. 2103 * <p> 2104 * <b>Multiple clients blocking for the same keys</b> 2105 * <p> 2106 * Multiple clients can block for the same key. They are put into a queue, so the first to be 2107 * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. 2108 * <p> 2109 * <b>blocking POP inside a MULTI/EXEC transaction</b> 2110 * <p> 2111 * BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies 2112 * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis 2113 * transaction). 2114 * <p> 2115 * The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil 2116 * reply, exactly what happens when the timeout is reached. If you like science fiction, think at 2117 * it like if inside MULTI/EXEC the time will flow at infinite speed :) 2118 * <p> 2119 * Time complexity: O(1) 2120 * @see #brpop(int, const(ubyte)[]...) 2121 * @param timeout 2122 * @param keys 2123 * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the 2124 * unblocking key and the popped value. 2125 * <p> 2126 * When a non-zero timeout is specified, and the BLPOP operation timed out, the return 2127 * value is a nil multi bulk reply. Most client values will return false or nil 2128 * accordingly to the programming language used. 2129 */ 2130 // override 2131 List!(const(ubyte)[]) blpop(int timeout, const(ubyte)[][] keys...) { 2132 return blpop(getArgsAddTimeout(timeout, keys)); 2133 } 2134 2135 private const(ubyte)[][] getArgsAddTimeout(int timeout, const(ubyte)[][] keys) { 2136 int size = cast(int)keys.length; 2137 const(ubyte)[][] args = new const(ubyte)[][size + 1]; 2138 for (int at = 0; at != size; ++at) { 2139 args[at] = keys[at]; 2140 } 2141 args[size] = Protocol.toByteArray(timeout); 2142 return args; 2143 } 2144 2145 /** 2146 * Sort a Set or a List accordingly to the specified parameters and store the result at dstkey. 2147 * @see #sort(const(ubyte)[], SortingParams) 2148 * @see #sort(const(ubyte)[]) 2149 * @see #sort(const(ubyte)[], const(ubyte)[]) 2150 * @param key 2151 * @param sortingParameters 2152 * @param dstkey 2153 * @return The number of elements of the list at dstkey. 2154 */ 2155 // override 2156 Long sort(const(ubyte)[] key, SortingParams sortingParameters, const(ubyte)[] dstkey) { 2157 checkIsInMultiOrPipeline(); 2158 client.sort(key, sortingParameters, dstkey); 2159 return client.getIntegerReply(); 2160 } 2161 2162 /** 2163 * Sort a Set or a List and Store the Result at dstkey. 2164 * <p> 2165 * Sort the elements contained in the List, Set, or Sorted Set value at key and store the result 2166 * at dstkey. By default sorting is numeric with elements being compared as double precision 2167 * floating point numbers. This is the simplest form of SORT. 2168 * @see #sort(const(ubyte)[]) 2169 * @see #sort(const(ubyte)[], SortingParams) 2170 * @see #sort(const(ubyte)[], SortingParams, const(ubyte)[]) 2171 * @param key 2172 * @param dstkey 2173 * @return The number of elements of the list at dstkey. 2174 */ 2175 // override 2176 Long sort(const(ubyte)[] key, const(ubyte)[] dstkey) { 2177 checkIsInMultiOrPipeline(); 2178 client.sort(key, dstkey); 2179 return client.getIntegerReply(); 2180 } 2181 2182 /** 2183 * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking 2184 * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty 2185 * lists. 2186 * <p> 2187 * The following is a description of the exact semantic. We describe BLPOP but the two commands 2188 * are identical, the only difference is that BLPOP pops the element from the left (head) of the 2189 * list, and BRPOP pops from the right (tail). 2190 * <p> 2191 * <b>Non blocking behavior</b> 2192 * <p> 2193 * When BLPOP is called, if at least one of the specified keys contain a non empty list, an 2194 * element is popped from the head of the list and returned to the caller together with the name 2195 * of the key (BLPOP returns a two elements array, the first element is the key, the second the 2196 * popped value). 2197 * <p> 2198 * Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 2199 * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP 2200 * guarantees to return an element from the list stored at list2 (since it is the first non empty 2201 * list starting from the left). 2202 * <p> 2203 * <b>Blocking behavior</b> 2204 * <p> 2205 * If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other 2206 * client performs a LPUSH or an RPUSH operation against one of the lists. 2207 * <p> 2208 * Once new data is present on one of the lists, the client finally returns with the name of the 2209 * key unblocking it and the popped value. 2210 * <p> 2211 * When blocking, if a non-zero timeout is specified, the client will unblock returning a nil 2212 * special value if the specified amount of seconds passed without a push operation against at 2213 * least one of the specified keys. 2214 * <p> 2215 * The timeout argument is interpreted as an integer value. A timeout of zero means instead to 2216 * block forever. 2217 * <p> 2218 * <b>Multiple clients blocking for the same keys</b> 2219 * <p> 2220 * Multiple clients can block for the same key. They are put into a queue, so the first to be 2221 * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. 2222 * <p> 2223 * <b>blocking POP inside a MULTI/EXEC transaction</b> 2224 * <p> 2225 * BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies 2226 * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis 2227 * transaction). 2228 * <p> 2229 * The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil 2230 * reply, exactly what happens when the timeout is reached. If you like science fiction, think at 2231 * it like if inside MULTI/EXEC the time will flow at infinite speed :) 2232 * <p> 2233 * Time complexity: O(1) 2234 * @see #blpop(int, const(ubyte)[]...) 2235 * @param timeout 2236 * @param keys 2237 * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the 2238 * unblocking key and the popped value. 2239 * <p> 2240 * When a non-zero timeout is specified, and the BLPOP operation timed out, the return 2241 * value is a nil multi bulk reply. Most client values will return false or nil 2242 * accordingly to the programming language used. 2243 */ 2244 // override 2245 List!(const(ubyte)[]) brpop(int timeout, const(ubyte)[][] keys...) { 2246 return brpop(getArgsAddTimeout(timeout, keys)); 2247 } 2248 2249 // override 2250 List!(const(ubyte)[]) blpop(const(ubyte)[][] args...) { 2251 checkIsInMultiOrPipeline(); 2252 client.blpop(args); 2253 client.setTimeoutInfinite(); 2254 try { 2255 return client.getBinaryMultiBulkReply(); 2256 } finally { 2257 client.rollbackTimeout(); 2258 } 2259 } 2260 2261 // override 2262 List!(const(ubyte)[]) brpop(const(ubyte)[][] args...) { 2263 checkIsInMultiOrPipeline(); 2264 client.brpop(args); 2265 client.setTimeoutInfinite(); 2266 try { 2267 return client.getBinaryMultiBulkReply(); 2268 } finally { 2269 client.rollbackTimeout(); 2270 } 2271 } 2272 2273 /** 2274 * Request for authentication in a password protected Redis server. A Redis server can be 2275 * instructed to require a password before to allow clients to issue commands. This is done using 2276 * the requirepass directive in the Redis configuration file. If the password given by the client 2277 * is correct the server replies with an OK status code reply and starts accepting commands from 2278 * the client. Otherwise an error is returned and the clients needs to try a new password. Note 2279 * that for the high performance nature of Redis it is possible to try a lot of passwords in 2280 * parallel in very short time, so make sure to generate a strong and very long password so that 2281 * this attack is infeasible. 2282 * @param password 2283 * @return Status code reply 2284 */ 2285 // override 2286 string auth(string password) { 2287 checkIsInMultiOrPipeline(); 2288 client.auth(password); 2289 return client.getStatusCodeReply(); 2290 } 2291 2292 Pipeline pipelined() { 2293 pipeline = new Pipeline(); 2294 pipeline.setClient(client); 2295 return pipeline; 2296 } 2297 2298 // override 2299 Long zcount(const(ubyte)[] key, double min, double max) { 2300 checkIsInMultiOrPipeline(); 2301 client.zcount(key, min, max); 2302 return client.getIntegerReply(); 2303 } 2304 2305 // override 2306 Long zcount(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) { 2307 checkIsInMultiOrPipeline(); 2308 client.zcount(key, min, max); 2309 return client.getIntegerReply(); 2310 } 2311 2312 /** 2313 * Return the all the elements in the sorted set at key with a score between min and max 2314 * (including elements with score equal to min or max). 2315 * <p> 2316 * The elements having the same score are returned sorted lexicographically as ASCII strings (this 2317 * follows from a property of Redis sorted sets and does not involve further computation). 2318 * <p> 2319 * Using the optional {@link #zrangeByScore(const(ubyte)[], double, double, int, int) LIMIT} it's possible 2320 * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large 2321 * the commands needs to traverse the list for offset elements and this adds up to the O(M) 2322 * figure. 2323 * <p> 2324 * The {@link #zcount(const(ubyte)[], double, double) ZCOUNT} command is similar to 2325 * {@link #zrangeByScore(const(ubyte)[], double, double) ZRANGEBYSCORE} but instead of returning the 2326 * actual elements in the specified interval, it just returns the number of matching elements. 2327 * <p> 2328 * <b>Exclusive intervals and infinity</b> 2329 * <p> 2330 * min and max can be -inf and +inf, so that you are not required to know what's the greatest or 2331 * smallest element in order to take, for instance, elements "up to a given value". 2332 * <p> 2333 * Also while the interval is for default closed (inclusive) it's possible to specify open 2334 * intervals prefixing the score with a "(" character, so for instance: 2335 * <p> 2336 * {@code ZRANGEBYSCORE zset (1.3 5} 2337 * <p> 2338 * Will return all the values with score > 1.3 and <= 5, while for instance: 2339 * <p> 2340 * {@code ZRANGEBYSCORE zset (5 (10} 2341 * <p> 2342 * Will return all the values with score > 5 and < 10 (5 and 10 excluded). 2343 * <p> 2344 * <b>Time complexity:</b> 2345 * <p> 2346 * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of 2347 * elements returned by the command, so if M is constant (for instance you always ask for the 2348 * first ten elements with LIMIT) you can consider it O(log(N)) 2349 * @see #zrangeByScore(const(ubyte)[], double, double) 2350 * @see #zrangeByScore(const(ubyte)[], double, double, int, int) 2351 * @see #zrangeByScoreWithScores(const(ubyte)[], double, double) 2352 * @see #zrangeByScoreWithScores(const(ubyte)[], double, double, int, int) 2353 * @see #zcount(const(ubyte)[], double, double) 2354 * @param key 2355 * @param min 2356 * @param max 2357 * @return Multi bulk reply specifically a list of elements in the specified score range. 2358 */ 2359 // override 2360 Set!(const(ubyte)[]) zrangeByScore(const(ubyte)[] key, double min, double max) { 2361 checkIsInMultiOrPipeline(); 2362 client.zrangeByScore(key, min, max); 2363 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2364 } 2365 2366 // override 2367 Set!(const(ubyte)[]) zrangeByScore(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) { 2368 checkIsInMultiOrPipeline(); 2369 client.zrangeByScore(key, min, max); 2370 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2371 } 2372 2373 /** 2374 * Return the all the elements in the sorted set at key with a score between min and max 2375 * (including elements with score equal to min or max). 2376 * <p> 2377 * The elements having the same score are returned sorted lexicographically as ASCII strings (this 2378 * follows from a property of Redis sorted sets and does not involve further computation). 2379 * <p> 2380 * Using the optional {@link #zrangeByScore(const(ubyte)[], double, double, int, int) LIMIT} it's possible 2381 * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large 2382 * the commands needs to traverse the list for offset elements and this adds up to the O(M) 2383 * figure. 2384 * <p> 2385 * The {@link #zcount(const(ubyte)[], double, double) ZCOUNT} command is similar to 2386 * {@link #zrangeByScore(const(ubyte)[], double, double) ZRANGEBYSCORE} but instead of returning the 2387 * actual elements in the specified interval, it just returns the number of matching elements. 2388 * <p> 2389 * <b>Exclusive intervals and infinity</b> 2390 * <p> 2391 * min and max can be -inf and +inf, so that you are not required to know what's the greatest or 2392 * smallest element in order to take, for instance, elements "up to a given value". 2393 * <p> 2394 * Also while the interval is for default closed (inclusive) it's possible to specify open 2395 * intervals prefixing the score with a "(" character, so for instance: 2396 * <p> 2397 * {@code ZRANGEBYSCORE zset (1.3 5} 2398 * <p> 2399 * Will return all the values with score > 1.3 and <= 5, while for instance: 2400 * <p> 2401 * {@code ZRANGEBYSCORE zset (5 (10} 2402 * <p> 2403 * Will return all the values with score > 5 and < 10 (5 and 10 excluded). 2404 * <p> 2405 * <b>Time complexity:</b> 2406 * <p> 2407 * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of 2408 * elements returned by the command, so if M is constant (for instance you always ask for the 2409 * first ten elements with LIMIT) you can consider it O(log(N)) 2410 * @see #zrangeByScore(const(ubyte)[], double, double) 2411 * @see #zrangeByScore(const(ubyte)[], double, double, int, int) 2412 * @see #zrangeByScoreWithScores(const(ubyte)[], double, double) 2413 * @see #zrangeByScoreWithScores(const(ubyte)[], double, double, int, int) 2414 * @see #zcount(const(ubyte)[], double, double) 2415 * @param key 2416 * @param min 2417 * @param max 2418 * @param offset 2419 * @param count 2420 * @return Multi bulk reply specifically a list of elements in the specified score range. 2421 */ 2422 // override 2423 Set!(const(ubyte)[]) zrangeByScore(const(ubyte)[] key, double min, double max, 2424 int offset, int count) { 2425 checkIsInMultiOrPipeline(); 2426 client.zrangeByScore(key, min, max, offset, count); 2427 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2428 } 2429 2430 // override 2431 Set!(const(ubyte)[]) zrangeByScore(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max, 2432 int offset, int count) { 2433 checkIsInMultiOrPipeline(); 2434 client.zrangeByScore(key, min, max, offset, count); 2435 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2436 } 2437 2438 /** 2439 * Return the all the elements in the sorted set at key with a score between min and max 2440 * (including elements with score equal to min or max). 2441 * <p> 2442 * The elements having the same score are returned sorted lexicographically as ASCII strings (this 2443 * follows from a property of Redis sorted sets and does not involve further computation). 2444 * <p> 2445 * Using the optional {@link #zrangeByScore(const(ubyte)[], double, double, int, int) LIMIT} it's possible 2446 * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large 2447 * the commands needs to traverse the list for offset elements and this adds up to the O(M) 2448 * figure. 2449 * <p> 2450 * The {@link #zcount(const(ubyte)[], double, double) ZCOUNT} command is similar to 2451 * {@link #zrangeByScore(const(ubyte)[], double, double) ZRANGEBYSCORE} but instead of returning the 2452 * actual elements in the specified interval, it just returns the number of matching elements. 2453 * <p> 2454 * <b>Exclusive intervals and infinity</b> 2455 * <p> 2456 * min and max can be -inf and +inf, so that you are not required to know what's the greatest or 2457 * smallest element in order to take, for instance, elements "up to a given value". 2458 * <p> 2459 * Also while the interval is for default closed (inclusive) it's possible to specify open 2460 * intervals prefixing the score with a "(" character, so for instance: 2461 * <p> 2462 * {@code ZRANGEBYSCORE zset (1.3 5} 2463 * <p> 2464 * Will return all the values with score > 1.3 and <= 5, while for instance: 2465 * <p> 2466 * {@code ZRANGEBYSCORE zset (5 (10} 2467 * <p> 2468 * Will return all the values with score > 5 and < 10 (5 and 10 excluded). 2469 * <p> 2470 * <b>Time complexity:</b> 2471 * <p> 2472 * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of 2473 * elements returned by the command, so if M is constant (for instance you always ask for the 2474 * first ten elements with LIMIT) you can consider it O(log(N)) 2475 * @see #zrangeByScore(const(ubyte)[], double, double) 2476 * @see #zrangeByScore(const(ubyte)[], double, double, int, int) 2477 * @see #zrangeByScoreWithScores(const(ubyte)[], double, double) 2478 * @see #zrangeByScoreWithScores(const(ubyte)[], double, double, int, int) 2479 * @see #zcount(const(ubyte)[], double, double) 2480 * @param key 2481 * @param min 2482 * @param max 2483 * @return Multi bulk reply specifically a list of elements in the specified score range. 2484 */ 2485 // override 2486 Set!(Tuple) zrangeByScoreWithScores(const(ubyte)[] key, double min, double max) { 2487 checkIsInMultiOrPipeline(); 2488 client.zrangeByScoreWithScores(key, min, max); 2489 return getTupledSet(); 2490 } 2491 2492 // override 2493 Set!(Tuple) zrangeByScoreWithScores(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) { 2494 checkIsInMultiOrPipeline(); 2495 client.zrangeByScoreWithScores(key, min, max); 2496 return getTupledSet(); 2497 } 2498 2499 /** 2500 * Return the all the elements in the sorted set at key with a score between min and max 2501 * (including elements with score equal to min or max). 2502 * <p> 2503 * The elements having the same score are returned sorted lexicographically as ASCII strings (this 2504 * follows from a property of Redis sorted sets and does not involve further computation). 2505 * <p> 2506 * Using the optional {@link #zrangeByScore(const(ubyte)[], double, double, int, int) LIMIT} it's possible 2507 * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large 2508 * the commands needs to traverse the list for offset elements and this adds up to the O(M) 2509 * figure. 2510 * <p> 2511 * The {@link #zcount(const(ubyte)[], double, double) ZCOUNT} command is similar to 2512 * {@link #zrangeByScore(const(ubyte)[], double, double) ZRANGEBYSCORE} but instead of returning the 2513 * actual elements in the specified interval, it just returns the number of matching elements. 2514 * <p> 2515 * <b>Exclusive intervals and infinity</b> 2516 * <p> 2517 * min and max can be -inf and +inf, so that you are not required to know what's the greatest or 2518 * smallest element in order to take, for instance, elements "up to a given value". 2519 * <p> 2520 * Also while the interval is for default closed (inclusive) it's possible to specify open 2521 * intervals prefixing the score with a "(" character, so for instance: 2522 * <p> 2523 * {@code ZRANGEBYSCORE zset (1.3 5} 2524 * <p> 2525 * Will return all the values with score > 1.3 and <= 5, while for instance: 2526 * <p> 2527 * {@code ZRANGEBYSCORE zset (5 (10} 2528 * <p> 2529 * Will return all the values with score > 5 and < 10 (5 and 10 excluded). 2530 * <p> 2531 * <b>Time complexity:</b> 2532 * <p> 2533 * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of 2534 * elements returned by the command, so if M is constant (for instance you always ask for the 2535 * first ten elements with LIMIT) you can consider it O(log(N)) 2536 * @see #zrangeByScore(const(ubyte)[], double, double) 2537 * @see #zrangeByScore(const(ubyte)[], double, double, int, int) 2538 * @see #zrangeByScoreWithScores(const(ubyte)[], double, double) 2539 * @see #zrangeByScoreWithScores(const(ubyte)[], double, double, int, int) 2540 * @see #zcount(const(ubyte)[], double, double) 2541 * @param key 2542 * @param min 2543 * @param max 2544 * @param offset 2545 * @param count 2546 * @return Multi bulk reply specifically a list of elements in the specified score range. 2547 */ 2548 // override 2549 Set!(Tuple) zrangeByScoreWithScores(const(ubyte)[] key, double min, double max, 2550 int offset, int count) { 2551 checkIsInMultiOrPipeline(); 2552 client.zrangeByScoreWithScores(key, min, max, offset, count); 2553 return getTupledSet(); 2554 } 2555 2556 // override 2557 Set!(Tuple) zrangeByScoreWithScores(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max, 2558 int offset, int count) { 2559 checkIsInMultiOrPipeline(); 2560 client.zrangeByScoreWithScores(key, min, max, offset, count); 2561 return getTupledSet(); 2562 } 2563 2564 protected Set!(Tuple) getTupledSet() { 2565 List!(const(ubyte)[]) membersWithScores = client.getBinaryMultiBulkReply(); 2566 if (membersWithScores.isEmpty()) { 2567 return Collections.emptySet!(Tuple)(); 2568 } 2569 Set!(Tuple) set = new LinkedHashSet!(Tuple)(membersWithScores.size() / 2, 1.0f); 2570 InputRange!(const(ubyte)[]) iterator = membersWithScores.iterator(); 2571 while (!iterator.empty()) { 2572 const(ubyte)[] first = iterator.front(); iterator.popFront(); 2573 const(ubyte)[] second = iterator.front(); iterator.popFront(); 2574 Double d = BuilderFactory.DOUBLE.build(new Bytes(cast(byte[])second)); 2575 set.add(new Tuple(first, d.value())); 2576 } 2577 return set; 2578 } 2579 2580 // override 2581 Set!(const(ubyte)[]) zrevrangeByScore(const(ubyte)[] key, double max, double min) { 2582 checkIsInMultiOrPipeline(); 2583 client.zrevrangeByScore(key, max, min); 2584 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2585 } 2586 2587 // override 2588 Set!(const(ubyte)[]) zrevrangeByScore(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min) { 2589 checkIsInMultiOrPipeline(); 2590 client.zrevrangeByScore(key, max, min); 2591 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2592 } 2593 2594 // override 2595 Set!(const(ubyte)[]) zrevrangeByScore(const(ubyte)[] key, double max, double min, 2596 int offset, int count) { 2597 checkIsInMultiOrPipeline(); 2598 client.zrevrangeByScore(key, max, min, offset, count); 2599 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2600 } 2601 2602 // override 2603 Set!(const(ubyte)[]) zrevrangeByScore(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min, 2604 int offset, int count) { 2605 checkIsInMultiOrPipeline(); 2606 client.zrevrangeByScore(key, max, min, offset, count); 2607 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2608 } 2609 2610 // override 2611 Set!(Tuple) zrevrangeByScoreWithScores(const(ubyte)[] key, double max, double min) { 2612 checkIsInMultiOrPipeline(); 2613 client.zrevrangeByScoreWithScores(key, max, min); 2614 return getTupledSet(); 2615 } 2616 2617 // override 2618 Set!(Tuple) zrevrangeByScoreWithScores(const(ubyte)[] key, double max, 2619 double min, int offset, int count) { 2620 checkIsInMultiOrPipeline(); 2621 client.zrevrangeByScoreWithScores(key, max, min, offset, count); 2622 return getTupledSet(); 2623 } 2624 2625 // override 2626 Set!(Tuple) zrevrangeByScoreWithScores(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min) { 2627 checkIsInMultiOrPipeline(); 2628 client.zrevrangeByScoreWithScores(key, max, min); 2629 return getTupledSet(); 2630 } 2631 2632 // override 2633 Set!(Tuple) zrevrangeByScoreWithScores(const(ubyte)[] key, const(ubyte)[] max, 2634 const(ubyte)[] min, int offset, int count) { 2635 checkIsInMultiOrPipeline(); 2636 client.zrevrangeByScoreWithScores(key, max, min, offset, count); 2637 return getTupledSet(); 2638 } 2639 2640 /** 2641 * Remove all elements in the sorted set at key with rank between start and end. Start and end are 2642 * 0-based with rank 0 being the element with the lowest score. Both start and end can be negative 2643 * numbers, where they indicate offsets starting at the element with the highest rank. For 2644 * example: -1 is the element with the highest score, -2 the element with the second highest score 2645 * and so forth. 2646 * <p> 2647 * <b>Time complexity:</b> O(log(N))+O(M) with N being the number of elements in the sorted set 2648 * and M the number of elements removed by the operation 2649 * @param key 2650 * @param start 2651 * @param stop 2652 * @return 2653 */ 2654 // override 2655 Long zremrangeByRank(const(ubyte)[] key, long start, long stop) { 2656 checkIsInMultiOrPipeline(); 2657 client.zremrangeByRank(key, start, stop); 2658 return client.getIntegerReply(); 2659 } 2660 2661 /** 2662 * Remove all the elements in the sorted set at key with a score between min and max (including 2663 * elements with score equal to min or max). 2664 * <p> 2665 * <b>Time complexity:</b> 2666 * <p> 2667 * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of 2668 * elements removed by the operation 2669 * @param key 2670 * @param min 2671 * @param max 2672 * @return Integer reply, specifically the number of elements removed. 2673 */ 2674 // override 2675 Long zremrangeByScore(const(ubyte)[] key, double min, double max) { 2676 checkIsInMultiOrPipeline(); 2677 client.zremrangeByScore(key, min, max); 2678 return client.getIntegerReply(); 2679 } 2680 2681 // override 2682 Long zremrangeByScore(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) { 2683 checkIsInMultiOrPipeline(); 2684 client.zremrangeByScore(key, min, max); 2685 return client.getIntegerReply(); 2686 } 2687 2688 /** 2689 * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at 2690 * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys 2691 * and the other (optional) arguments. 2692 * <p> 2693 * As the terms imply, the {@link #zinterstore(const(ubyte)[], const(ubyte)[]...)} ZINTERSTORE} command requires 2694 * an element to be present in each of the given inputs to be inserted in the result. The {@link 2695 * #zunionstore(const(ubyte)[], const(ubyte)[]...)} command inserts all elements across all inputs. 2696 * <p> 2697 * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means 2698 * that the score of each element in the sorted set is first multiplied by this weight before 2699 * being passed to the aggregation. When this option is not given, all weights default to 1. 2700 * <p> 2701 * With the AGGREGATE option, it's possible to specify how the results of the union or 2702 * intersection are aggregated. This option defaults to SUM, where the score of an element is 2703 * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the 2704 * resulting set will contain the minimum or maximum score of an element across the inputs where 2705 * it exists. 2706 * <p> 2707 * <b>Time complexity:</b> O(N) + O(M log(M)) with N being the sum of the sizes of the input 2708 * sorted sets, and M being the number of elements in the resulting sorted set 2709 * @see #zunionstore(const(ubyte)[], const(ubyte)[]...) 2710 * @see #zunionstore(const(ubyte)[], ZParams, const(ubyte)[]...) 2711 * @see #zinterstore(const(ubyte)[], const(ubyte)[]...) 2712 * @see #zinterstore(const(ubyte)[], ZParams, const(ubyte)[]...) 2713 * @param dstkey 2714 * @param sets 2715 * @return Integer reply, specifically the number of elements in the sorted set at dstkey 2716 */ 2717 // override 2718 Long zunionstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...) { 2719 checkIsInMultiOrPipeline(); 2720 client.zunionstore(dstkey, sets); 2721 return client.getIntegerReply(); 2722 } 2723 2724 /** 2725 * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at 2726 * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys 2727 * and the other (optional) arguments. 2728 * <p> 2729 * As the terms imply, the {@link #zinterstore(const(ubyte)[], const(ubyte)[]...) ZINTERSTORE} command requires an 2730 * element to be present in each of the given inputs to be inserted in the result. The {@link 2731 * #zunionstore(const(ubyte)[], const(ubyte)[]...) ZUNIONSTORE} command inserts all elements across all inputs. 2732 * <p> 2733 * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means 2734 * that the score of each element in the sorted set is first multiplied by this weight before 2735 * being passed to the aggregation. When this option is not given, all weights default to 1. 2736 * <p> 2737 * With the AGGREGATE option, it's possible to specify how the results of the union or 2738 * intersection are aggregated. This option defaults to SUM, where the score of an element is 2739 * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the 2740 * resulting set will contain the minimum or maximum score of an element across the inputs where 2741 * it exists. 2742 * <p> 2743 * <b>Time complexity:</b> O(N) + O(M log(M)) with N being the sum of the sizes of the input 2744 * sorted sets, and M being the number of elements in the resulting sorted set 2745 * @see #zunionstore(const(ubyte)[], const(ubyte)[]...) 2746 * @see #zunionstore(const(ubyte)[], ZParams, const(ubyte)[]...) 2747 * @see #zinterstore(const(ubyte)[], const(ubyte)[]...) 2748 * @see #zinterstore(const(ubyte)[], ZParams, const(ubyte)[]...) 2749 * @param dstkey 2750 * @param sets 2751 * @param params 2752 * @return Integer reply, specifically the number of elements in the sorted set at dstkey 2753 */ 2754 // override 2755 Long zunionstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...) { 2756 checkIsInMultiOrPipeline(); 2757 client.zunionstore(dstkey, params, sets); 2758 return client.getIntegerReply(); 2759 } 2760 2761 /** 2762 * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at 2763 * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys 2764 * and the other (optional) arguments. 2765 * <p> 2766 * As the terms imply, the {@link #zinterstore(const(ubyte)[], const(ubyte)[]...) ZINTERSTORE} command requires an 2767 * element to be present in each of the given inputs to be inserted in the result. The {@link 2768 * #zunionstore(const(ubyte)[], const(ubyte)[]...) ZUNIONSTORE} command inserts all elements across all inputs. 2769 * <p> 2770 * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means 2771 * that the score of each element in the sorted set is first multiplied by this weight before 2772 * being passed to the aggregation. When this option is not given, all weights default to 1. 2773 * <p> 2774 * With the AGGREGATE option, it's possible to specify how the results of the union or 2775 * intersection are aggregated. This option defaults to SUM, where the score of an element is 2776 * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the 2777 * resulting set will contain the minimum or maximum score of an element across the inputs where 2778 * it exists. 2779 * <p> 2780 * <b>Time complexity:</b> O(N) + O(M log(M)) with N being the sum of the sizes of the input 2781 * sorted sets, and M being the number of elements in the resulting sorted set 2782 * @see #zunionstore(const(ubyte)[], const(ubyte)[]...) 2783 * @see #zunionstore(const(ubyte)[], ZParams, const(ubyte)[]...) 2784 * @see #zinterstore(const(ubyte)[], const(ubyte)[]...) 2785 * @see #zinterstore(const(ubyte)[], ZParams, const(ubyte)[]...) 2786 * @param dstkey 2787 * @param sets 2788 * @return Integer reply, specifically the number of elements in the sorted set at dstkey 2789 */ 2790 // override 2791 Long zinterstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...) { 2792 checkIsInMultiOrPipeline(); 2793 client.zinterstore(dstkey, sets); 2794 return client.getIntegerReply(); 2795 } 2796 2797 /** 2798 * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at 2799 * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys 2800 * and the other (optional) arguments. 2801 * <p> 2802 * As the terms imply, the {@link #zinterstore(const(ubyte)[], const(ubyte)[]...) ZINTERSTORE} command requires an 2803 * element to be present in each of the given inputs to be inserted in the result. The {@link 2804 * #zunionstore(const(ubyte)[], const(ubyte)[]...) ZUNIONSTORE} command inserts all elements across all inputs. 2805 * <p> 2806 * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means 2807 * that the score of each element in the sorted set is first multiplied by this weight before 2808 * being passed to the aggregation. When this option is not given, all weights default to 1. 2809 * <p> 2810 * With the AGGREGATE option, it's possible to specify how the results of the union or 2811 * intersection are aggregated. This option defaults to SUM, where the score of an element is 2812 * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the 2813 * resulting set will contain the minimum or maximum score of an element across the inputs where 2814 * it exists. 2815 * <p> 2816 * <b>Time complexity:</b> O(N) + O(M log(M)) with N being the sum of the sizes of the input 2817 * sorted sets, and M being the number of elements in the resulting sorted set 2818 * @see #zunionstore(const(ubyte)[], const(ubyte)[]...) 2819 * @see #zunionstore(const(ubyte)[], ZParams, const(ubyte)[]...) 2820 * @see #zinterstore(const(ubyte)[], const(ubyte)[]...) 2821 * @see #zinterstore(const(ubyte)[], ZParams, const(ubyte)[]...) 2822 * @param dstkey 2823 * @param sets 2824 * @param params 2825 * @return Integer reply, specifically the number of elements in the sorted set at dstkey 2826 */ 2827 // override 2828 Long zinterstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...) { 2829 checkIsInMultiOrPipeline(); 2830 client.zinterstore(dstkey, params, sets); 2831 return client.getIntegerReply(); 2832 } 2833 2834 // override 2835 Long zlexcount(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) { 2836 checkIsInMultiOrPipeline(); 2837 client.zlexcount(key, min, max); 2838 return client.getIntegerReply(); 2839 } 2840 2841 // override 2842 Set!(const(ubyte)[]) zrangeByLex(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) { 2843 checkIsInMultiOrPipeline(); 2844 client.zrangeByLex(key, min, max); 2845 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2846 } 2847 2848 // override 2849 Set!(const(ubyte)[]) zrangeByLex(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max, 2850 int offset, int count) { 2851 checkIsInMultiOrPipeline(); 2852 client.zrangeByLex(key, min, max, offset, count); 2853 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2854 } 2855 2856 // override 2857 Set!(const(ubyte)[]) zrevrangeByLex(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min) { 2858 checkIsInMultiOrPipeline(); 2859 client.zrevrangeByLex(key, max, min); 2860 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2861 } 2862 2863 // override 2864 Set!(const(ubyte)[]) zrevrangeByLex(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min, int offset, int count) { 2865 checkIsInMultiOrPipeline(); 2866 client.zrevrangeByLex(key, max, min, offset, count); 2867 return new SetFromList!(const(ubyte)[])(client.getBinaryMultiBulkReply()); 2868 } 2869 2870 // override 2871 Long zremrangeByLex(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) { 2872 checkIsInMultiOrPipeline(); 2873 client.zremrangeByLex(key, min, max); 2874 return client.getIntegerReply(); 2875 } 2876 2877 /** 2878 * Synchronously save the DB on disk. 2879 * <p> 2880 * Save the whole dataset on disk (this means that all the databases are saved, as well as keys 2881 * with an EXPIRE set (the expire is preserved). The server hangs while the saving is not 2882 * completed, no connection is served in the meanwhile. An OK code is returned when the DB was 2883 * fully stored in disk. 2884 * <p> 2885 * The background variant of this command is {@link #bgsave() BGSAVE} that is able to perform the 2886 * saving in the background while the server continues serving other clients. 2887 * <p> 2888 * @return Status code reply 2889 */ 2890 // override 2891 string save() { 2892 client.save(); 2893 return client.getStatusCodeReply(); 2894 } 2895 2896 /** 2897 * Asynchronously save the DB on disk. 2898 * <p> 2899 * Save the DB in background. The OK code is immediately returned. Redis forks, the parent 2900 * continues to server the clients, the child saves the DB on disk then exit. A client my be able 2901 * to check if the operation succeeded using the LASTSAVE command. 2902 * @return Status code reply 2903 */ 2904 // override 2905 string bgsave() { 2906 client.bgsave(); 2907 return client.getStatusCodeReply(); 2908 } 2909 2910 /** 2911 * Rewrite the append only file in background when it gets too big. Please for detailed 2912 * information about the Redis Append Only File check the <a 2913 * href="http://redis.io/topics/persistence#append-only-file">Append Only File Howto</a>. 2914 * <p> 2915 * BGREWRITEAOF rewrites the Append Only File in background when it gets too big. The Redis Append 2916 * Only File is a Journal, so every operation modifying the dataset is logged in the Append Only 2917 * File (and replayed at startup). This means that the Append Only File always grows. In order to 2918 * rebuild its content the BGREWRITEAOF creates a new version of the append only file starting 2919 * directly form the dataset in memory in order to guarantee the generation of the minimal number 2920 * of commands needed to rebuild the database. 2921 * <p> 2922 * @return Status code reply 2923 */ 2924 // override 2925 string bgrewriteaof() { 2926 client.bgrewriteaof(); 2927 return client.getStatusCodeReply(); 2928 } 2929 2930 /** 2931 * Return the UNIX time stamp of the last successfully saving of the dataset on disk. 2932 * <p> 2933 * Return the UNIX TIME of the last DB save executed with success. A client may check if a 2934 * {@link #bgsave() BGSAVE} command succeeded reading the LASTSAVE value, then issuing a BGSAVE 2935 * command and checking at regular intervals every N seconds if LASTSAVE changed. 2936 * @return Integer reply, specifically an UNIX time stamp. 2937 */ 2938 // override 2939 Long lastsave() { 2940 client.lastsave(); 2941 return client.getIntegerReply(); 2942 } 2943 2944 /** 2945 * Synchronously save the DB on disk, then shutdown the server. 2946 * <p> 2947 * Stop all the clients, save the DB, then quit the server. This commands makes sure that the DB 2948 * is switched off without the lost of any data. This is not guaranteed if the client uses simply 2949 * {@link #save() SAVE} and then {@link #quit() QUIT} because other clients may alter the DB data 2950 * between the two commands. 2951 * @return Status code reply on error. On success nothing is returned since the server quits and 2952 * the connection is closed. 2953 */ 2954 // override 2955 string shutdown() { 2956 client.shutdown(); 2957 string status; 2958 try { 2959 status = client.getStatusCodeReply(); 2960 } catch (RedisException ex) { 2961 status = null; 2962 } 2963 return status; 2964 } 2965 2966 /** 2967 * Provide information and statistics about the server. 2968 * <p> 2969 * The info command returns different information and statistics about the server in an format 2970 * that's simple to parse by computers and easy to read by humans. 2971 * <p> 2972 * <b>Format of the returned string:</b> 2973 * <p> 2974 * All the fields are in the form field:value 2975 * 2976 * <pre> 2977 * edis_version:0.07 2978 * connected_clients:1 2979 * connected_slaves:0 2980 * used_memory:3187 2981 * changes_since_last_save:0 2982 * last_save_time:1237655729 2983 * total_connections_received:1 2984 * total_commands_processed:1 2985 * uptime_in_seconds:25 2986 * uptime_in_days:0 2987 * </pre> 2988 * 2989 * <b>Notes</b> 2990 * <p> 2991 * used_memory is returned in bytes, and is the total number of bytes allocated by the program 2992 * using malloc. 2993 * <p> 2994 * uptime_in_days is redundant since the uptime in seconds contains already the full uptime 2995 * information, this field is only mainly present for humans. 2996 * <p> 2997 * changes_since_last_save does not refer to the number of key changes, but to the number of 2998 * operations that produced some kind of change in the dataset. 2999 * <p> 3000 * @return Bulk reply 3001 */ 3002 // override 3003 string info() { 3004 client.info(); 3005 return client.getBulkReply(); 3006 } 3007 3008 // override 3009 string info(string section) { 3010 client.info(section); 3011 return client.getBulkReply(); 3012 } 3013 3014 /** 3015 * Dump all the received requests in real time. 3016 * <p> 3017 * MONITOR is a debugging command that outputs the whole sequence of commands received by the 3018 * Redis server. is very handy in order to understand what is happening into the database. This 3019 * command is used directly via telnet. 3020 * @param jedisMonitor 3021 */ 3022 void monitor(RedisMonitor jedisMonitor) { 3023 client.monitor(); 3024 client.getStatusCodeReply(); 3025 jedisMonitor.proceed(client); 3026 } 3027 3028 /** 3029 * Change the replication settings. 3030 * <p> 3031 * The SLAVEOF command can change the replication settings of a slave on the fly. If a Redis 3032 * server is already acting as slave, the command SLAVEOF NO ONE will turn off the replication 3033 * turning the Redis server into a MASTER. In the proper form SLAVEOF hostname port will make the 3034 * server a slave of the specific server listening at the specified hostname and port. 3035 * <p> 3036 * If a server is already a slave of some master, SLAVEOF hostname port will stop the replication 3037 * against the old server and start the synchronization against the new one discarding the old 3038 * dataset. 3039 * <p> 3040 * The form SLAVEOF no one will stop replication turning the server into a MASTER but will not 3041 * discard the replication. So if the old master stop working it is possible to turn the slave 3042 * into a master and set the application to use the new master in read/write. Later when the other 3043 * Redis server will be fixed it can be configured in order to work as slave. 3044 * <p> 3045 * @param host 3046 * @param port 3047 * @return Status code reply 3048 */ 3049 // override 3050 string slaveof(string host, int port) { 3051 client.slaveof(host, port); 3052 return client.getStatusCodeReply(); 3053 } 3054 3055 // override 3056 string slaveofNoOne() { 3057 client.slaveofNoOne(); 3058 return client.getStatusCodeReply(); 3059 } 3060 3061 /** 3062 * Retrieve the configuration of a running Redis server. Not all the configuration parameters are 3063 * supported. 3064 * <p> 3065 * CONFIG GET returns the current configuration parameters. This sub command only accepts a single 3066 * argument, that is glob style pattern. All the configuration parameters matching this parameter 3067 * are reported as a list of key-value pairs. 3068 * <p> 3069 * <b>Example:</b> 3070 * 3071 * <pre> 3072 * $ redis-cli config get '*' 3073 * 1. "dbfilename" 3074 * 2. "dump.rdb" 3075 * 3. "requirepass" 3076 * 4. (nil) 3077 * 5. "masterauth" 3078 * 6. (nil) 3079 * 7. "maxmemory" 3080 * 8. "0\n" 3081 * 9. "appendfsync" 3082 * 10. "everysec" 3083 * 11. "save" 3084 * 12. "3600 1 300 100 60 10000" 3085 * 3086 * $ redis-cli config get 'm*' 3087 * 1. "masterauth" 3088 * 2. (nil) 3089 * 3. "maxmemory" 3090 * 4. "0\n" 3091 * </pre> 3092 * @param pattern 3093 * @return Bulk reply. 3094 */ 3095 // override 3096 List!(const(ubyte)[]) configGet(const(ubyte)[] pattern) { 3097 checkIsInMultiOrPipeline(); 3098 client.configGet(pattern); 3099 return client.getBinaryMultiBulkReply(); 3100 } 3101 3102 /** 3103 * Reset the stats returned by INFO 3104 * @return 3105 */ 3106 // override 3107 string configResetStat() { 3108 checkIsInMultiOrPipeline(); 3109 client.configResetStat(); 3110 return client.getStatusCodeReply(); 3111 } 3112 3113 /** 3114 * The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying 3115 * the minimal changes needed to make it reflect the configuration currently used by the server, 3116 * which may be different compared to the original one because of the use of the CONFIG SET command. 3117 * 3118 * The rewrite is performed in a very conservative way: 3119 * <ul> 3120 * <li>Comments and the overall structure of the original redis.conf are preserved as much as possible.</li> 3121 * <li>If an option already exists in the old redis.conf file, it will be rewritten at the same position (line number).</li> 3122 * <li>If an option was not already present, but it is set to its default value, it is not added by the rewrite process.</li> 3123 * <li>If an option was not already present, but it is set to a non-default value, it is appended at the end of the file.</li> 3124 * <li>Non used lines are blanked. For instance if you used to have multiple save directives, but 3125 * the current configuration has fewer or none as you disabled RDB persistence, all the lines will be blanked.</li> 3126 * </ul> 3127 * 3128 * CONFIG REWRITE is also able to rewrite the configuration file from scratch if the original one 3129 * no longer exists for some reason. However if the server was started without a configuration 3130 * file at all, the CONFIG REWRITE will just return an error. 3131 * @return OK when the configuration was rewritten properly. Otherwise an error is returned. 3132 */ 3133 // override 3134 string configRewrite() { 3135 checkIsInMultiOrPipeline(); 3136 client.configRewrite(); 3137 return client.getStatusCodeReply(); 3138 } 3139 3140 /** 3141 * Alter the configuration of a running Redis server. Not all the configuration parameters are 3142 * supported. 3143 * <p> 3144 * The list of configuration parameters supported by CONFIG SET can be obtained issuing a 3145 * {@link #configGet(const(ubyte)[]) CONFIG GET *} command. 3146 * <p> 3147 * The configuration set using CONFIG SET is immediately loaded by the Redis server that will 3148 * start acting as specified starting from the next command. 3149 * <p> 3150 * <b>Parameters value format</b> 3151 * <p> 3152 * The value of the configuration parameter is the same as the one of the same parameter in the 3153 * Redis configuration file, with the following exceptions: 3154 * <p> 3155 * <ul> 3156 * <li>The save parameter is a list of space-separated integers. Every pair of integers specify the 3157 * time and number of changes limit to trigger a save. For instance the command CONFIG SET save 3158 * "3600 10 60 10000" will configure the server to issue a background saving of the RDB file every 3159 * 3600 seconds if there are at least 10 changes in the dataset, and every 60 seconds if there are 3160 * at least 10000 changes. To completely disable automatic snapshots just set the parameter as an 3161 * empty string. 3162 * <li>All the integer parameters representing memory are returned and accepted only using bytes 3163 * as unit. 3164 * </ul> 3165 * @param parameter 3166 * @param value 3167 * @return Status code reply 3168 */ 3169 // override 3170 const(ubyte)[] configSet(const(ubyte)[] parameter, const(ubyte)[] value) { 3171 checkIsInMultiOrPipeline(); 3172 client.configSet(parameter, value); 3173 return client.getBinaryBulkReply(); 3174 } 3175 3176 bool isConnected() { 3177 return client.isConnected(); 3178 } 3179 3180 // override 3181 Long strlen(const(ubyte)[] key) { 3182 checkIsInMultiOrPipeline(); 3183 client.strlen(key); 3184 return client.getIntegerReply(); 3185 } 3186 3187 void sync() { 3188 client.sync(); 3189 } 3190 3191 // override 3192 Long lpushx(const(ubyte)[] key, const(ubyte)[][] string...) { 3193 checkIsInMultiOrPipeline(); 3194 client.lpushx(key, string); 3195 return client.getIntegerReply(); 3196 } 3197 3198 /** 3199 * Undo a {@link #expire(const(ubyte)[], int) expire} at turning the expire key into a normal key. 3200 * <p> 3201 * Time complexity: O(1) 3202 * @param key 3203 * @return Integer reply, specifically: 1: the key is now persist. 0: the key is not persist (only 3204 * happens when key not set). 3205 */ 3206 // override 3207 Long persist(const(ubyte)[] key) { 3208 client.persist(key); 3209 return client.getIntegerReply(); 3210 } 3211 3212 // override 3213 Long rpushx(const(ubyte)[] key, const(ubyte)[][] string...) { 3214 checkIsInMultiOrPipeline(); 3215 client.rpushx(key, string); 3216 return client.getIntegerReply(); 3217 } 3218 3219 // override 3220 const(ubyte)[] echo(const(ubyte)[] string) { 3221 checkIsInMultiOrPipeline(); 3222 client.echo(string); 3223 return client.getBinaryBulkReply(); 3224 } 3225 3226 // override 3227 Long linsert(const(ubyte)[] key, ListPosition where, const(ubyte)[] pivot, 3228 const(ubyte)[] value) { 3229 checkIsInMultiOrPipeline(); 3230 client.linsert(key, where, pivot, value); 3231 return client.getIntegerReply(); 3232 } 3233 3234 // // override 3235 // string debug(DebugParams params) { 3236 // client.debug(params); 3237 // return client.getStatusCodeReply(); 3238 // } 3239 3240 Client getClient() { 3241 return client; 3242 } 3243 3244 /** 3245 * Pop a value from a list, push it to another list and return it; or block until one is available 3246 * @param source 3247 * @param destination 3248 * @param timeout 3249 * @return the element 3250 */ 3251 // override 3252 const(ubyte)[] brpoplpush(const(ubyte)[] source, const(ubyte)[] destination, int timeout) { 3253 client.brpoplpush(source, destination, timeout); 3254 client.setTimeoutInfinite(); 3255 try { 3256 return client.getBinaryBulkReply(); 3257 } finally { 3258 client.rollbackTimeout(); 3259 } 3260 } 3261 3262 /** 3263 * Sets or clears the bit at offset in the string value stored at key 3264 * @param key 3265 * @param offset 3266 * @param value 3267 * @return 3268 */ 3269 // override 3270 bool setbit(const(ubyte)[] key, long offset, bool value) { 3271 checkIsInMultiOrPipeline(); 3272 client.setbit(key, offset, value); 3273 return client.getIntegerReply() == 1; 3274 } 3275 3276 // override 3277 bool setbit(const(ubyte)[] key, long offset, const(ubyte)[] value) { 3278 checkIsInMultiOrPipeline(); 3279 client.setbit(key, offset, value); 3280 return client.getIntegerReply() == 1; 3281 } 3282 3283 /** 3284 * Returns the bit value at offset in the string value stored at key 3285 * @param key 3286 * @param offset 3287 * @return 3288 */ 3289 // override 3290 bool getbit(const(ubyte)[] key, long offset) { 3291 checkIsInMultiOrPipeline(); 3292 client.getbit(key, offset); 3293 return client.getIntegerReply() == 1; 3294 } 3295 3296 Long bitpos(const(ubyte)[] key, bool value) { 3297 return bitpos(key, value, new BitPosParams()); 3298 } 3299 3300 Long bitpos(const(ubyte)[] key, bool value, BitPosParams params) { 3301 checkIsInMultiOrPipeline(); 3302 client.bitpos(key, value, params); 3303 return client.getIntegerReply(); 3304 } 3305 3306 // override 3307 Long setrange(const(ubyte)[] key, long offset, const(ubyte)[] value) { 3308 checkIsInMultiOrPipeline(); 3309 client.setrange(key, offset, value); 3310 return client.getIntegerReply(); 3311 } 3312 3313 // override 3314 const(ubyte)[] getrange(const(ubyte)[] key, long startOffset, long endOffset) { 3315 checkIsInMultiOrPipeline(); 3316 client.getrange(key, startOffset, endOffset); 3317 return client.getBinaryBulkReply(); 3318 } 3319 3320 // override 3321 Long publish(const(ubyte)[] channel, const(ubyte)[] message) { 3322 checkIsInMultiOrPipeline(); 3323 client.publish(channel, message); 3324 return client.getIntegerReply(); 3325 } 3326 3327 // override 3328 void subscribe(BinaryRedisPubSub jedisPubSub, const(ubyte)[][] channels...) { 3329 client.setTimeoutInfinite(); 3330 try { 3331 jedisPubSub.proceed(client, channels); 3332 } finally { 3333 client.rollbackTimeout(); 3334 } 3335 } 3336 3337 // override 3338 void psubscribe(BinaryRedisPubSub jedisPubSub, const(ubyte)[][] patterns...) { 3339 client.setTimeoutInfinite(); 3340 try { 3341 jedisPubSub.proceedWithPatterns(client, patterns); 3342 } finally { 3343 client.rollbackTimeout(); 3344 } 3345 } 3346 3347 // override 3348 int getDB() { 3349 return client.getDB(); 3350 } 3351 3352 /** 3353 * Evaluates scripts using the Lua interpreter built into Redis starting from version 2.6.0. 3354 * <p> 3355 * @param script 3356 * @param keys 3357 * @param args 3358 * @return Script result 3359 */ 3360 // override 3361 Object eval(const(ubyte)[] script, List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args) { 3362 return eval(script, Protocol.toByteArray(keys.size()), getParamsWithBinary(keys, args)); 3363 } 3364 3365 static const(ubyte)[][] getParamsWithBinary(List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args) { 3366 int keyCount = keys.size(); 3367 int argCount = args.size(); 3368 const(ubyte)[][] params = new const(ubyte)[][keyCount + argCount]; 3369 3370 for (int i = 0; i < keyCount; i++) 3371 params[i] = keys.get(i); 3372 3373 for (int i = 0; i < argCount; i++) 3374 params[keyCount + i] = args.get(i); 3375 3376 return params; 3377 } 3378 3379 // override 3380 Object eval(const(ubyte)[] script, const(ubyte)[] keyCount, const(ubyte)[][] params...) { 3381 client.setTimeoutInfinite(); 3382 try { 3383 client.eval(script, keyCount, params); 3384 return client.getOne(); 3385 } finally { 3386 client.rollbackTimeout(); 3387 } 3388 } 3389 3390 // override 3391 Object eval(const(ubyte)[] script, int keyCount, const(ubyte)[][] params...) { 3392 return eval(script, Protocol.toByteArray(keyCount), params); 3393 } 3394 3395 // override 3396 Object eval(const(ubyte)[] script) { 3397 return eval(script, 0); 3398 } 3399 3400 // override 3401 Object evalsha(const(ubyte)[] sha1) { 3402 return evalsha(sha1, 0); 3403 } 3404 3405 // override 3406 Object evalsha(const(ubyte)[] sha1, List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args) { 3407 return evalsha(sha1, keys.size(), getParamsWithBinary(keys, args)); 3408 } 3409 3410 // override 3411 Object evalsha(const(ubyte)[] sha1, int keyCount, const(ubyte)[][] params...) { 3412 client.setTimeoutInfinite(); 3413 try { 3414 client.evalsha(sha1, keyCount, params); 3415 return client.getOne(); 3416 } finally { 3417 client.rollbackTimeout(); 3418 } 3419 } 3420 3421 // override 3422 string scriptFlush() { 3423 client.scriptFlush(); 3424 return client.getStatusCodeReply(); 3425 } 3426 3427 long scriptExists(const(ubyte)[] sha1) { 3428 const(ubyte)[][] a = new const(ubyte)[][1]; 3429 a[0] = sha1; 3430 return scriptExists(a).get(0); 3431 } 3432 3433 // override 3434 List!(long) scriptExists(const(ubyte)[][] sha1...) { 3435 client.scriptExists(sha1); 3436 return client.getIntegerMultiBulkReply(); 3437 } 3438 3439 // override 3440 const(ubyte)[] scriptLoad(const(ubyte)[] script) { 3441 client.scriptLoad(script); 3442 return client.getBinaryBulkReply(); 3443 } 3444 3445 // override 3446 string scriptKill() { 3447 client.scriptKill(); 3448 return client.getStatusCodeReply(); 3449 } 3450 3451 // override 3452 string slowlogReset() { 3453 client.slowlogReset(); 3454 return client.getBulkReply(); 3455 } 3456 3457 // override 3458 Long slowlogLen() { 3459 client.slowlogLen(); 3460 return client.getIntegerReply(); 3461 } 3462 3463 // override 3464 List!(const(ubyte)[]) slowlogGetBinary() { 3465 client.slowlogGet(); 3466 return client.getBinaryMultiBulkReply(); 3467 } 3468 3469 // override 3470 List!(const(ubyte)[]) slowlogGetBinary(long entries) { 3471 client.slowlogGet(entries); 3472 return client.getBinaryMultiBulkReply(); 3473 } 3474 3475 // override 3476 Long objectRefcount(const(ubyte)[] key) { 3477 client.objectRefcount(key); 3478 return client.getIntegerReply(); 3479 } 3480 3481 // override 3482 const(ubyte)[] objectEncoding(const(ubyte)[] key) { 3483 client.objectEncoding(key); 3484 return client.getBinaryBulkReply(); 3485 } 3486 3487 // override 3488 Long objectIdletime(const(ubyte)[] key) { 3489 client.objectIdletime(key); 3490 return client.getIntegerReply(); 3491 } 3492 3493 // override 3494 Long bitcount(const(ubyte)[] key) { 3495 checkIsInMultiOrPipeline(); 3496 client.bitcount(key); 3497 return client.getIntegerReply(); 3498 } 3499 3500 // override 3501 Long bitcount(const(ubyte)[] key, long start, long end) { 3502 checkIsInMultiOrPipeline(); 3503 client.bitcount(key, start, end); 3504 return client.getIntegerReply(); 3505 } 3506 3507 // override 3508 Long bitop(BitOP op, const(ubyte)[] destKey, const(ubyte)[][] srcKeys...) { 3509 checkIsInMultiOrPipeline(); 3510 client.bitop(op, destKey, srcKeys); 3511 return client.getIntegerReply(); 3512 } 3513 3514 // override 3515 const(ubyte)[] dump(const(ubyte)[] key) { 3516 checkIsInMultiOrPipeline(); 3517 client.dump(key); 3518 return client.getBinaryBulkReply(); 3519 } 3520 3521 // override 3522 string restore(const(ubyte)[] key, int ttl, const(ubyte)[] serializedValue) { 3523 checkIsInMultiOrPipeline(); 3524 client.restore(key, ttl, serializedValue); 3525 return client.getStatusCodeReply(); 3526 } 3527 3528 // override 3529 string restoreReplace(const(ubyte)[] key, int ttl, const(ubyte)[] serializedValue) { 3530 checkIsInMultiOrPipeline(); 3531 client.restoreReplace(key, ttl, serializedValue); 3532 return client.getStatusCodeReply(); 3533 } 3534 3535 /** 3536 * Set a timeout on the specified key. After the timeout the key will be automatically deleted by 3537 * the server. A key with an associated timeout is said to be volatile in Redis terminology. 3538 * <p> 3539 * Volatile keys are stored on disk like the other keys, the timeout is persistent too like all the 3540 * other aspects of the dataset. Saving a dataset containing expires and stopping the server does 3541 * not stop the flow of time as Redis stores on disk the time when the key will no longer be 3542 * available as Unix time, and not the remaining milliseconds. 3543 * <p> 3544 * Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire 3545 * set. It is also possible to undo the expire at all turning the key into a normal key using the 3546 * {@link #persist(const(ubyte)[]) PERSIST} command. 3547 * <p> 3548 * Time complexity: O(1) 3549 * @see <a href="http://redis.io/commands/pexpire">PEXPIRE Command</a> 3550 * @param key 3551 * @param milliseconds 3552 * @return Integer reply, specifically: 1: the timeout was set. 0: the timeout was not set since 3553 * the key already has an associated timeout (this may happen only in Redis versions < 3554 * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. 3555 */ 3556 // override 3557 Long pexpire(const(ubyte)[] key, long milliseconds) { 3558 checkIsInMultiOrPipeline(); 3559 client.pexpire(key, milliseconds); 3560 return client.getIntegerReply(); 3561 } 3562 3563 // override 3564 Long pexpireAt(const(ubyte)[] key, long millisecondsTimestamp) { 3565 checkIsInMultiOrPipeline(); 3566 client.pexpireAt(key, millisecondsTimestamp); 3567 return client.getIntegerReply(); 3568 } 3569 3570 // override 3571 Long pttl(const(ubyte)[] key) { 3572 checkIsInMultiOrPipeline(); 3573 client.pttl(key); 3574 return client.getIntegerReply(); 3575 } 3576 3577 /** 3578 * PSETEX works exactly like {@link #setex(const(ubyte)[], int, const(ubyte)[])} with the sole difference that the 3579 * expire time is specified in milliseconds instead of seconds. Time complexity: O(1) 3580 * @param key 3581 * @param milliseconds 3582 * @param value 3583 * @return Status code reply 3584 */ 3585 // override 3586 string psetex(const(ubyte)[] key, long milliseconds, const(ubyte)[] value) { 3587 checkIsInMultiOrPipeline(); 3588 client.psetex(key, milliseconds, value); 3589 return client.getStatusCodeReply(); 3590 } 3591 3592 // override 3593 const(ubyte)[] memoryDoctorBinary() { 3594 checkIsInMultiOrPipeline(); 3595 client.memoryDoctor(); 3596 return client.getBinaryBulkReply(); 3597 } 3598 3599 // override 3600 string clientKill(const(ubyte)[] ipPort) { 3601 checkIsInMultiOrPipeline(); 3602 this.client.clientKill(ipPort); 3603 return this.client.getStatusCodeReply(); 3604 } 3605 3606 // override 3607 string clientKill(string ip, int port) { 3608 checkIsInMultiOrPipeline(); 3609 this.client.clientKill(ip, port); 3610 return this.client.getStatusCodeReply(); 3611 } 3612 3613 // override 3614 Long clientKill(ClientKillParams params) { 3615 checkIsInMultiOrPipeline(); 3616 this.client.clientKill(params); 3617 return this.client.getIntegerReply(); 3618 } 3619 3620 // override 3621 const(ubyte)[] clientGetnameBinary() { 3622 checkIsInMultiOrPipeline(); 3623 client.clientGetname(); 3624 return client.getBinaryBulkReply(); 3625 } 3626 3627 // override 3628 const(ubyte)[] clientListBinary() { 3629 checkIsInMultiOrPipeline(); 3630 client.clientList(); 3631 return client.getBinaryBulkReply(); 3632 } 3633 3634 // override 3635 string clientSetname(const(ubyte)[] name) { 3636 checkIsInMultiOrPipeline(); 3637 client.clientSetname(name); 3638 return client.getBulkReply(); 3639 } 3640 3641 string clientPause(long timeout) { 3642 checkIsInMultiOrPipeline(); 3643 client.clientPause(timeout); 3644 return client.getBulkReply(); 3645 } 3646 3647 List!(string) time() { 3648 checkIsInMultiOrPipeline(); 3649 client.time(); 3650 return client.getMultiBulkReply(); 3651 } 3652 3653 // override 3654 string migrate(string host, int port, const(ubyte)[] key, 3655 int destinationDb, int timeout) { 3656 checkIsInMultiOrPipeline(); 3657 client.migrate(host, port, key, destinationDb, timeout); 3658 return client.getStatusCodeReply(); 3659 } 3660 3661 // override 3662 string migrate(string host, int port, int destinationDB, 3663 int timeout, MigrateParams params, const(ubyte)[][] keys...) { 3664 checkIsInMultiOrPipeline(); 3665 client.migrate(host, port, destinationDB, timeout, params, keys); 3666 return client.getStatusCodeReply(); 3667 } 3668 3669 /** 3670 * Syncrhonous replication of Redis as described here: http://antirez.com/news/66 Since Java 3671 * Object class has implemented "wait" method, we cannot use it, so I had to change the name of 3672 * the method. Sorry :S 3673 */ 3674 // override 3675 Long waitReplicas(int replicas, long timeout) { 3676 checkIsInMultiOrPipeline(); 3677 client.waitReplicas(replicas, timeout); 3678 return client.getIntegerReply(); 3679 } 3680 3681 // override 3682 Long pfadd(const(ubyte)[] key, const(ubyte)[][] elements...) { 3683 checkIsInMultiOrPipeline(); 3684 client.pfadd(key, elements); 3685 return client.getIntegerReply(); 3686 } 3687 3688 // override 3689 Long pfcount(const(ubyte)[] key) { 3690 checkIsInMultiOrPipeline(); 3691 client.pfcount(key); 3692 return client.getIntegerReply(); 3693 } 3694 3695 // override 3696 string pfmerge(const(ubyte)[] destkey, const(ubyte)[][] sourcekeys...) { 3697 checkIsInMultiOrPipeline(); 3698 client.pfmerge(destkey, sourcekeys); 3699 return client.getStatusCodeReply(); 3700 } 3701 3702 // override 3703 Long pfcount(const(ubyte)[][] keys...) { 3704 checkIsInMultiOrPipeline(); 3705 client.pfcount(keys); 3706 return client.getIntegerReply(); 3707 } 3708 3709 ScanResult!(const(ubyte)[]) scan(const(ubyte)[] cursor) { 3710 return scan(cursor, new ScanParams()); 3711 } 3712 3713 ScanResult!(const(ubyte)[]) scan(const(ubyte)[] cursor, ScanParams params) { 3714 checkIsInMultiOrPipeline(); 3715 client.scan(cursor, params); 3716 List!(Object) result = client.getObjectMultiBulkReply(); 3717 // const(ubyte)[] newcursor = (const(ubyte)[]) result.get(0); 3718 // List!(const(ubyte)[]) rawResults = (List!(const(ubyte)[])) result.get(1); 3719 // return new ScanResult!(const(ubyte)[])(newcursor, rawResults); 3720 implementationMissing(); 3721 return null; 3722 } 3723 3724 // override 3725 ScanResult!(MapEntry!(const(ubyte)[], const(ubyte)[])) hscan(const(ubyte)[] key, const(ubyte)[] cursor) { 3726 return hscan(key, cursor, new ScanParams()); 3727 } 3728 3729 // override 3730 ScanResult!(MapEntry!(const(ubyte)[], const(ubyte)[])) hscan(const(ubyte)[] key, const(ubyte)[] cursor, 3731 ScanParams params) { 3732 checkIsInMultiOrPipeline(); 3733 client.hscan(key, cursor, params); 3734 // List!(Object) result = client.getObjectMultiBulkReply(); 3735 // const(ubyte)[] newcursor = cast(const(ubyte)[]) result.get(0); 3736 // List!(MapEntry!(const(ubyte)[], const(ubyte)[])) results = new ArrayList!(MapEntry!(const(ubyte)[], const(ubyte)[]))(); 3737 // List!(const(ubyte)[]) rawResults = cast(List!(const(ubyte)[])) result.get(1); 3738 // Iterator!(const(ubyte)[]) iterator = rawResults.iterator(); 3739 // while (iterator.hasNext()) { 3740 // results.add(new AbstractMap.SimpleEntry!(const(ubyte)[], const(ubyte)[])(iterator.next(), iterator.next())); 3741 // } 3742 // return new ScanResult!(MapEntry!(const(ubyte)[], const(ubyte)[]))(newcursor, results); 3743 3744 implementationMissing(); 3745 return null; 3746 } 3747 3748 // override 3749 ScanResult!(const(ubyte)[]) sscan(const(ubyte)[] key, const(ubyte)[] cursor) { 3750 return sscan(key, cursor, new ScanParams()); 3751 } 3752 3753 // override 3754 ScanResult!(const(ubyte)[]) sscan(const(ubyte)[] key, const(ubyte)[] cursor, ScanParams params) { 3755 checkIsInMultiOrPipeline(); 3756 client.sscan(key, cursor, params); 3757 // List!(Object) result = client.getObjectMultiBulkReply(); 3758 // const(ubyte)[] newcursor = (const(ubyte)[]) result.get(0); 3759 // List!(const(ubyte)[]) rawResults = (List!(const(ubyte)[])) result.get(1); 3760 // return new ScanResult<>(newcursor, rawResults); 3761 3762 implementationMissing(); 3763 return null; 3764 } 3765 3766 // override 3767 ScanResult!(Tuple) zscan(const(ubyte)[] key, const(ubyte)[] cursor) { 3768 return zscan(key, cursor, new ScanParams()); 3769 } 3770 3771 // override 3772 ScanResult!(Tuple) zscan(const(ubyte)[] key, const(ubyte)[] cursor, ScanParams params) { 3773 checkIsInMultiOrPipeline(); 3774 client.zscan(key, cursor, params); 3775 List!(Object) result = client.getObjectMultiBulkReply(); 3776 // const(ubyte)[] newcursor = (const(ubyte)[]) result.get(0); 3777 // List!(Tuple) results = new ArrayList<>(); 3778 // List!(const(ubyte)[]) rawResults = (List!(const(ubyte)[])) result.get(1); 3779 // Iterator!(const(ubyte)[]) iterator = rawResults.iterator(); 3780 // while (iterator.hasNext()) { 3781 // results.add(new Tuple(iterator.next(), BuilderFactory.DOUBLE.build(iterator.next()))); 3782 // } 3783 // return new ScanResult<>(newcursor, results); 3784 3785 implementationMissing(); 3786 return null; 3787 } 3788 3789 // override 3790 Long geoadd(const(ubyte)[] key, double longitude, double latitude, const(ubyte)[] member) { 3791 checkIsInMultiOrPipeline(); 3792 client.geoadd(key, longitude, latitude, member); 3793 return client.getIntegerReply(); 3794 } 3795 3796 // override 3797 Long geoadd(const(ubyte)[] key, Map!(const(ubyte)[], GeoCoordinate) memberCoordinateMap) { 3798 checkIsInMultiOrPipeline(); 3799 client.geoadd(key, memberCoordinateMap); 3800 return client.getIntegerReply(); 3801 } 3802 3803 // override 3804 Double geodist(const(ubyte)[] key, const(ubyte)[] member1, const(ubyte)[] member2) { 3805 checkIsInMultiOrPipeline(); 3806 client.geodist(key, member1, member2); 3807 string dval = client.getBulkReply(); 3808 return (dval !is null ? new Double(dval) : null); 3809 } 3810 3811 // override 3812 Double geodist(const(ubyte)[] key, const(ubyte)[] member1, const(ubyte)[] member2, GeoUnit unit) { 3813 checkIsInMultiOrPipeline(); 3814 client.geodist(key, member1, member2, unit); 3815 string dval = client.getBulkReply(); 3816 return (dval !is null ? new Double(dval) : null); 3817 } 3818 3819 // override 3820 List!(const(ubyte)[]) geohash(const(ubyte)[] key, const(ubyte)[][] members...) { 3821 checkIsInMultiOrPipeline(); 3822 client.geohash(key, members); 3823 return client.getBinaryMultiBulkReply(); 3824 } 3825 3826 // override 3827 List!(GeoCoordinate) geopos(const(ubyte)[] key, const(ubyte)[][] members...) { 3828 checkIsInMultiOrPipeline(); 3829 client.geopos(key, members); 3830 return BuilderFactory.GEO_COORDINATE_LIST.build(cast(Object)client.getObjectMultiBulkReply()); 3831 } 3832 3833 // override 3834 List!(GeoRadiusResponse) georadius(const(ubyte)[] key, double longitude, double latitude, 3835 double radius, GeoUnit unit) { 3836 checkIsInMultiOrPipeline(); 3837 client.georadius(key, longitude, latitude, radius, unit); 3838 return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(cast(Object)client.getObjectMultiBulkReply()); 3839 } 3840 3841 // override 3842 List!(GeoRadiusResponse) georadiusReadonly(const(ubyte)[] key, double longitude, double latitude, 3843 double radius, GeoUnit unit) { 3844 checkIsInMultiOrPipeline(); 3845 client.georadiusReadonly(key, longitude, latitude, radius, unit); 3846 return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(cast(Object)client.getObjectMultiBulkReply()); 3847 } 3848 3849 // override 3850 List!(GeoRadiusResponse) georadius(const(ubyte)[] key, double longitude, double latitude, 3851 double radius, GeoUnit unit, GeoRadiusParam param) { 3852 checkIsInMultiOrPipeline(); 3853 client.georadius(key, longitude, latitude, radius, unit, param); 3854 return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(cast(Object)client.getObjectMultiBulkReply()); 3855 } 3856 3857 // override 3858 List!(GeoRadiusResponse) georadiusReadonly(const(ubyte)[] key, double longitude, double latitude, 3859 double radius, GeoUnit unit, GeoRadiusParam param) { 3860 checkIsInMultiOrPipeline(); 3861 client.georadiusReadonly(key, longitude, latitude, radius, unit, param); 3862 return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(cast(Object)client.getObjectMultiBulkReply()); 3863 } 3864 3865 // override 3866 List!(GeoRadiusResponse) georadiusByMember(const(ubyte)[] key, const(ubyte)[] member, double radius, 3867 GeoUnit unit) { 3868 checkIsInMultiOrPipeline(); 3869 client.georadiusByMember(key, member, radius, unit); 3870 return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(cast(Object)client.getObjectMultiBulkReply()); 3871 } 3872 3873 // override 3874 List!(GeoRadiusResponse) georadiusByMemberReadonly(const(ubyte)[] key, const(ubyte)[] member, double radius, 3875 GeoUnit unit) { 3876 checkIsInMultiOrPipeline(); 3877 client.georadiusByMemberReadonly(key, member, radius, unit); 3878 return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(cast(Object)client.getObjectMultiBulkReply()); 3879 } 3880 3881 // override 3882 List!(GeoRadiusResponse) georadiusByMember(const(ubyte)[] key, const(ubyte)[] member, double radius, 3883 GeoUnit unit, GeoRadiusParam param) { 3884 checkIsInMultiOrPipeline(); 3885 client.georadiusByMember(key, member, radius, unit, param); 3886 return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(cast(Object)client.getObjectMultiBulkReply()); 3887 } 3888 3889 // override 3890 List!(GeoRadiusResponse) georadiusByMemberReadonly(const(ubyte)[] key, const(ubyte)[] member, double radius, 3891 GeoUnit unit, GeoRadiusParam param) { 3892 checkIsInMultiOrPipeline(); 3893 client.georadiusByMemberReadonly(key, member, radius, unit, param); 3894 return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(cast(Object)client.getObjectMultiBulkReply()); 3895 } 3896 3897 3898 // override 3899 List!(long) bitfield(const(ubyte)[] key, const(ubyte)[][] arguments...) { 3900 checkIsInMultiOrPipeline(); 3901 client.bitfield(key, arguments); 3902 return client.getIntegerMultiBulkReply(); 3903 } 3904 3905 // override 3906 Long hstrlen(const(ubyte)[] key, const(ubyte)[] field) { 3907 checkIsInMultiOrPipeline(); 3908 client.hstrlen(key, field); 3909 return client.getIntegerReply(); 3910 } 3911 3912 // override 3913 List!(const(ubyte)[]) xread(int count, long block, Map!(const(ubyte)[], const(ubyte)[]) streams) { 3914 checkIsInMultiOrPipeline(); 3915 client.xread(count, block, streams); 3916 return client.getBinaryMultiBulkReply(); 3917 } 3918 3919 // override 3920 List!(const(ubyte)[]) xreadGroup(const(ubyte)[] groupname, const(ubyte)[] consumer, int count, long block, bool noAck, 3921 Map!(const(ubyte)[], const(ubyte)[]) streams) { 3922 checkIsInMultiOrPipeline(); 3923 client.xreadGroup(groupname, consumer, count, block, noAck, streams); 3924 return client.getBinaryMultiBulkReply(); 3925 } 3926 3927 // override 3928 const(ubyte)[] xadd(const(ubyte)[] key, const(ubyte)[] id, Map!(const(ubyte)[], const(ubyte)[]) hash, long maxLen, bool approximateLength) { 3929 checkIsInMultiOrPipeline(); 3930 client.xadd(key, id, hash, maxLen, approximateLength); 3931 return client.getBinaryBulkReply(); 3932 } 3933 3934 // override 3935 Long xlen(const(ubyte)[] key) { 3936 checkIsInMultiOrPipeline(); 3937 client.xlen(key); 3938 return client.getIntegerReply(); 3939 } 3940 3941 // override 3942 List!(const(ubyte)[]) xrange(const(ubyte)[] key, const(ubyte)[] start, const(ubyte)[] end, long count) { 3943 checkIsInMultiOrPipeline(); 3944 client.xrange(key, start, end, count); 3945 return client.getBinaryMultiBulkReply(); 3946 } 3947 3948 // override 3949 List!(const(ubyte)[]) xrevrange(const(ubyte)[] key, const(ubyte)[] end, const(ubyte)[] start, int count) { 3950 checkIsInMultiOrPipeline(); 3951 client.xrevrange(key, end, start, count); 3952 return client.getBinaryMultiBulkReply(); 3953 } 3954 3955 // override 3956 Long xack(const(ubyte)[] key, const(ubyte)[] group, const(ubyte)[][] ids...) { 3957 checkIsInMultiOrPipeline(); 3958 client.xack(key, group, ids); 3959 return client.getIntegerReply(); 3960 } 3961 3962 // override 3963 string xgroupCreate(const(ubyte)[] key, const(ubyte)[] consumer, const(ubyte)[] id, bool makeStream) { 3964 checkIsInMultiOrPipeline(); 3965 client.xgroupCreate(key, consumer, id, makeStream); 3966 return client.getStatusCodeReply(); 3967 } 3968 3969 // override 3970 string xgroupSetID(const(ubyte)[] key, const(ubyte)[] consumer, const(ubyte)[] id) { 3971 checkIsInMultiOrPipeline(); 3972 client.xgroupSetID(key, consumer, id); 3973 return client.getStatusCodeReply(); 3974 } 3975 3976 // override 3977 Long xgroupDestroy(const(ubyte)[] key, const(ubyte)[] consumer) { 3978 checkIsInMultiOrPipeline(); 3979 client.xgroupDestroy(key, consumer); 3980 return client.getIntegerReply(); 3981 } 3982 3983 // override 3984 string xgroupDelConsumer(const(ubyte)[] key, const(ubyte)[] consumer, const(ubyte)[] consumerName) { 3985 checkIsInMultiOrPipeline(); 3986 client.xgroupDelConsumer(key, consumer, consumerName); 3987 return client.getStatusCodeReply(); 3988 } 3989 3990 // override 3991 Long xdel(const(ubyte)[] key, const(ubyte)[][] ids...) { 3992 checkIsInMultiOrPipeline(); 3993 client.xdel(key, ids); 3994 return client.getIntegerReply(); 3995 } 3996 3997 // override 3998 Long xtrim(const(ubyte)[] key, long maxLen, bool approximateLength) { 3999 checkIsInMultiOrPipeline(); 4000 client.xtrim(key, maxLen, approximateLength); 4001 return client.getIntegerReply(); 4002 } 4003 4004 // override 4005 List!(const(ubyte)[]) xpending(const(ubyte)[] key, const(ubyte)[] groupname, const(ubyte)[] start, 4006 const(ubyte)[] end, int count, const(ubyte)[] consumername) { 4007 checkIsInMultiOrPipeline(); 4008 client.xpending(key, groupname, start, end, count, consumername); 4009 return client.getBinaryMultiBulkReply(); } 4010 4011 // override 4012 List!(const(ubyte)[]) xclaim(const(ubyte)[] key, const(ubyte)[] groupname, const(ubyte)[] consumername, 4013 long minIdleTime, long newIdleTime, int retries, 4014 bool force, const(ubyte)[][] ids) { 4015 checkIsInMultiOrPipeline(); 4016 client.xclaim(key, groupname, consumername, minIdleTime, newIdleTime, retries, force, ids); 4017 return client.getBinaryMultiBulkReply(); 4018 } 4019 4020 // override 4021 Object sendCommand(ProtocolCommand cmd, const(ubyte)[][] args...) { 4022 client.sendCommand(cmd, args); 4023 return client.getOne(); 4024 } 4025 } 4026 4027 4028 4029 4030 /** 4031 * A decorator to implement Set from List. Assume that given List do not contains duplicated 4032 * values. The resulting set displays the same ordering, concurrency, and performance 4033 * characteristics as the backing list. This class should be used only for Redis commands which 4034 * return Set result. 4035 * @param <E> 4036 */ 4037 class SetFromList(E) : AbstractSet!(E) { // , Serializable 4038 private List!(E) list; 4039 4040 this(List!(E) list) { 4041 if (list is null) { 4042 throw new NullPointerException("list"); 4043 } 4044 this.list = list; 4045 } 4046 4047 override 4048 void clear() { 4049 list.clear(); 4050 } 4051 4052 override 4053 int size() { 4054 return list.size(); 4055 } 4056 4057 override 4058 bool isEmpty() { 4059 return list.isEmpty(); 4060 } 4061 4062 override 4063 bool contains(E o) { 4064 return list.contains(o); 4065 } 4066 4067 override 4068 bool remove(E o) { 4069 return list.remove(o); 4070 } 4071 4072 override 4073 bool add(E e) { 4074 return !contains(e) && list.add(e); 4075 } 4076 4077 // // override 4078 // Iterator!(E) iterator() { 4079 // return list.iterator(); 4080 // } 4081 4082 override 4083 E[] toArray() { 4084 return list.toArray(); 4085 } 4086 4087 // // override 4088 // <T> T[] toArray(T[] a) { 4089 // return list.toArray(a); 4090 // } 4091 4092 override 4093 string toString() { 4094 return list.toString(); 4095 } 4096 4097 override 4098 size_t toHash() @trusted nothrow { 4099 return list.toHash(); 4100 } 4101 4102 override bool opEquals(Object o) { 4103 if (o is null) return false; 4104 if (o is this) return true; 4105 Set!E c = cast(Set!E) o; 4106 if(c is null) return false; 4107 4108 if (c.size() != size()) { 4109 return false; 4110 } 4111 4112 return containsAll(c); 4113 } 4114 4115 override 4116 bool containsAll(Collection!E c) { 4117 return list.containsAll(c); 4118 } 4119 4120 override 4121 bool removeAll(Collection!E c) { 4122 return list.removeAll(c); 4123 } 4124 4125 override 4126 bool retainAll(Collection!E c) { 4127 return list.retainAll(c); 4128 } 4129 4130 static SetFromList!(E) of(E)(List!(E) list) { 4131 return new SetFromList!E(list); 4132 } 4133 }