-
Notifications
You must be signed in to change notification settings - Fork 358
/
Copy pathredis.h
3773 lines (3381 loc) · 169 KB
/
redis.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_REDIS_H
#define SEWENEW_REDISPLUSPLUS_REDIS_H
#include <string>
#include <chrono>
#include <memory>
#include <initializer_list>
#include <tuple>
#include "sw/redis++/connection_pool.h"
#include "sw/redis++/reply.h"
#include "sw/redis++/command_options.h"
#include "sw/redis++/utils.h"
#include "sw/redis++/subscriber.h"
#include "sw/redis++/pipeline.h"
#include "sw/redis++/transaction.h"
#include "sw/redis++/sentinel.h"
#include "sw/redis++/redis_uri.h"
namespace sw {
namespace redis {
template <typename Impl>
class QueuedRedis;
using Transaction = QueuedRedis<TransactionImpl>;
using Pipeline = QueuedRedis<PipelineImpl>;
class Redis {
public:
/// @brief Construct `Redis` instance with connection options and connection pool options.
/// @param connection_opts Connection options.
/// @param pool_opts Connection pool options.
/// @see `ConnectionOptions`
/// @see `ConnectionPoolOptions`
/// @see /~https://github.com/sewenew/redis-plus-plus#connection
explicit Redis(const ConnectionOptions &connection_opts,
const ConnectionPoolOptions &pool_opts = {}) :
_pool(std::make_shared<ConnectionPool>(pool_opts, connection_opts)) {}
/// @brief Construct `Redis` instance with URI.
/// @param uri URI, e.g. 'tcp://127.0.0.1', 'tcp://127.0.0.1:6379', or 'unix://path/to/socket'.
/// Full URI scheme: 'tcp://[[username:]password@]host[:port][/db]' or
/// unix://[[username:]password@]path-to-unix-domain-socket[/db]
/// @see /~https://github.com/sewenew/redis-plus-plus/issues/37
/// @see /~https://github.com/sewenew/redis-plus-plus#connection
explicit Redis(const std::string &uri) : Redis(Uri(uri)) {}
/// @brief Construct `Redis` instance with Redis sentinel, i.e. get node info from sentinel.
/// @param sentinel `Sentinel` instance.
/// @param master_name Name of master node.
/// @param role Connect to master node or slave node.
/// - Role::MASTER: Connect to master node.
/// - Role::SLAVE: Connect to slave node.
/// @param connection_opts Connection options.
/// @param pool_opts Connection pool options.
/// @see `Sentinel`
/// @see `Role`
/// @see /~https://github.com/sewenew/redis-plus-plus#redis-sentinel
Redis(const std::shared_ptr<Sentinel> &sentinel,
const std::string &master_name,
Role role,
const ConnectionOptions &connection_opts,
const ConnectionPoolOptions &pool_opts = {}) :
_pool(std::make_shared<ConnectionPool>(SimpleSentinel(sentinel, master_name, role),
pool_opts,
connection_opts)) {}
/// @brief `Redis` is not copyable.
Redis(const Redis &) = delete;
/// @brief `Redis` is not copyable.
Redis& operator=(const Redis &) = delete;
/// @brief `Redis` is movable.
Redis(Redis &&) = default;
/// @brief `Redis` is movable.
Redis& operator=(Redis &&) = default;
/// @brief Create a pipeline.
/// @param new_connection Whether creating a `Pipeline` object in a new connection.
/// @return The created pipeline.
/// @note Instead of picking a connection from the underlying connection pool,
/// this method will create a new connection to Redis. So it's not a cheap operation,
/// and you'd better reuse the returned object as much as possible.
/// @see /~https://github.com/sewenew/redis-plus-plus#pipeline
Pipeline pipeline(bool new_connection = true);
/// @brief Create a transaction.
/// @param piped Whether commands in a transaction should be sent in a pipeline to reduce RTT.
/// @param new_connection Whether creating a `Pipeline` object in a new connection.
/// @return The created transaction.
/// @note Instead of picking a connection from the underlying connection pool,
/// this method will create a new connection to Redis. So it's not a cheap operation,
/// and you'd better reuse the returned object as much as possible.
/// @see /~https://github.com/sewenew/redis-plus-plus#transaction
Transaction transaction(bool piped = false, bool new_connection = true);
/// @brief Create a subscriber.
/// @return The created subscriber.
/// @note Instead of picking a connection from the underlying connection pool,
/// this method will create a new connection to Redis. So it's not a cheap operation,
/// and you'd better reuse the returned object as much as possible.
/// @see /~https://github.com/sewenew/redis-plus-plus#publishsubscribe
Subscriber subscriber();
template <typename Cmd, typename ...Args>
auto command(Cmd cmd, Args &&...args)
-> typename std::enable_if<!std::is_convertible<Cmd, StringView>::value, ReplyUPtr>::type;
template <typename ...Args>
auto command(const StringView &cmd_name, Args &&...args)
-> typename std::enable_if<!IsIter<typename LastType<Args...>::type>::value,
ReplyUPtr>::type;
template <typename ...Args>
auto command(const StringView &cmd_name, Args &&...args)
-> typename std::enable_if<IsIter<typename LastType<Args...>::type>::value, void>::type;
template <typename Result, typename ...Args>
Result command(const StringView &cmd_name, Args &&...args);
template <typename Input>
auto command(Input first, Input last)
-> typename std::enable_if<IsIter<Input>::value, ReplyUPtr>::type;
template <typename Result, typename Input>
auto command(Input first, Input last)
-> typename std::enable_if<IsIter<Input>::value, Result>::type;
template <typename Input, typename Output>
auto command(Input first, Input last, Output output)
-> typename std::enable_if<IsIter<Input>::value, void>::type;
// CONNECTION commands.
/// @brief Send password to Redis.
/// @param password Password.
/// @note Normally, you should not call this method.
/// Instead, you should set password with `ConnectionOptions` or URI.
/// @see https://redis.io/commands/auth
void auth(const StringView &password);
/// @brief Send user and password to Redis.
/// @param user User name.
/// @param password Password.
/// @note Normally, you should not call this method.
/// Instead, you should set password with `ConnectionOptions` or URI.
/// Also this overload only works with Redis 6.0 or later.
/// @see https://redis.io/commands/auth
void auth(const StringView &user, const StringView &password);
/// @brief Ask Redis to return the given message.
/// @param msg Message to be sent.
/// @return Return the given message.
/// @see https://redis.io/commands/echo
std::string echo(const StringView &msg);
/// @brief Test if the connection is alive.
/// @return Always return *PONG*.
/// @see https://redis.io/commands/ping
std::string ping();
/// @brief Test if the connection is alive.
/// @param msg Message sent to Redis.
/// @return Return the given message.
/// @see https://redis.io/commands/ping
std::string ping(const StringView &msg);
// After sending QUIT, only the current connection will be close, while
// other connections in the pool is still open. This is a strange behavior.
// So we DO NOT support the QUIT command. If you want to quit connection to
// server, just destroy the Redis object.
//
// void quit();
// We get a connection from the pool, and send the SELECT command to switch
// to a specified DB. However, when we try to send other commands to the
// given DB, we might get a different connection from the pool, and these
// commands, in fact, work on other DB. e.g.
//
// redis.select(1); // get a connection from the pool and switch to the 1th DB
// redis.get("key"); // might get another connection from the pool,
// // and try to get 'key' on the default DB
//
// Obviously, this is NOT what we expect. So we DO NOT support SELECT command.
// In order to select a DB, we can specify the DB index with the ConnectionOptions.
//
// However, since Pipeline and Transaction always send multiple commands on a
// single connection, these two classes have a *select* method.
//
// void select(long long idx);
/// @brief Swap two Redis databases.
/// @param idx1 The index of the first database.
/// @param idx2 The index of the second database.
/// @see https://redis.io/commands/swapdb
void swapdb(long long idx1, long long idx2);
// SERVER commands.
/// @brief Rewrite AOF in the background.
/// @see https://redis.io/commands/bgrewriteaof
void bgrewriteaof();
/// @brief Save database in the background.
/// @return *Background saving started* if BGSAVE started correctly.
/// @see https://redis.io/commands/bgsave
std::string bgsave();
/// @brief Get the size of the currently selected database.
/// @return Number of keys in currently selected database.
/// @see https://redis.io/commands/dbsize
long long dbsize();
/// @brief Remove keys of all databases.
/// @param async Whether flushing databases asynchronously, i.e. without blocking the server.
/// @see https://redis.io/commands/flushall
void flushall(bool async = false);
/// @brief Remove keys of current databases.
/// @param async Whether flushing databases asynchronously, i.e. without blocking the server.
/// @see https://redis.io/commands/flushdb
void flushdb(bool async = false);
/// @brief Get the info about the server.
/// @return Server info.
/// @see https://redis.io/commands/info
std::string info();
/// @brief Get the info about the server on the given section.
/// @param section Section.
/// @return Server info.
/// @see https://redis.io/commands/info
std::string info(const StringView §ion);
/// @brief Get the UNIX timestamp in seconds, at which the database was saved successfully.
/// @return The last saving time.
/// @see https://redis.io/commands/lastsave
long long lastsave();
/// @brief Save databases into RDB file **synchronously**, i.e. block the server during saving.
/// @see https://redis.io/commands/save
void save();
// KEY commands.
/// @brief Delete the given key.
/// @param key Key.
/// @return Number of keys removed.
/// @retval 1 If the key exists, and has been removed.
/// @retval 0 If the key does not exist.
/// @see https://redis.io/commands/del
long long del(const StringView &key);
/// @brief Delete the given list of keys.
/// @param first Iterator to the first key in the range.
/// @param last Off-the-end iterator to the given range.
/// @return Number of keys removed.
/// @see https://redis.io/commands/del
template <typename Input>
long long del(Input first, Input last);
/// @brief Delete the given list of keys.
/// @param il Initializer list of keys to be deleted.
/// @return Number of keys removed.
/// @see https://redis.io/commands/del
template <typename T>
long long del(std::initializer_list<T> il) {
return del(il.begin(), il.end());
}
/// @brief Get the serialized valued stored at key.
/// @param key Key.
/// @return The serialized value.
/// @note If key does not exist, `dump` returns `OptionalString{}` (`std::nullopt`).
/// @see https://redis.io/commands/dump
OptionalString dump(const StringView &key);
/// @brief Check if the given key exists.
/// @param key Key.
/// @return Whether the given key exists.
/// @retval 1 If key exists.
/// @retval 0 If key does not exist.
/// @see https://redis.io/commands/exists
long long exists(const StringView &key);
/// @brief Check if the given keys exist.
/// @param first Iterator to the first key.
/// @param last Off-the-end iterator to the given range.
/// @return Number of keys existing.
/// @see https://redis.io/commands/exists
template <typename Input>
long long exists(Input first, Input last);
/// @brief Check if the given keys exist.
/// @param il Initializer list of keys to be checked.
/// @return Number of keys existing.
/// @see https://redis.io/commands/exists
template <typename T>
long long exists(std::initializer_list<T> il) {
return exists(il.begin(), il.end());
}
/// @brief Set a timeout on key.
/// @param key Key.
/// @param timeout Timeout in seconds.
/// @return Whether timeout has been set.
/// @retval true If timeout has been set.
/// @retval false If key does not exist.
/// @see https://redis.io/commands/expire
bool expire(const StringView &key, long long timeout);
/// @brief Set a timeout on key.
/// @param key Key.
/// @param timeout Timeout in seconds.
/// @return Whether timeout has been set.
/// @retval true If timeout has been set.
/// @retval false If key does not exist.
/// @see https://redis.io/commands/expire
bool expire(const StringView &key, const std::chrono::seconds &timeout);
/// @brief Set a timeout on key, i.e. expire the key at a future time point.
/// @param key Key.
/// @param timestamp Time in seconds since UNIX epoch.
/// @return Whether timeout has been set.
/// @retval true If timeout has been set.
/// @retval false If key does not exist.
/// @see https://redis.io/commands/expireat
bool expireat(const StringView &key, long long timestamp);
/// @brief Set a timeout on key, i.e. expire the key at a future time point.
/// @param key Key.
/// @param timestamp Time in seconds since UNIX epoch.
/// @return Whether timeout has been set.
/// @retval true If timeout has been set.
/// @retval false If key does not exist.
/// @see https://redis.io/commands/expireat
bool expireat(const StringView &key,
const std::chrono::time_point<std::chrono::system_clock,
std::chrono::seconds> ×tamp);
/// @brief Get keys matching the given pattern.
/// @param pattern Pattern.
/// @param output Output iterator to the destination where the returned keys are stored.
/// @note It's always a bad idea to call `keys`, since it might block Redis for a long time,
/// especially when the data set is very big.
/// @see `Redis::scan`
/// @see https://redis.io/commands/keys
template <typename Output>
void keys(const StringView &pattern, Output output);
/// @brief Move a key to the given database.
/// @param key Key.
/// @param db The destination database.
/// @return Whether key has been moved.
/// @retval true If key has been moved.
/// @retval false If key was not moved.
/// @see https://redis.io/commands/move
bool move(const StringView &key, long long db);
/// @brief Remove timeout on key.
/// @param key Key.
/// @return Whether timeout has been removed.
/// @retval true If timeout has been removed.
/// @retval false If key does not exist, or does not have an associated timeout.
/// @see https://redis.io/commands/persist
bool persist(const StringView &key);
/// @brief Set a timeout on key.
/// @param key Key.
/// @param timeout Timeout in milliseconds.
/// @return Whether timeout has been set.
/// @retval true If timeout has been set.
/// @retval false If key does not exist.
/// @see https://redis.io/commands/pexpire
bool pexpire(const StringView &key, long long timeout);
/// @brief Set a timeout on key.
/// @param key Key.
/// @param timeout Timeout in milliseconds.
/// @return Whether timeout has been set.
/// @retval true If timeout has been set.
/// @retval false If key does not exist.
/// @see https://redis.io/commands/pexpire
bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout);
/// @brief Set a timeout on key, i.e. expire the key at a future time point.
/// @param key Key.
/// @param timestamp Time in milliseconds since UNIX epoch.
/// @return Whether timeout has been set.
/// @retval true If timeout has been set.
/// @retval false If key does not exist.
/// @see https://redis.io/commands/pexpireat
bool pexpireat(const StringView &key, long long timestamp);
/// @brief Set a timeout on key, i.e. expire the key at a future time point.
/// @param key Key.
/// @param timestamp Time in milliseconds since UNIX epoch.
/// @return Whether timeout has been set.
/// @retval true If timeout has been set.
/// @retval false If key does not exist.
/// @see https://redis.io/commands/pexpireat
bool pexpireat(const StringView &key,
const std::chrono::time_point<std::chrono::system_clock,
std::chrono::milliseconds> ×tamp);
/// @brief Get the TTL of a key in milliseconds.
/// @param key Key.
/// @return TTL of the key in milliseconds.
/// @see https://redis.io/commands/pttl
long long pttl(const StringView &key);
/// @brief Get a random key from current database.
/// @return A random key.
/// @note If the database is empty, `randomkey` returns `OptionalString{}` (`std::nullopt`).
/// @see https://redis.io/commands/randomkey
OptionalString randomkey();
/// @brief Rename `key` to `newkey`.
/// @param key Key to be renamed.
/// @param newkey The new name of the key.
/// @see https://redis.io/commands/rename
void rename(const StringView &key, const StringView &newkey);
/// @brief Rename `key` to `newkey` if `newkey` does not exist.
/// @param key Key to be renamed.
/// @param newkey The new name of the key.
/// @return Whether key has been renamed.
/// @retval true If key has been renamed.
/// @retval false If newkey already exists.
/// @see https://redis.io/commands/renamenx
bool renamenx(const StringView &key, const StringView &newkey);
/// @brief Create a key with the value obtained by `Redis::dump`.
/// @param key Key.
/// @param val Value obtained by `Redis::dump`.
/// @param ttl Timeout of the created key in milliseconds. If `ttl` is 0, set no timeout.
/// @param replace Whether to overwrite an existing key.
/// If `replace` is `false` and key already exists, throw an exception.
/// @see https://redis.io/commands/restore
void restore(const StringView &key,
const StringView &val,
long long ttl,
bool replace = false);
/// @brief Create a key with the value obtained by `Redis::dump`.
/// @param key Key.
/// @param val Value obtained by `Redis::dump`.
/// @param ttl Timeout of the created key in milliseconds. If `ttl` is 0, set no timeout.
/// @param replace Whether to overwrite an existing key.
/// If `replace` is `false` and key already exists, throw an exception.
/// @see https://redis.io/commands/restore
void restore(const StringView &key,
const StringView &val,
const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0},
bool replace = false);
// TODO: sort
/// @brief Scan keys of the database matching the given pattern.
///
/// Example:
/// @code{.cpp}
/// sw::redis::Cursor cursor = 0;
/// std::unordered_set<std::string> keys;
/// while (true) {
/// cursor = redis.scan(cursor, "pattern:*", 10, std::inserter(keys, keys.begin()));
/// if (cursor == 0) {
/// break;
/// }
/// }
/// @endcode
/// @param cursor Cursor.
/// @param pattern Pattern of the keys to be scanned.
/// @param count A hint for how many keys to be scanned.
/// @param output Output iterator to the destination where the returned keys are stored.
/// @return The cursor to be used for the next scan operation.
/// @see https://redis.io/commands/scan
/// TODO: support the TYPE option for Redis 6.0.
template <typename Output>
Cursor scan(Cursor cursor,
const StringView &pattern,
long long count,
Output output);
/// @brief Scan all keys of the database.
/// @param cursor Cursor.
/// @param output Output iterator to the destination where the returned keys are stored.
/// @return The cursor to be used for the next scan operation.
/// @see https://redis.io/commands/scan
template <typename Output>
Cursor scan(Cursor cursor,
Output output);
/// @brief Scan keys of the database matching the given pattern.
/// @param cursor Cursor.
/// @param pattern Pattern of the keys to be scanned.
/// @param output Output iterator to the destination where the returned keys are stored.
/// @return The cursor to be used for the next scan operation.
/// @see https://redis.io/commands/scan
template <typename Output>
Cursor scan(Cursor cursor,
const StringView &pattern,
Output output);
/// @brief Scan keys of the database matching the given pattern.
/// @param cursor Cursor.
/// @param count A hint for how many keys to be scanned.
/// @param output Output iterator to the destination where the returned keys are stored.
/// @return The cursor to be used for the next scan operation.
/// @see https://redis.io/commands/scan
template <typename Output>
Cursor scan(Cursor cursor,
long long count,
Output output);
/// @brief Update the last access time of the given key.
/// @param key Key.
/// @return Whether last access time of the key has been updated.
/// @retval 1 If key exists, and last access time has been updated.
/// @retval 0 If key does not exist.
/// @see https://redis.io/commands/touch
long long touch(const StringView &key);
/// @brief Update the last access time of the given key.
/// @param first Iterator to the first key to be touched.
/// @param last Off-the-end iterator to the given range.
/// @return Whether last access time of the key has been updated.
/// @retval 1 If key exists, and last access time has been updated.
/// @retval 0 If key does not exist.
/// @see https://redis.io/commands/touch
template <typename Input>
long long touch(Input first, Input last);
/// @brief Update the last access time of the given key.
/// @param il Initializer list of keys to be touched.
/// @return Whether last access time of the key has been updated.
/// @retval 1 If key exists, and last access time has been updated.
/// @retval 0 If key does not exist.
/// @see https://redis.io/commands/touch
template <typename T>
long long touch(std::initializer_list<T> il) {
return touch(il.begin(), il.end());
}
/// @brief Get the remaining Time-To-Live of a key.
/// @param key Key.
/// @return TTL in seconds.
/// @retval TTL If the key has a timeout.
/// @retval -1 If the key exists but does not have a timeout.
/// @retval -2 If the key does not exist.
/// @note In Redis 2.6 or older, `ttl` returns -1 if the key does not exist,
/// or if the key exists but does not have a timeout.
/// @see https://redis.io/commands/ttl
long long ttl(const StringView &key);
/// @brief Get the type of the value stored at key.
/// @param key Key.
/// @return The type of the value.
/// @see https://redis.io/commands/type
std::string type(const StringView &key);
/// @brief Remove the given key asynchronously, i.e. without blocking Redis.
/// @param key Key.
/// @return Whether the key has been removed.
/// @retval 1 If key exists, and has been removed.
/// @retval 0 If key does not exist.
/// @see https://redis.io/commands/unlink
long long unlink(const StringView &key);
/// @brief Remove the given keys asynchronously, i.e. without blocking Redis.
/// @param first Iterator to the first key in the given range.
/// @param last Off-the-end iterator to the given range.
/// @return Number of keys that have been removed.
/// @see https://redis.io/commands/unlink
template <typename Input>
long long unlink(Input first, Input last);
/// @brief Remove the given keys asynchronously, i.e. without blocking Redis.
/// @param il Initializer list of keys to be unlinked.
/// @return Number of keys that have been removed.
/// @see https://redis.io/commands/unlink
template <typename T>
long long unlink(std::initializer_list<T> il) {
return unlink(il.begin(), il.end());
}
/// @brief Wait until previous write commands are successfully replicated to at
/// least the specified number of replicas or the given timeout has been reached.
/// @param numslaves Number of replicas.
/// @param timeout Timeout in milliseconds. If timeout is 0ms, wait forever.
/// @return Number of replicas that have been successfully replicated these write commands.
/// @note The return value might be less than `numslaves`, because timeout has been reached.
/// @see https://redis.io/commands/wait
long long wait(long long numslaves, long long timeout);
/// @brief Wait until previous write commands are successfully replicated to at
/// least the specified number of replicas or the given timeout has been reached.
/// @param numslaves Number of replicas.
/// @param timeout Timeout in milliseconds. If timeout is 0ms, wait forever.
/// @return Number of replicas that have been successfully replicated these write commands.
/// @note The return value might be less than `numslaves`, because timeout has been reached.
/// @see https://redis.io/commands/wait
/// TODO: Support default parameter for `timeout` to wait forever.
long long wait(long long numslaves, const std::chrono::milliseconds &timeout);
// STRING commands.
/// @brief Append the given string to the string stored at key.
/// @param key Key.
/// @param str String to be appended.
/// @return The length of the string after the append operation.
/// @see https://redis.io/commands/append
long long append(const StringView &key, const StringView &str);
/// @brief Get the number of bits that have been set for the given range of the string.
/// @param key Key.
/// @param start Start index (inclusive) of the range. 0 means the beginning of the string.
/// @param end End index (inclusive) of the range. -1 means the end of the string.
/// @return Number of bits that have been set.
/// @note The index can be negative to index from the end of the string.
/// @see https://redis.io/commands/bitcount
long long bitcount(const StringView &key, long long start = 0, long long end = -1);
/// @brief Do bit operation on the string stored at `key`, and save the result to `destination`.
/// @param op Bit operations.
/// @param destination The destination key where the result is saved.
/// @param key The key where the string to be operated is stored.
/// @return The length of the string saved at `destination`.
/// @see https://redis.io/commands/bitop
/// @see `BitOp`
long long bitop(BitOp op, const StringView &destination, const StringView &key);
/// @brief Do bit operations on the strings stored at the given keys,
/// and save the result to `destination`.
/// @param op Bit operations.
/// @param destination The destination key where the result is saved.
/// @param first Iterator to the first key where the string to be operated is stored.
/// @param last Off-the-end iterator to the given range of keys.
/// @return The length of the string saved at `destination`.
/// @see https://redis.io/commands/bitop
/// @see `BitOp`
template <typename Input>
long long bitop(BitOp op, const StringView &destination, Input first, Input last);
/// @brief Do bit operations on the strings stored at the given keys,
/// and save the result to `destination`.
/// @param op Bit operations.
/// @param destination The destination key where the result is saved.
/// @param il Initializer list of keys where the strings are operated.
/// @return The length of the string saved at `destination`.
/// @see https://redis.io/commands/bitop
/// @see `BitOp`
template <typename T>
long long bitop(BitOp op, const StringView &destination, std::initializer_list<T> il) {
return bitop(op, destination, il.begin(), il.end());
}
/// @brief Get the position of the first bit set to 0 or 1 in the given range of the string.
/// @param key Key.
/// @param bit 0 or 1.
/// @param start Start index (inclusive) of the range. 0 means the beginning of the string.
/// @param end End index (inclusive) of the range. -1 means the end of the string.
/// @return The position of the first bit set to 0 or 1.
/// @see https://redis.io/commands/bitpos
long long bitpos(const StringView &key,
long long bit,
long long start = 0,
long long end = -1);
/// @brief Decrement the integer stored at key by 1.
/// @param key Key.
/// @return The value after the decrement.
/// @see https://redis.io/commands/decr
long long decr(const StringView &key);
/// @brief Decrement the integer stored at key by `decrement`.
/// @param key Key.
/// @param decrement Decrement.
/// @return The value after the decrement.
/// @see https://redis.io/commands/decrby
long long decrby(const StringView &key, long long decrement);
/// @brief Get the string value stored at key.
///
/// Example:
/// @code{.cpp}
/// auto val = redis.get("key");
/// if (val)
/// std::cout << *val << std::endl;
/// else
/// std::cout << "key not exist" << std::endl;
/// @endcode
/// @param key Key.
/// @return The value stored at key.
/// @note If key does not exist, `get` returns `OptionalString{}` (`std::nullopt`).
/// @see https://redis.io/commands/get
OptionalString get(const StringView &key);
/// @brief Get the bit value at offset in the string.
/// @param key Key.
/// @param offset Offset.
/// @return The bit value.
/// @see https://redis.io/commands/getbit
long long getbit(const StringView &key, long long offset);
/// @brief Get the substring of the string stored at key.
/// @param key Key.
/// @param start Start index (inclusive) of the range. 0 means the beginning of the string.
/// @param end End index (inclusive) of the range. -1 means the end of the string.
/// @return The substring in range [start, end]. If key does not exist, return an empty string.
/// @see https://redis.io/commands/getrange
std::string getrange(const StringView &key, long long start, long long end);
/// @brief Atomically set the string stored at `key` to `val`, and return the old value.
/// @param key Key.
/// @param val Value to be set.
/// @return The old value stored at key.
/// @note If key does not exist, `getset` returns `OptionalString{}` (`std::nullopt`).
/// @see https://redis.io/commands/getset
/// @see `OptionalString`
OptionalString getset(const StringView &key, const StringView &val);
/// @brief Increment the integer stored at key by 1.
/// @param key Key.
/// @return The value after the increment.
/// @see https://redis.io/commands/incr
long long incr(const StringView &key);
/// @brief Increment the integer stored at key by `increment`.
/// @param key Key.
/// @param increment Increment.
/// @return The value after the increment.
/// @see https://redis.io/commands/incrby
long long incrby(const StringView &key, long long increment);
/// @brief Increment the floating point number stored at key by `increment`.
/// @param key Key.
/// @param increment Increment.
/// @return The value after the increment.
/// @see https://redis.io/commands/incrbyfloat
double incrbyfloat(const StringView &key, double increment);
/// @brief Get the values of multiple keys atomically.
///
/// Example:
/// @code{.cpp}
/// std::vector<std::string> keys = {"k1", "k2", "k3"};
/// std::vector<OptionalString> vals;
/// redis.mget(keys.begin(), keys.end(), std::back_inserter(vals));
/// for (const auto &val : vals) {
/// if (val)
/// std::cout << *val << std::endl;
/// else
/// std::cout << "key does not exist" << std::endl;
/// }
/// @endcode
/// @param first Iterator to the first key of the given range.
/// @param last Off-the-end iterator to the given range.
/// @param output Output iterator to the destination where the values are stored.
/// @note The destination should be a container of `OptionalString` type,
/// since the given key might not exist (in this case, the value of the corresponding
/// key is `OptionalString{}` (`std::nullopt`)).
/// @see https://redis.io/commands/mget
template <typename Input, typename Output>
void mget(Input first, Input last, Output output);
/// @brief Get the values of multiple keys atomically.
///
/// Example:
/// @code{.cpp}
/// std::vector<OptionalString> vals;
/// redis.mget({"k1", "k2", "k3"}, std::back_inserter(vals));
/// for (const auto &val : vals) {
/// if (val)
/// std::cout << *val << std::endl;
/// else
/// std::cout << "key does not exist" << std::endl;
/// }
/// @endcode
/// @param il Initializer list of keys.
/// @param output Output iterator to the destination where the values are stored.
/// @note The destination should be a container of `OptionalString` type,
/// since the given key might not exist (in this case, the value of the corresponding
/// key is `OptionalString{}` (`std::nullopt`)).
/// @see https://redis.io/commands/mget
template <typename T, typename Output>
void mget(std::initializer_list<T> il, Output output) {
mget(il.begin(), il.end(), output);
}
/// @brief Set multiple key-value pairs.
///
/// Example:
/// @code{.cpp}
/// std::vector<std::pair<std::string, std::string>> kvs1 = {{"k1", "v1"}, {"k2", "v2"}};
/// redis.mset(kvs1.begin(), kvs1.end());
/// std::unordered_map<std::string, std::string> kvs2 = {{"k3", "v3"}, {"k4", "v4"}};
/// redis.mset(kvs2.begin(), kvs2.end());
/// @endcode
/// @param first Iterator to the first key-value pair.
/// @param last Off-the-end iterator to the given range.
/// @see https://redis.io/commands/mset
template <typename Input>
void mset(Input first, Input last);
/// @brief Set multiple key-value pairs.
///
/// Example:
/// @code{.cpp}
/// redis.mset({std::make_pair("k1", "v1"), std::make_pair("k2", "v2")});
/// @endcode
/// @param il Initializer list of key-value pairs.
/// @see https://redis.io/commands/mset
template <typename T>
void mset(std::initializer_list<T> il) {
mset(il.begin(), il.end());
}
/// @brief Set the given key-value pairs if all specified keys do not exist.
///
/// Example:
/// @code{.cpp}
/// std::vector<std::pair<std::string, std::string>> kvs1;
/// redis.msetnx(kvs1.begin(), kvs1.end());
/// std::unordered_map<std::string, std::string> kvs2;
/// redis.msetnx(kvs2.begin(), kvs2.end());
/// @endcode
/// @param first Iterator to the first key-value pair.
/// @param last Off-the-end iterator of the given range.
/// @return Whether all keys have been set.
/// @retval true If all keys have been set.
/// @retval false If no key was set, i.e. at least one key already exist.
/// @see https://redis.io/commands/msetnx
template <typename Input>
bool msetnx(Input first, Input last);
/// @brief Set the given key-value pairs if all specified keys do not exist.
///
/// Example:
/// @code{.cpp}
/// redis.msetnx({make_pair("k1", "v1"), make_pair("k2", "v2")});
/// @endcode
/// @param il Initializer list of key-value pairs.
/// @return Whether all keys have been set.
/// @retval true If all keys have been set.
/// @retval false If no key was set, i.e. at least one key already exist.
/// @see https://redis.io/commands/msetnx
template <typename T>
bool msetnx(std::initializer_list<T> il) {
return msetnx(il.begin(), il.end());
}
/// @brief Set key-value pair with the given timeout in milliseconds.
/// @param key Key.
/// @param ttl Time-To-Live in milliseconds.
/// @param val Value.
/// @see https://redis.io/commands/psetex
void psetex(const StringView &key,
long long ttl,
const StringView &val);
/// @brief Set key-value pair with the given timeout in milliseconds.
/// @param key Key.
/// @param ttl Time-To-Live in milliseconds.
/// @param val Value.
/// @see https://redis.io/commands/psetex
void psetex(const StringView &key,
const std::chrono::milliseconds &ttl,
const StringView &val);
/// @brief Set a key-value pair.
///
/// Example:
/// @code{.cpp}
/// // Set a key-value pair.
/// redis.set("key", "value");
/// // Set a key-value pair, and expire it after 10 seconds.
/// redis.set("key", "value", std::chrono::seconds(10));
/// // Set a key-value pair with a timeout, only if the key already exists.
/// if (redis.set("key", "value", std::chrono::seconds(10), UpdateType::EXIST))
/// std::cout << "OK" << std::endl;
/// else
/// std::cout << "key does not exist" << std::endl;
/// @endcode
/// @param key Key.
/// @param val Value.
/// @param ttl Timeout on the key. If `ttl` is 0ms, do not set timeout.
/// @param type Options for set command:
/// - UpdateType::EXIST: Set the key only if it already exists.
/// - UpdateType::NOT_EXIST: Set the key only if it does not exist.
/// - UpdateType::ALWAYS: Always set the key no matter whether it exists.
/// @return Whether the key has been set.
/// @retval true If the key has been set.
/// @retval false If the key was not set, because of the given option.
/// @see https://redis.io/commands/set
bool set(const StringView &key,
const StringView &val,
const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0),
UpdateType type = UpdateType::ALWAYS);
bool set(const StringView &key,
const StringView &val,
bool keepttl,
UpdateType type = UpdateType::ALWAYS);
// TODO: add SETBIT command.
/// @brief Set key-value pair with the given timeout in seconds.
/// @param key Key.
/// @param ttl Time-To-Live in seconds.
/// @param val Value.
/// @see https://redis.io/commands/setex
void setex(const StringView &key,
long long ttl,
const StringView &val);
/// @brief Set key-value pair with the given timeout in seconds.
/// @param key Key.
/// @param ttl Time-To-Live in seconds.
/// @param val Value.
/// @see https://redis.io/commands/setex
void setex(const StringView &key,
const std::chrono::seconds &ttl,
const StringView &val);
/// @brief Set the key if it does not exist.
/// @param key Key.
/// @param val Value.
/// @return Whether the key has been set.
/// @retval true If the key has been set.
/// @retval false If the key was not set, i.e. the key already exists.
/// @see https://redis.io/commands/setnx
bool setnx(const StringView &key, const StringView &val);
/// @brief Set the substring starting from `offset` to the given value.
/// @param key Key.
/// @param offset Offset.
/// @param val Value.
/// @return The length of the string after this operation.
/// @see https://redis.io/commands/setrange
long long setrange(const StringView &key, long long offset, const StringView &val);
/// @brief Get the length of the string stored at key.
/// @param key Key.
/// @return The length of the string.
/// @note If key does not exist, `strlen` returns 0.
/// @see https://redis.io/commands/strlen
long long strlen(const StringView &key);
// LIST commands.
/// @brief Pop the first element of the list in a blocking way.
/// @param key Key where the list is stored.
/// @param timeout Timeout in seconds. 0 means block forever.
/// @return Key-element pair.
/// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`).
/// @see `Redis::lpop`
/// @see https://redis.io/commands/blpop
OptionalStringPair blpop(const StringView &key, long long timeout);
/// @brief Pop the first element of the list in a blocking way.
/// @param key Key where the list is stored.
/// @param timeout Timeout in seconds. 0 means block forever.
/// @return Key-element pair.
/// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`).
/// @see `Redis::lpop`
/// @see https://redis.io/commands/blpop
OptionalStringPair blpop(const StringView &key,
const std::chrono::seconds &timeout = std::chrono::seconds{0});
/// @brief Pop the first element of multiple lists in a blocking way.
/// @param first Iterator to the first key.
/// @param last Off-the-end iterator to the key range.
/// @param timeout Timeout in seconds. 0 means block forever.
/// @return Key-element pair.
/// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`).
/// @see `Redis::lpop`
/// @see https://redis.io/commands/blpop
template <typename Input>
OptionalStringPair blpop(Input first, Input last, long long timeout);