/Server/GameServer/redis/redisclient.h
C++ Header | 3197 lines | 2552 code | 507 blank | 138 comment | 286 complexity | b3b6e186a284494de02e206ffc45274b MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/* redisclient.h -- a C++ client library for redis. 2 * 3 * Copyright (c) 2009, Brian Hammond <brian at fictorial dot com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#ifndef REDISCLIENT_H 32#define REDISCLIENT_H 33 34#ifndef _MSC_VER 35#include <errno.h> 36#include <sys/socket.h> 37 38#include <string> 39#include <vector> 40#include <map> 41#include <set> 42#include <stdexcept> 43#include <ctime> 44#include <sstream> 45 46#include <boost/concept_check.hpp> 47#include <boost/lexical_cast.hpp> 48#include <boost/functional/hash.hpp> 49#include <boost/foreach.hpp> 50#include <boost/thread/mutex.hpp> 51#include <boost/thread/condition_variable.hpp> 52#include <boost/random.hpp> 53#include <boost/cstdint.hpp> 54#include <boost/date_time/posix_time/ptime.hpp> 55#include <boost/date_time/posix_time/posix_time_types.hpp> 56#include <boost/optional.hpp> 57#include <boost/variant.hpp> 58 59#include "anet.h" 60 61#define REDIS_LBR "\r\n" 62#define REDIS_STATUS_REPLY_OK "OK" 63#define REDIS_PREFIX_STATUS_REPLY_ERROR "-ERR " 64#define REDIS_PREFIX_STATUS_REPLY_ERR_C '-' 65#define REDIS_PREFIX_STATUS_REPLY_VALUE '+' 66#define REDIS_PREFIX_SINGLE_BULK_REPLY '$' 67#define REDIS_PREFIX_MULTI_BULK_REPLY '*' 68#define REDIS_PREFIX_INT_REPLY ':' 69#define REDIS_WHITESPACE " \f\n\r\t\v" 70 71template<class Object > 72struct make; 73namespace redis 74{ 75 template<typename CONSISTENT_HASHER> 76 class base_client; 77 78 enum reply_t 79 { 80 no_reply, 81 status_code_reply, 82 error_reply, 83 int_reply, 84 bulk_reply, 85 multi_bulk_reply 86 }; 87 88 struct connection_data 89 { 90 connection_data(const std::string & host = "localhost", uint16_t port = 6379, int dbindex = 0) 91 : host(host), port(port), dbindex(dbindex), socket(ANET_ERR) 92 { 93 } 94 95 bool operator==(const connection_data & other) const 96 { 97 if(host != other.host) 98 return false; 99 if(port != other.port) 100 return false; 101 if(dbindex != other.dbindex) 102 return false; 103 104 return true; 105 } 106 107 std::string host; 108 boost::uint16_t port; 109 int dbindex; 110 111 private: 112 int socket; 113 114 template<typename CONSISTENT_HASHER> 115 friend class base_client; 116 }; 117 118 enum server_role 119 { 120 role_master, 121 role_slave 122 }; 123 124 // Generic error that is thrown when communicating with the redis server. 125 126 class redis_error : public std::exception 127 { 128 public: 129 redis_error(const std::string & err) : err_(err) {} 130 virtual ~redis_error() throw () {} 131 operator const std::string () const { return err_; } 132 virtual const char* what() const throw () 133 { 134 return err_.c_str(); 135 } 136 137 private: 138 std::string err_; 139 }; 140 141 // Some socket-level I/O or general connection error. 142 143 class connection_error : public redis_error 144 { 145 public: 146 connection_error(const std::string & err) : redis_error(err) {} 147 }; 148 149 // Redis gave us a reply we were not expecting. 150 // Possibly an internal error (here or in redis, probably here). 151 152 class protocol_error : public redis_error 153 { 154 public: 155 protocol_error(const std::string & err) : redis_error(err) {}; 156 }; 157 158 // A key that you expected to exist does not in fact exist. 159 160 class key_error : public redis_error 161 { 162 public: 163 key_error(const std::string & err) : redis_error(err) {}; 164 }; 165 166 // A operation with time limit does not deliver an result early enough. 167 168 class timeout_error : public redis_error 169 { 170 public: 171 timeout_error(const std::string & err) : redis_error(err) {}; 172 }; 173 174 // A value of an expected type or other semantics was found to be invalid. 175 176 class value_error : public redis_error 177 { 178 public: 179 value_error(const std::string & err) : redis_error(err) {}; 180 }; 181 182 struct key 183 { 184 explicit key(const std::string & name) 185 : name(name) 186 { 187 } 188 189 std::string name; 190 }; 191 192 class makecmd 193 { 194 public: 195 explicit makecmd(const std::string & cmd_name) 196 { 197 append(cmd_name); 198 //if (!finalize) 199 // buffer_ << " "; 200 } 201 202 const std::string & key_name() const 203 { 204 if(!key_name_) 205 throw std::runtime_error("No key defined!"); 206 return *key_name_; 207 } 208 209 inline makecmd & operator<<(const key & datum) 210 { 211 if(key_name_) 212 throw std::runtime_error("You could not add a second key"); 213 else 214 key_name_ = datum.name; 215 append(datum.name); 216 return *this; 217 } 218 219 inline makecmd & operator<<(const std::string & datum) 220 { 221 append(datum); 222 return *this; 223 } 224 225 template <typename T> 226 makecmd & operator<<(T const & datum) 227 { 228 append( boost::lexical_cast<std::string>(datum) ); 229 return *this; 230 } 231 232 makecmd & operator<<(const std::vector<std::string> & data) 233 { 234 lines_.insert( lines_.end(), data.begin(), data.end() ); 235 return *this; 236 } 237 238 template <typename T> 239 makecmd & operator<<(const std::vector<T> & data) 240 { 241 size_t n = data.size(); 242 for (size_t i = 0; i < n; ++i) 243 { 244 append( boost::lexical_cast<std::string>( data[i] ) ); 245 //if (i < n - 1) 246 // buffer_ << " "; 247 } 248 return *this; 249 } 250 251 operator std::string () const 252 { 253 std::ostringstream oss; 254 size_t n = lines_.size(); 255 oss << REDIS_PREFIX_MULTI_BULK_REPLY << n << REDIS_LBR; 256 257 for (size_t i = 0; i < n; ++i) 258 { 259 const std::string & param = lines_[i]; 260 oss << REDIS_PREFIX_SINGLE_BULK_REPLY << param.size() << REDIS_LBR; 261 oss << param << REDIS_LBR; 262 } 263 264 return oss.str(); 265 } 266 267 private: 268 void append(const std::string & param) 269 { 270 lines_.push_back(param); 271 } 272 273 std::vector<std::string> lines_; 274 boost::optional<std::string> key_name_; 275 }; 276 277 template<typename CONSISTENT_HASHER> 278 class base_client; 279 280 typedef boost::variant< std::string, int, std::vector<std::string> > reply_val_t; 281 typedef std::pair<reply_t, reply_val_t> reply_data_t; 282 283 class command 284 { 285 private: 286 std::string request_; 287 std::string hash_key_; 288 reply_data_t reply_; 289 290 void check_reply_t(reply_t reply_type) const 291 { 292 if( reply_.first != reply_type ) 293 throw std::runtime_error("invalid reply type"); 294 } 295 296 void set_reply(const reply_data_t & reply) 297 { 298 reply_ = reply; 299 } 300 301 template<typename CONSISTENT_HASHER> 302 friend class base_client; 303 304 public: 305 command( const makecmd & cmd_input ) 306 : request_(cmd_input), hash_key_(cmd_input.key_name()) 307 { 308 reply_.first = no_reply; 309 } 310 311 reply_t reply_type() const 312 { 313 return reply_.first; 314 } 315 316 const std::string & get_status_code_reply() const 317 { 318 check_reply_t(status_code_reply); 319 return boost::get<std::string>(reply_.second); 320 } 321 322 const std::string & get_error_reply() const 323 { 324 check_reply_t(error_reply); 325 return boost::get<std::string>(reply_.second); 326 } 327 328 int get_int_reply() const 329 { 330 check_reply_t(int_reply); 331 return boost::get<int>(reply_.second); 332 } 333 334 const std::string & get_bulk_reply() const 335 { 336 check_reply_t(bulk_reply); 337 return boost::get<std::string>(reply_.second); 338 } 339 340 const std::vector<std::string> & get_multi_bulk_reply() const 341 { 342 check_reply_t(multi_bulk_reply); 343 return boost::get< std::vector<std::string> >(reply_.second); 344 } 345 }; 346 347 struct server_info 348 { 349 std::string version; 350 bool bgsave_in_progress; 351 unsigned long connected_clients; 352 unsigned long connected_slaves; 353 unsigned long used_memory; 354 unsigned long changes_since_last_save; 355 unsigned long last_save_time; 356 unsigned long total_connections_received; 357 unsigned long total_commands_processed; 358 unsigned long uptime_in_seconds; 359 unsigned long uptime_in_days; 360 server_role role; 361 unsigned short arch_bits; 362 std::string multiplexing_api; 363 std::map<std::string, std::string> param_map; 364 }; 365 366 inline ssize_t recv_or_throw(int fd, void* buf, size_t n, int flags) 367 { 368 ssize_t bytes_received; 369 370 do 371 bytes_received = ::recv(fd, buf, n, flags); 372 while(bytes_received < static_cast<ssize_t>(0) && errno == EINTR); 373 374 if( bytes_received == static_cast<ssize_t>(0) ) 375 throw connection_error("connection was closed"); 376 377 // Handle receive errors. I overlooked it totally, thanks mkx! 378 if( bytes_received == static_cast<ssize_t>(-1) ) 379 throw connection_error(std::string("recv error: ") + strerror(errno)); 380 381 return bytes_received; 382 } 383 384 // You should construct a 'client' object per connection to a redis-server. 385 // 386 // Please read the online redis command reference: 387 // http://code.google.com/p/redis/wiki/CommandReference 388 // 389 // No provisions for customizing the allocator on the string/bulk value type 390 // (std::string) are provided. If needed, you can always change the 391 // string_type typedef in your local version. 392 393 template<typename CONSISTENT_HASHER> 394 class base_client 395 { 396 private: 397 void init(connection_data & con) 398 { 399 char err[ANET_ERR_LEN]; 400 con.socket = anetTcpConnect(err, const_cast<char*>(con.host.c_str()), con.port); 401 if (con.socket == ANET_ERR) 402 { 403 std::ostringstream os; 404 os << err << " (redis://" << con.host << ':' << con.port << ")"; 405 throw connection_error( os.str() ); 406 } 407 anetTcpNoDelay(NULL, con.socket); 408 select(con.dbindex, con); 409 } 410 411 public: 412 typedef std::string string_type; 413 typedef std::vector<string_type> string_vector; 414 typedef std::pair<string_type, string_type> string_pair; 415 typedef std::vector<string_pair> string_pair_vector; 416 typedef std::pair<string_type, double> string_score_pair; 417 typedef std::vector<string_score_pair> string_score_vector; 418 typedef std::set<string_type> string_set; 419 420 typedef long int_type; 421 422 explicit base_client(const string_type & host = "localhost", 423 uint16_t port = 6379, int_type dbindex = 0) 424 { 425 connection_data con; 426 con.host = host; 427 con.port = port; 428 con.dbindex = dbindex; 429 init(con); 430 connections_.push_back(con); 431 } 432 433 template<typename CON_ITERATOR> 434 base_client(CON_ITERATOR begin, CON_ITERATOR end) 435 { 436 while(begin != end) 437 { 438 connection_data con = *begin; 439 init(con); 440 connections_.push_back(con); 441 begin++; 442 } 443 444 if( connections_.empty() ) 445 throw std::runtime_error("No connections given!"); 446 } 447 448 base_client<CONSISTENT_HASHER>* clone() const 449 { 450 return new base_client<CONSISTENT_HASHER>(connections_.begin(), connections_.end()); 451 } 452 453 inline static string_type missing_value() 454 { 455 return "**nonexistent-key**"; 456 } 457 458 enum datatype 459 { 460 datatype_none, // key doesn't exist 461 datatype_string, 462 datatype_list, 463 datatype_set, 464 datatype_zset, 465 datatype_hash, 466 datatype_unknown 467 }; 468 469 int_type get_list(const string_type & key, string_vector & out) 470 { 471 return lrange(key, 0, -1, out); 472 } 473 474 void lrem_exact(const string_type & key, 475 int_type count, 476 const string_type & value) 477 { 478 if (lrem(key, count, value) != count) 479 throw value_error("failed to remove exactly N elements from list"); 480 } 481 enum range_specifier 482 { 483 exclude_min = 1 << 0, 484 exclude_max = 1 << 1 485 }; 486 487 enum aggregate_type 488 { 489 aggregate_sum = 1 << 0, 490 aggregate_min = 1 << 1, 491 aggregate_max = 1 << 2 492 }; 493 494 enum sort_order 495 { 496 sort_order_ascending, 497 sort_order_descending 498 }; 499 500 ~base_client() 501 { 502 BOOST_FOREACH(connection_data & con, connections_) 503 { 504 if (con.socket != ANET_ERR) 505 close(con.socket); 506 } 507 } 508 509 const std::vector<connection_data> & connections() const 510 { 511 return connections_; 512 } 513 514 void auth(const string_type & pass) 515 { 516 if( connections_.size() > 1 ) 517 throw std::runtime_error("feature is not available in cluster mode"); 518 519 int socket = connections_[0].socket; 520 send_(socket, makecmd("AUTH") << pass); 521 recv_ok_reply_(socket); 522 } 523 524 void set(const string_type & key, 525 const string_type & value) 526 { 527 int socket = get_socket(key); 528 send_(socket, makecmd("SET") << key << value); 529 recv_ok_reply_(socket); 530 } 531 532 void mset( const string_vector & keys, const string_vector & values ) 533 { 534 assert( keys.size() == values.size() ); 535 536 std::map< int, boost::optional<makecmd> > socket_commands; 537 538 for(size_t i=0; i < keys.size(); i++) 539 { 540 int socket = get_socket(keys); 541 boost::optional<makecmd> & cmd = socket_commands[socket]; 542 if(!cmd) 543 cmd = makecmd("MSET"); 544 *cmd << keys[i] << values[i]; 545 } 546 547 typedef std::pair< int, boost::optional<makecmd> > sock_pair; 548 BOOST_FOREACH(const sock_pair & sp, socket_commands) 549 { 550 send_(sp.first, *sp.second); 551 } 552 553 BOOST_FOREACH(const sock_pair & sp, socket_commands) 554 { 555 recv_ok_reply_(sp.first); 556 } 557 } 558 559 void mset( const string_pair_vector & key_value_pairs ) 560 { 561 std::map< int, boost::optional<makecmd> > socket_commands; 562 563 for(size_t i=0; i < key_value_pairs.size(); i++) 564 { 565 const string_type & key = key_value_pairs[i].first; 566 const string_type & value = key_value_pairs[i].second; 567 568 int socket = get_socket(key); 569 boost::optional<makecmd> & cmd = socket_commands[socket]; 570 if(!cmd) 571 cmd = makecmd("MSET"); 572 *cmd << key << value; 573 } 574 575 typedef std::pair< int, boost::optional<makecmd> > sock_pair; 576 BOOST_FOREACH(const sock_pair & sp, socket_commands) 577 { 578 send_(sp.first, *sp.second); 579 } 580 581 BOOST_FOREACH(const sock_pair & sp, socket_commands) 582 { 583 recv_ok_reply_(sp.first); 584 } 585 } 586 587 private: 588 struct msetex_data 589 { 590 boost::optional<makecmd> mset_cmd; 591 std::string expire_cmds; 592 size_t count; 593 }; 594 595 public: 596 void msetex( const string_pair_vector & key_value_pairs, int_type seconds ) 597 { 598 std::map< int, msetex_data > socket_commands; 599 600 for(size_t i=0; i < key_value_pairs.size(); i++) 601 { 602 const string_type & key = key_value_pairs[i].first; 603 const string_type & value = key_value_pairs[i].second; 604 605 int socket = get_socket(key); 606 msetex_data & dat = socket_commands[socket]; 607 boost::optional<makecmd> & cmd = dat.mset_cmd; 608 if(!cmd) 609 { 610 cmd = makecmd("MSET"); 611 dat.count = 0; 612 } 613 *cmd << key << value; 614 615 std::string & expire_cmds = dat.expire_cmds; 616 expire_cmds += makecmd("EXPIRE") << key << seconds; 617 dat.count++; 618 } 619 620 typedef std::pair< int, msetex_data > sock_pair; 621 BOOST_FOREACH(const sock_pair & sp, socket_commands) 622 { 623 std::string cmds = *sp.second.mset_cmd; 624 cmds += sp.second.expire_cmds; 625 send_(sp.first, cmds); 626 } 627 628 BOOST_FOREACH(const sock_pair & sp, socket_commands) 629 { 630 recv_ok_reply_(sp.first); 631 for(size_t i= 0; i < sp.second.count; i++) 632 recv_int_ok_reply_(sp.first); 633 634 } 635 } 636 637 string_type get(const string_type & key) 638 { 639 int socket = get_socket(key); 640 send_(socket, makecmd("GET") << key); 641 return recv_bulk_reply_(socket); 642 } 643 644 string_type getset(const string_type & key, const string_type & value) 645 { 646 int socket = get_socket(key); 647 send_(socket, makecmd("GETSET") << key << value); 648 return recv_bulk_reply_(socket); 649 } 650 651 private: 652 struct connection_keys 653 { 654 boost::optional<makecmd> cmd; 655 656 /// Gives the position of the value in the original array 657 std::vector<size_t> indices; 658 }; 659 660 public: 661 void exec(command & cmd) 662 { 663 int socket = get_socket(cmd.hash_key_); 664 send_( socket, cmd.request_ ); 665 cmd.set_reply( recv_generic_reply_(socket) ); 666 } 667 668 void exec(std::vector<command> & commands) 669 { 670 std::map< int, std::string > socket_commands; 671 672 for(size_t i=0; i < commands.size(); i++) 673 { 674 int socket = get_socket( commands[i].hash_key_ ); 675 socket_commands[socket] += commands[i].request_; 676 } 677 678 typedef std::pair< int, std::string > sock_pair; 679 BOOST_FOREACH(const sock_pair & sp, socket_commands) 680 { 681 send_(sp.first, sp.second); 682 } 683 684 for(size_t i=0; i < commands.size(); i++) 685 { 686 int socket = get_socket( commands[i].hash_key_ ); 687 commands[i].set_reply( recv_generic_reply_(socket) ); 688 } 689 } 690 691 void exec_transaction(std::vector<command> & commands) 692 { 693 int cmd_socket = -1; 694 std::string cmd_str = makecmd("MULTI"); 695 696 for(size_t i=0; i < commands.size(); i++) 697 { 698 int socket = get_socket( commands[i].hash_key_ ); 699 if( cmd_socket == -1 ) 700 cmd_socket = socket; 701 else if( cmd_socket != socket ) 702 throw std::runtime_error("calls in transaction map to different server!"); 703 704 cmd_str += commands[i].request_; 705 } 706 707 cmd_str += makecmd("EXEC"); 708 709 send_(cmd_socket, cmd_str); 710 recv_ok_reply_(cmd_socket); // MULTI => +OK 711 for(size_t i=0; i < commands.size(); i++) 712 { 713 std::string resp = recv_single_line_reply_(cmd_socket); 714 if( resp != "QUEUED" ) 715 throw std::runtime_error("invalid state (expected 'QUEUED' in transaction but got '" + resp + "')"); 716 } 717 if( read_line(cmd_socket)[0] != REDIS_PREFIX_MULTI_BULK_REPLY ) 718 throw std::runtime_error("EXEC does not return a multi bulk reply"); 719 720 for(size_t i=0; i < commands.size(); i++) 721 { 722 commands[i].set_reply( recv_generic_reply_(cmd_socket) ); 723 } 724 } 725 726 void mget(const string_vector & keys, string_vector & out) 727 { 728 out = string_vector( keys.size() ); 729 std::map< int, connection_keys > socket_commands; 730 731 for(size_t i=0; i < keys.size(); i++) 732 { 733 int socket = get_socket(keys[i]); 734 connection_keys & con_keys = socket_commands[socket]; 735 boost::optional<makecmd> & cmd = con_keys.cmd; 736 if(!cmd) 737 cmd = makecmd("MGET"); 738 *cmd << keys[i]; 739 con_keys.indices.push_back(i); 740 } 741 742 typedef std::pair< int, connection_keys > sock_pair; 743 BOOST_FOREACH(const sock_pair & sp, socket_commands) 744 { 745 send_(sp.first, *sp.second.cmd); 746 } 747 748 BOOST_FOREACH(const sock_pair & sp, socket_commands) 749 { 750 const connection_keys & con_keys = sp.second; 751 string_vector cur_out; 752 recv_multi_bulk_reply_(sp.first, cur_out); 753 754 for(size_t i=0; i < cur_out.size(); i++) 755 out[con_keys.indices[i]] = cur_out[i]; 756 } 757 } 758 759 bool setnx(const string_type & key, 760 const string_type & value) 761 { 762 int socket = get_socket(key); 763 send_(socket, makecmd("SETNX") << key << value); 764 return recv_int_reply_(socket) == 1; 765 } 766 767 bool msetnx( const string_vector & keys, const string_vector & values ) 768 { 769 assert( keys.size() == values.size() ); 770 771 std::map< int, boost::optional<makecmd> > socket_commands; 772 773 for(size_t i=0; i < keys.size(); i++) 774 { 775 int socket = get_socket(keys); 776 boost::optional<makecmd> & cmd = socket_commands[socket]; 777 if(!cmd) 778 cmd = makecmd("MSETNX"); 779 *cmd << keys[i] << values[i]; 780 } 781 782 if( socket_commands.size() > 1 ) 783 throw std::runtime_error("feature is not available in cluster mode"); 784 785 typedef std::pair< int, boost::optional<makecmd> > sock_pair; 786 BOOST_FOREACH(const sock_pair & sp, socket_commands) 787 { 788 send_(sp.first, *sp.second); 789 recv_ok_reply_(sp.first); 790 } 791 } 792 793 bool msetnx( const string_pair_vector & key_value_pairs ) 794 { 795 std::map< int, boost::optional<makecmd> > socket_commands; 796 797 for(size_t i=0; i < key_value_pairs.size(); i++) 798 { 799 int socket = get_socket(keys); 800 boost::optional<makecmd> & cmd = socket_commands[socket]; 801 if(!cmd) 802 cmd = makecmd("MSETNX"); 803 *cmd << key_value_pairs[i].first << key_value_pairs[i].second; 804 } 805 806 if( socket_commands.size() > 1 ) 807 throw std::runtime_error("feature is not available in cluster mode"); 808 809 typedef std::pair< int, boost::optional<makecmd> > sock_pair; 810 BOOST_FOREACH(const sock_pair & sp, socket_commands) 811 { 812 send_(sp.first, *sp.second); 813 recv_ok_reply_(sp.first); 814 } 815 } 816 817 void setex(const string_type & key, const string_type & value, unsigned int secs) 818 { 819 int socket = get_socket(key); 820 send_(socket, makecmd("SETEX") << key << secs << value); 821 recv_ok_reply_(socket); 822 } 823 824 size_t append(const string_type & key, const string_type & value) 825 { 826 int socket = get_socket(key); 827 send_(socket, makecmd("APPEND") << key << value); 828 int res = recv_int_reply_(socket); 829 if(res < 0) 830 throw protocol_error("expected value size"); 831 832 assert( static_cast<size_t>(res) >= value.size() ); 833 return static_cast<size_t>(res); 834 } 835 836 string_type substr(const string_type & key, int start, int end) 837 { 838 int socket = get_socket(key); 839 send_(socket, makecmd("SUBSTR") << key << start << end); 840 return recv_bulk_reply_(socket); 841 } 842 843 int_type incr(const string_type & key) 844 { 845 int socket = get_socket(key); 846 send_(socket, makecmd("INCR") << key); 847 return recv_int_reply_(socket); 848 } 849 850 template<typename INT_TYPE> 851 INT_TYPE incr(const string_type & key) 852 { 853 int socket = get_socket(key); 854 send_(socket, makecmd("INCR") << key); 855 return recv_int_reply_<INT_TYPE>(socket); 856 } 857 858 int_type incrby(const string_type & key, int_type by) 859 { 860 int socket = get_socket(key); 861 send_(socket, makecmd("INCRBY") << key << by); 862 return recv_int_reply_(socket); 863 } 864 865 template<typename INT_TYPE> 866 INT_TYPE incrby(const string_type & key, INT_TYPE by) 867 { 868 int socket = get_socket(key); 869 send_(socket, makecmd("INCRBY") << key << by); 870 return recv_int_reply_<INT_TYPE>(socket); 871 } 872 873 int_type decr(const string_type & key) 874 { 875 int socket = get_socket(key); 876 send_(socket, makecmd("DECR") << key); 877 return recv_int_reply_(socket); 878 } 879 880 template<typename INT_TYPE> 881 INT_TYPE decr(const string_type & key) 882 { 883 int socket = get_socket(key); 884 send_(socket, makecmd("DECR") << key); 885 return recv_int_reply_<INT_TYPE>(socket); 886 } 887 888 int_type decrby(const string_type & key, int_type by) 889 { 890 int socket = get_socket(key); 891 send_(socket, makecmd("DECRBY") << key << by); 892 return recv_int_reply_(socket); 893 } 894 895 template<typename INT_TYPE> 896 INT_TYPE decrby(const string_type & key, INT_TYPE by) 897 { 898 int socket = get_socket(key); 899 send_(socket, makecmd("DECRBY") << key << by); 900 return recv_int_reply_<INT_TYPE>(socket); 901 } 902 903 bool exists(const string_type & key) 904 { 905 int socket = get_socket(key); 906 send_(socket, makecmd("EXISTS") << key); 907 return recv_int_reply_(socket) == 1; 908 } 909 910 bool del(const string_type & key) 911 { 912 int socket = get_socket(key); 913 send_(socket, makecmd("DEL") << key); 914 return recv_int_reply_(socket) != 0; 915 } 916 917 template<typename ITERATOR> 918 bool del(ITERATOR begin, ITERATOR end) 919 { 920 std::map<int, string_vector> sock_key_map; 921 while( begin != end ) 922 { 923 string_type key = *begin++; 924 sock_key_map[ get_socket(key) ].push_back(key); 925 } 926 927 typedef std::pair<const int, string_vector> sock_key_pair; 928 BOOST_FOREACH(const sock_key_pair & p, sock_key_map) 929 { 930 send_(p.first, makecmd("DEL") << p.second); 931 } 932 933 int_type res = false; 934 935 BOOST_FOREACH(const sock_key_pair & p, sock_key_map) 936 { 937 res += recv_int_reply_(p.first); 938 } 939 940 return res; 941 } 942 943 datatype type(const string_type & key) 944 { 945 int socket = get_socket(key); 946 send_(socket, makecmd("TYPE") << key); 947 std::string response = recv_single_line_reply_(socket); 948 949 if(response == "none") return datatype_none; 950 if(response == "string") return datatype_string; 951 if(response == "list") return datatype_list; 952 if(response == "set") return datatype_set; 953 if(response == "zset") return datatype_zset; 954 if(response == "hash") return datatype_hash; 955 956#ifndef NDEBUG 957 std::cerr << "Got unknown datatype name: " << response << std::endl; 958#endif // NDEBUG 959 960 return datatype_unknown; 961 } 962 963 int_type keys(const string_type & pattern, string_vector & out) 964 { 965 BOOST_FOREACH(const connection_data & con, connections_) 966 { 967 send_(con.socket, makecmd("KEYS") << pattern); 968 } 969 970 int_type res = 0; 971 972 BOOST_FOREACH(const connection_data & con, connections_) 973 { 974 res += recv_multi_bulk_reply_(con.socket, out); 975 } 976 977 return res; 978 } 979 980 string_type randomkey() 981 { 982 int socket = connections_[0].socket; 983 if( connections_.size() > 1 ) 984 { 985 /* 986 * Select a random server if there are more then one 987 */ 988 989 boost::mt19937 gen; 990 boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time(); 991 boost::posix_time::ptime epoch( boost::gregorian::date(1970, 1, 1) ); 992 gen.seed( (now-epoch).total_seconds() ); 993 994 boost::uniform_int<> dist(0, connections_.size()-1); 995 boost::variate_generator< boost::mt19937&, boost::uniform_int<> > die(gen, dist); 996 socket = connections_[die()].socket; 997 } 998 999 send_(socket, makecmd("RANDOMKEY") ); 1000 return recv_bulk_reply_(socket); 1001 } 1002 1003 /** 1004 * @warning Not cluster save (the old name and the new one must be on the same redis server) 1005 */ 1006 void rename(const string_type & old_name, const string_type & new_name) 1007 { 1008 int source_socket = get_socket(old_name); 1009 int destin_socket = get_socket(new_name); 1010 if( source_socket != destin_socket ) 1011 { 1012 switch( type(old_name) ) 1013 { 1014 case datatype_none: // key doesn't exist 1015 return; 1016 case datatype_string: 1017 set(new_name, get(old_name)); 1018 break; 1019 case datatype_list: 1020 { 1021 string_vector content; 1022 lrange(old_name, 0, -1, content); 1023 del(new_name); 1024 BOOST_FOREACH(const string_type & val, content) 1025 { 1026 rpush(new_name, val); 1027 } 1028 break; 1029 } 1030 case datatype_set: 1031 { 1032 string_set content; 1033 smembers(old_name, content); 1034 del(new_name); 1035 BOOST_FOREACH(const string_type & val, content) 1036 { 1037 sadd(new_name, val); 1038 } 1039 break; 1040 } 1041 case datatype_zset: 1042 case datatype_hash: 1043 case datatype_unknown: 1044 default: 1045 throw std::runtime_error("renaming is not supported for this datatype in cluster mode"); 1046 } 1047 del(old_name); 1048 return; 1049 } 1050 1051 send_(source_socket, makecmd("RENAME") << old_name << new_name); 1052 recv_ok_reply_(source_socket); 1053 } 1054 1055 /** 1056 * @warning Not cluster save (the old name and the new one must be on the same redis server) 1057 */ 1058 bool renamenx(const string_type & old_name, const string_type & new_name) 1059 { 1060 int source_socket = get_socket(old_name); 1061 int destin_socket = get_socket(new_name); 1062 1063 if( source_socket != destin_socket ) 1064 { 1065 if( exists(new_name) ) 1066 return false; 1067 rename(old_name, new_name); 1068 return true; 1069 } 1070 1071 send_(source_socket, makecmd("RENAMENX") << old_name << new_name); 1072 return recv_int_reply_(source_socket) == 1; 1073 } 1074 1075 /** 1076 * @returns the number of keys in the currently selected database. In cluster mode the number 1077 * of keys in all currently selected databases is returned. 1078 */ 1079 int_type dbsize() 1080 { 1081 int_type val = 0; 1082 1083 BOOST_FOREACH(const connection_data & con, connections_) 1084 { 1085 send_(con.socket, makecmd("DBSIZE")); 1086 } 1087 1088 BOOST_FOREACH(const connection_data & con, connections_) 1089 { 1090 val += recv_int_reply_(con.socket); 1091 } 1092 1093 return val; 1094 } 1095 1096 /** 1097 * @returns the number of keys in the currently selected database with the given connection. 1098 */ 1099 int_type dbsize(const connection_data & con) 1100 { 1101 send_(con.socket, makecmd("DBSIZE")); 1102 return recv_int_reply_(con.socket); 1103 } 1104 1105 void expire(const string_type & key, unsigned int secs) 1106 { 1107 int socket = get_socket(key); 1108 send_(socket, makecmd("EXPIRE") << key << secs); 1109 recv_int_ok_reply_(socket); 1110 } 1111 1112 int ttl(const string_type & key) 1113 { 1114 int socket = get_socket(key); 1115 send_(socket, makecmd("TTL") << key); 1116 return recv_int_reply_(socket); 1117 } 1118 1119 int_type rpush(const string_type & key, 1120 const string_type & value) 1121 { 1122 int socket = get_socket(key); 1123 send_(socket, makecmd("RPUSH") << key << value); 1124 return recv_int_reply_(socket); 1125 } 1126 1127 int_type lpush(const string_type & key, 1128 const string_type & value) 1129 { 1130 int socket = get_socket(key); 1131 send_(socket, makecmd("LPUSH") << key << value); 1132 return recv_int_reply_(socket); 1133 } 1134 1135 int_type llen(const string_type & key) 1136 { 1137 int socket = get_socket(key); 1138 send_(socket, makecmd("LLEN") << key); 1139 return recv_int_reply_(socket); 1140 } 1141 1142 int_type lrange(const string_type & key, 1143 int_type start, 1144 int_type end, 1145 string_vector & out) 1146 { 1147 int socket = get_socket(key); 1148 send_(socket, makecmd("LRANGE") << key << start << end); 1149 return recv_multi_bulk_reply_(socket, out); 1150 } 1151 1152 void ltrim(const string_type & key, 1153 int_type start, 1154 int_type end) 1155 { 1156 int socket = get_socket(key); 1157 send_(socket, makecmd("LTRIM") << key << start << end); 1158 recv_ok_reply_(socket); 1159 } 1160 1161 string_type lindex(const string_type & key, 1162 int_type index) 1163 { 1164 int socket = get_socket(key); 1165 send_(socket, makecmd("LINDEX") << key << index); 1166 return recv_bulk_reply_(socket); 1167 } 1168 1169 void lset(const string_type & key, int_type index, const string_type & value) 1170 { 1171 int socket = get_socket(key); 1172 send_(socket, makecmd("LSET") << key << index << value); 1173 recv_ok_reply_(socket); 1174 } 1175 1176 int_type lrem(const string_type & key, int_type count, const string_type & value) 1177 { 1178 int socket = get_socket(key); 1179 send_(socket, makecmd("LREM") << key << count << value); 1180 return recv_int_reply_(socket); 1181 } 1182 1183 string_type lpop(const string_type & key) 1184 { 1185 int socket = get_socket(key); 1186 send_(socket, makecmd("LPOP") << key); 1187 return recv_bulk_reply_(socket); 1188 } 1189 1190 string_type rpop(const string_type & key) 1191 { 1192 int socket = get_socket(key); 1193 send_(socket, makecmd("RPOP") << key); 1194 return recv_bulk_reply_(socket); 1195 } 1196 1197 /** 1198 * @warning Not cluster save (all keys must be on the same redis server) 1199 */ 1200 string_pair blpop(const string_vector & keys, int_type timeout_seconds = 0) 1201 { 1202 int socket = get_socket(keys); 1203 if(socket == -1) 1204 // How to do in cluster mode? Is reinserting of to much poped values a solution? 1205 throw std::runtime_error("feature is not available in cluster mode"); 1206 1207 send_(socket, makecmd("BLPOP") << keys << timeout_seconds); 1208 string_vector sv; 1209 try 1210 { 1211 recv_multi_bulk_reply_(socket, sv); 1212 } 1213 catch(key_error & e) 1214 { 1215 assert(timeout_seconds > 0); 1216 throw timeout_error("could not pop value in time"); 1217 } 1218 if(sv.size() == 2) 1219 return make_pair( sv[0], sv[1] ); 1220 else 1221 return make_pair( "", missing_value() ); 1222 } 1223 1224 string_type blpop(const string_type & key, int_type timeout_seconds = 0) 1225 { 1226 int socket = get_socket(key); 1227 if(socket == -1) 1228 // How to do in cluster mode? Is reinserting of to much poped values a solution? 1229 throw std::runtime_error("feature is not available in cluster mode"); 1230 1231 send_(socket, makecmd("BLPOP") << key << timeout_seconds); 1232 string_vector sv; 1233 try 1234 { 1235 recv_multi_bulk_reply_(socket, sv); 1236 } 1237 catch(key_error & e) 1238 { 1239 assert(timeout_seconds > 0); 1240 return missing_value(); // should we throw a timeout_error? 1241 // we set a timeout so we expect that this can happen 1242 } 1243 if(sv.size() == 2) 1244 { 1245 assert(key == sv[0]); 1246 return sv[1]; 1247 } 1248 else 1249 return missing_value(); 1250 } 1251 1252 /** 1253 * @warning Not cluster save (all keys must be on the same redis server) 1254 */ 1255 string_pair brpop(const string_vector & keys, int_type timeout_seconds) 1256 { 1257 int socket = get_socket(keys); 1258 makecmd m("BRPOP"); 1259 for(size_t i=0; i < keys.size(); i++) 1260 m << keys[i]; 1261 m << timeout_seconds; 1262 send_(socket, m); 1263 string_vector sv; 1264 try 1265 { 1266 recv_multi_bulk_reply_(socket, sv); 1267 } 1268 catch(key_error & e) 1269 { 1270 assert(timeout_seconds > 0); 1271 return missing_value(); // should we throw a timeout_error? 1272 // we set a timeout so we expect that this can happen 1273 } 1274 if(sv.size() == 2) 1275 return make_pair( sv[0], sv[1] ); 1276 else 1277 return make_pair( "", missing_value ); 1278 } 1279 1280 string_type brpop(const string_type & key, int_type timeout_seconds) 1281 { 1282 int socket = get_socket(key); 1283 send_(socket, makecmd("BRPOP") << key << timeout_seconds); 1284 string_vector sv; 1285 try 1286 { 1287 recv_multi_bulk_reply_(socket, sv); 1288 } 1289 catch(key_error & e) 1290 { 1291 assert(timeout_seconds > 0); 1292 return missing_value(); // should we throw a timeout_error? 1293 // we set a timeout so we expect that this can happen 1294 } 1295 if(sv.size() == 2) 1296 { 1297 assert(key == sv[0]); 1298 return sv[1]; 1299 } 1300 else 1301 return missing_value(); 1302 } 1303 1304 bool sadd(const string_type & key, 1305 const string_type & value) 1306 { 1307 int socket = get_socket(key); 1308 send_(socket, makecmd("SADD") << key << value); 1309 return recv_int_reply_(socket) == 1; 1310 } 1311 1312 template<typename ITERATOR> 1313 int_type sadd(const string_type & key, ITERATOR begin, ITERATOR end) 1314 { 1315 int socket = get_socket(key); 1316 std::string commands; 1317 size_t count = 0; 1318 1319 while(begin != end) 1320 { 1321 string_type val = *begin++; 1322 commands += makecmd("SADD") << key << val; 1323 count++; 1324 } 1325 1326 send_(socket, commands); 1327 1328 int_type res = 0; 1329 for(size_t i=0; i < count; i++) 1330 { 1331 res += recv_int_reply_(socket); 1332 } 1333 return res; 1334 } 1335 1336 void srem(const string_type & key, 1337 const string_type & value) 1338 { 1339 int socket = get_socket(key); 1340 send_(socket, makecmd("SREM") << key << value); 1341 recv_int_ok_reply_(socket); 1342 } 1343 1344 string_type spop(const string_type & key) 1345 { 1346 int socket = get_socket(key); 1347 send_(socket, makecmd("SPOP") << key); 1348 return recv_bulk_reply_(socket); 1349 } 1350 1351 void smove(const string_type & srckey, const string_type & dstkey, const string_type & member) 1352 { 1353 int src_socket = get_socket(srckey); 1354 int dst_socket = get_socket(dstkey); 1355 if(dst_socket != src_socket) 1356 { 1357 srem(srckey, member); 1358 sadd(dstkey, member); 1359 return; 1360 } 1361 1362 send_(src_socket, makecmd("SMOVE") << srckey << dstkey << member); 1363 recv_int_ok_reply_(src_socket); 1364 } 1365 1366 int_type scard(const string_type & key) 1367 { 1368 int socket = get_socket(key); 1369 send_(socket, makecmd("SCARD") << key); 1370 return recv_int_reply_(socket); 1371 } 1372 1373 bool sismember(const string_type & key, 1374 const string_type & value) 1375 { 1376 int socket = get_socket(key); 1377 send_(socket, makecmd("SISMEMBER") << key << value); 1378 return recv_int_reply_(socket) == 1; 1379 } 1380 1381 /** 1382 * @returns the intersection between the Sets stored at key1, key2, ..., keyN 1383 * @warning Not cluster save (all keys must be on the same redis server) 1384 */ 1385 int_type sinter(const string_vector & keys, string_set & out) 1386 { 1387 std::map<int, string_vector> per_server; 1388 BOOST_FOREACH(const string_type & key, keys) 1389 { 1390 per_server[get_socket(key)].push_back(key); 1391 } 1392 1393 size_t i = 0; 1394 1395 typedef std::pair<const int, string_vector> per_server_pair; 1396 BOOST_FOREACH(const per_server_pair & p, per_server) 1397 { 1398 send_(p.first, makecmd("SINTER") << p.second); 1399 } 1400 1401 BOOST_FOREACH(const per_server_pair & p, per_server) 1402 { 1403 string_set cur; 1404 recv_multi_bulk_reply_(p.first, cur); 1405 if(i > 0) 1406 { 1407 string_set prev = out; 1408 out.clear(); 1409 1410 std::set_intersection(prev.begin(), prev.end(), cur.begin(), cur.end(), std::inserter(out, out.end())); 1411 } 1412 else 1413 { 1414 out = cur; 1415 } 1416 i++; 1417 } 1418 1419 return out.size(); 1420 } 1421 1422 /** 1423 * @warning Not cluster save (all keys must be on the same redis server) 1424 */ 1425 int_type sinterstore(const string_type & dstkey, const string_vector & keys) 1426 { 1427 int socket = get_socket(dstkey); 1428 int source_sockets = get_socket(keys); 1429 if(socket != source_sockets) 1430 { 1431 std::cerr << 0 << std::endl; 1432 string_set content; 1433 std::cerr << 1 << std::endl; 1434 sinter(keys, content); 1435 std::cerr << 2 << std::endl; 1436 del(dstkey); 1437 std::cerr << 3 << std::endl; 1438 BOOST_FOREACH(const string_type & val, content) 1439 { 1440 std::cerr << 4 << std::endl; 1441 sadd(dstkey, val); 1442 } 1443 std::cerr << 5 << std::endl; 1444 return content.size(); 1445 } 1446 1447 send_(socket, makecmd("SINTERSTORE") << dstkey << keys); 1448 return recv_int_reply_(socket); 1449 } 1450 1451 int_type sunion(const string_vector & keys, string_set & out) 1452 { 1453 int socket = get_socket(keys); 1454 if(socket == -1) 1455 { 1456 std::map< int, boost::optional<makecmd> > per_socket_keys; 1457 BOOST_FOREACH(const string_type & key, keys) 1458 { 1459 boost::optional<makecmd> & optCmd = per_socket_keys[ get_socket(key) ]; 1460 if( !optCmd ) 1461 optCmd = makecmd("SUNION"); 1462 *optCmd << key; 1463 } 1464 1465 typedef std::pair< const int, boost::optional<makecmd> > per_sock_pair; 1466 BOOST_FOREACH(const per_sock_pair & p, per_socket_keys) 1467 { 1468 send_(p.first, *p.second); 1469 } 1470 1471 BOOST_FOREACH(const per_sock_pair & p, per_socket_keys) 1472 { 1473 recv_multi_bulk_reply_(p.first, out); 1474 } 1475 return out.size(); 1476 } 1477 1478 send_(socket, makecmd("SUNION") << keys); 1479 return recv_multi_bulk_reply_(socket, out); 1480 } 1481 1482 int_type sunionstore(const string_type & dstkey, 1483 const string_vector & keys) 1484 { 1485 int socket = get_socket(dstkey); 1486 int source_sockets = get_socket(keys); 1487 if(socket != source_sockets) 1488 { 1489 string_set content; 1490 sunion(keys, content); 1491 del(dstkey); 1492 return sadd(dstkey, content.begin(), content.end()); 1493 } 1494 1495 send_(socket, makecmd("SUNIONSTORE") << dstkey << keys); 1496 return recv_int_reply_(socket); 1497 } 1498 1499 int_type sdiff(const string_vector & keys, string_set & out) 1500 { 1501 int socket = get_socket(keys); 1502 send_(socket, makecmd("SDIFF") << keys); 1503 return recv_multi_bulk_reply_(socket, out); 1504 } 1505 1506 int_type sdiffstore(const string_type & dstkey, const string_vector & keys) 1507 { 1508 int socket = get_socket(dstkey); 1509 int source_sockets = get_socket(keys); 1510 if(socket != source_sockets) 1511 throw std::runtime_error("not available in cluster mode"); 1512 1513 send_(socket, makecmd("SDIFFSTORE") << dstkey << keys); 1514 return recv_int_reply_(socket); 1515 } 1516 1517 int_type smembers(const string_type & key, string_set & out) 1518 { 1519 int socket = get_socket(key); 1520 send_(socket, makecmd("SMEMBERS") << key); 1521 return recv_multi_bulk_reply_(socket, out); 1522 } 1523 1524 string_type srandmember(const string_type & key) 1525 { 1526 int socket = get_socket(key); 1527 send_(socket, makecmd("SPOP") << key); 1528 return recv_bulk_reply_(socket); 1529 } 1530 1531 void zadd(const string_type & key, double score, const string_type & member) 1532 { 1533 int socket = get_socket(key); 1534 send_(socket, makecmd("ZADD") << key << score << member); 1535 recv_int_ok_reply_(socket); 1536 } 1537 1538 void zadd(const string_type & key, const string_score_pair & value) 1539 { 1540 zadd(key, value.second, value.first); 1541 } 1542 1543 void zrem(const string_type & key, const string_type & member) 1544 { 1545 int socket = get_socket(key); 1546 send_(socket, makecmd("ZREM") << key << member); 1547 recv_int_ok_reply_(socket); 1548 } 1549 1550 double zincrby(const string_type & key, const string_type & member, double increment) 1551 { 1552 int socket = get_socket(key); 1553 send_(socket, makecmd("ZINCRBY") << key << increment << member); 1554 return boost::lexical_cast<double>( recv_bulk_reply_(socket) ); 1555 } 1556 1557 int_type zrank(const string_type & key, const string_type & member) 1558 { 1559 int socket = get_socket(key); 1560 send_(socket, makecmd("ZRANK") << key << member); 1561 return recv_int_reply_(socket); 1562 } 1563 1564 int_type zrevrank(const string_type & key, const string_type & value) 1565 { 1566 int socket = get_socket(key); 1567 send_(socket, makecmd("ZREVRANK") << key << value); 1568 return boost::lexical_cast<int_type>( recv_int_reply_(socket) ); 1569 } 1570 1571 void zrange(const string_type & key, int_type start, int_type end, string_vector & out) 1572 { 1573 int socket = get_socket(key); 1574 send_( socket, makecmd("ZRANGE") << key << start << end ); 1575 recv_multi_bulk_reply_(socket, out); 1576 } 1577 1578 private: 1579 void convert(const string_vector & in, string_score_vector & out) 1580 { 1581 assert( in.size() % 2 == 0 ); 1582 1583 for(size_t i=0; i < in.size(); i += 2) 1584 { 1585 const std::string & value = in[i]; 1586 const std::string & str_score = in[i+1]; 1587 double score = boost::lexical_cast<double>(str_score); 1588 out.push_back( make_pair(value, score) ); 1589 } 1590 } 1591 1592 1593 public: 1594 1595 void zrange(const string_type & key, int_type start, int_type end, string_score_vector & out) 1596 { 1597 int socket = get_socket(key); 1598 send_( socket, makecmd("ZRANGE") << key << start << end << "WITHSCORES" ); 1599 string_vector res; 1600 recv_multi_bulk_reply_(socket, res); 1601 convert(res, out); 1602 } 1603 1604 void zrevrange(const string_type & key, int_type start, int_type end, string_vector & out) 1605 { 1606 int socket = get_socket(key); 1607 send_( socket, makecmd("ZREVRANGE") << key << start << end ); 1608 recv_multi_bulk_reply_(socket, out); 1609 } 1610 1611 void zrevrange(const string_type & key, int_type start, int_type end, string_score_vector & out) 1612 { 1613 int socket = get_socket(key); 1614 send_( socket, makecmd("ZREVRANGE") << key << start << end << "WITHSCORES" ); 1615 string_vector res; 1616 recv_multi_bulk_reply_(socket, res); 1617 convert(res, out); 1618 } 1619 1620 protected: 1621 void zrangebyscore_base(bool withscores, const string_type & key, double min, double max, string_vector & out, int_type offset, int_type max_count, int range_modification) 1622 { 1623 int socket = get_socket(key); 1624 std::string min_str, max_str; 1625 if( range_modification & exclude_min ) 1626 min_str = "("; 1627 if( range_modification & exclude_max ) 1628 max_str = "("; 1629 1630 min_str += boost::lexical_cast<std::string>(min); 1631 max_str += boost::lexical_cast<std::string>(max); 1632 1633 makecmd m("ZRANGEBYSCORE"); 1634 m << key << min_str << max_str; 1635 1636 if(max_count != -1 || offset > 0) 1637 { 1638 std::cerr << "Adding limit: " << offset << " " << max_count << std::endl; 1639 m << "LIMIT" << offset << max_count; 1640 } 1641 1642 send_(socket, m); 1643 recv_multi_bulk_reply_(socket, out); 1644 } 1645 1646 public: 1647 void zrangebyscore(const string_type & key, double min, double max, string_vector & out, int_type offset = 0, int_type max_count = -1, int range_modification = 0) 1648 { 1649 zrangebyscore_base(false, key, min, max, out, offset, max_count, range_modification); 1650 } 1651 1652 void zrangebyscore(const string_type & key, double min, double max, string_score_vector & out, int_type offset = 0, int_type max_count = -1, int range_modification = 0) 1653 { 1654 string_vector res; 1655 zrangebyscore_base(true, key, min, max, res, offset, max_count, range_modification); 1656 convert(res, out); 1657 } 1658 1659 int_type zcount(const string_type & key, double min, double max, int range_modification = 0) 1660 { 1661 int socket = get_socket(key); 1662 std::string min_str, max_str; 1663 if( range_modification & exclude_min ) 1664 min_str = "("; 1665 if( range_modification & exclude_max ) 1666 max_str = "("; 1667 1668 min_str += boost::lexical_cast<std::string>(min); 1669 max_str += boost::lexical_cast<std::string>(max); 1670 1671 send_(socket, makecmd("ZCOUNT") << key << min_str << max_str); 1672 return recv_int_reply_(socket); 1673 } 1674 1675 int_type zremrangebyrank( const string_type & key, int_type start, int_type end ) 1676 { 1677 int socket = get_socket(key); 1678 send_(socket, makecmd("ZREMRANGEBYRANK") << key << start << end); 1679 return recv_int_reply_(socket); 1680 } 1681 1682 int_type zremrangebyscore( const string_type& key, double min, double max ) 1683 { 1684 int socket = get_socket(key); 1685 send_(socket, makecmd("ZREMRANGEBYSCORE") << key << min << max); 1686 return recv_int_reply_(socket); 1687 } 1688 1689 int_type zcard( const string_type & key ) 1690 { 1691 int socket = get_socket(key); 1692 send_(socket, makecmd("ZCARD") << key); 1693 return recv_int_reply_(socket); 1694 } 1695 1696 double zscore( const string_type& key, const string_type& element ) 1697 { 1698 int socket = get_socket(key); 1699 send_(socket, makecmd("ZSCORE") << key << element); 1700 return boost::lexical_cast<double>( recv_bulk_reply_(socket) ); 1701 } 1702 1703 int_type zunionstore( const string_type & dstkey, const string_vector & keys, const std::vector<double> & weights = std::vector<double>(), aggregate_type aggragate = aggregate_sum ) 1704 { 1705 int dst_socket = get_socket(dstkey); 1706 int socket = get_socket(keys); 1707 if(socket != dst_socket) 1708 throw std::runtime_error("feature is not available in cluster mode"); 1709 1710 makecmd m("ZUNIONSTORE"); 1711 m << dstkey << keys.size() << keys; 1712 1713 if( weights.size() > 0 ) 1714 { 1715 assert(keys.size() == weights.size()); 1716 m << "WEIGHTS" << weights; 1717 } 1718 1719 m << "AGGREGATE"; 1720 switch(aggragate) 1721 { 1722 case aggregate_sum: 1723 m << "SUM"; 1724 break; 1725 case aggregate_min: 1726 m << "MIN"; 1727 break; 1728 case aggregate_max: 1729 m << "MAX"; 1730 break; 1731 default: 1732 assert(false); 1733 } 1734 1735 send_(socket, m); 1736 return re…
Large files files are truncated, but you can click here to view the full file