Skip to content

Commit 1ae4d4a

Browse files
committed
support for hash field expiration
HPEXPIRETIME, HPTTL, HPERSIST, HGETF, HSETF
1 parent 9b0baec commit 1ae4d4a

File tree

11 files changed

+1236
-13
lines changed

11 files changed

+1236
-13
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace StackExchange.Redis;
6+
7+
/// <summary>
8+
/// Specifies the options to the HSETF command when to create hash/fields and the return data
9+
/// </summary>
10+
[Flags]
11+
public enum HashFieldFlags
12+
{
13+
14+
/// <summary>
15+
/// No options specified.
16+
/// </summary>
17+
None = 0,
18+
/// <summary>
19+
/// When DC (“Don’t Create”) is specified: if key does not exist: do nothing (don’t create key)
20+
/// </summary>
21+
DC = 1,
22+
/// <summary>
23+
/// When DCF (“Don’t Create Fields”) is specified: for each specified field: if the field already exists: set the field's value and expiration time; ignore fields that do not exist
24+
/// </summary>
25+
DCF = 2,
26+
/// <summary>
27+
/// When DOF (“Don’t Overwrite Fields”) is specified: for each specified field: if such field does not exist: create field and set its value and expiration time; ignore fields that already exists
28+
/// </summary>
29+
DOF = 4,
30+
/// <summary>
31+
/// When GETNEW is specified: returns the new value of given fields
32+
/// </summary>
33+
GETNEW = 8,
34+
/// <summary>
35+
/// When GETOLD is specified: returns the old value of given fields
36+
/// </summary>
37+
GETOLD = 16,
38+
}
39+
40+
internal static class HashFieldFlagsExtensions
41+
{
42+
internal static bool isNone(this HashFieldFlags flags) =>
43+
flags == HashFieldFlags.None;
44+
internal static bool isDC(this HashFieldFlags flags) =>
45+
flags.HasFlag(HashFieldFlags.DC);
46+
47+
internal static bool isDCF(this HashFieldFlags flags) =>
48+
flags.HasFlag(HashFieldFlags.DCF);
49+
50+
internal static bool isDOF(this HashFieldFlags flags) =>
51+
flags.HasFlag(HashFieldFlags.DOF);
52+
53+
internal static bool isGETNEW(this HashFieldFlags flags) =>
54+
flags.HasFlag(HashFieldFlags.GETNEW);
55+
56+
internal static bool isGETOLD(this HashFieldFlags flags) =>
57+
flags.HasFlag(HashFieldFlags.GETOLD);
58+
59+
internal static List<RedisValue> ToRedisValueList(this HashFieldFlags flags) =>
60+
flags.isNone() ? new List<RedisValue>() : flags.ToString().Split(',').Select(v => (RedisValue)v).ToList();
61+
62+
}
63+

src/StackExchange.Redis/Enums/RedisCommand.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,25 @@ internal enum RedisCommand
6868
HEXISTS,
6969
HEXPIRE,
7070
HEXPIREAT,
71+
HEXPIRETIME,
7172
HGET,
7273
HGETALL,
74+
HGETF,
7375
HINCRBY,
7476
HINCRBYFLOAT,
7577
HKEYS,
7678
HLEN,
7779
HMGET,
7880
HMSET,
81+
HPERSIST,
7982
HPEXPIRE,
8083
HPEXPIREAT,
84+
HPEXPIRETIME,
85+
HPTTL,
8186
HRANDFIELD,
8287
HSCAN,
8388
HSET,
89+
HSETF,
8490
HSETNX,
8591
HSTRLEN,
8692
HVALS,
@@ -285,12 +291,15 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
285291
case RedisCommand.HDEL:
286292
case RedisCommand.HEXPIRE:
287293
case RedisCommand.HEXPIREAT:
294+
case RedisCommand.HGETF:
288295
case RedisCommand.HINCRBY:
289296
case RedisCommand.HINCRBYFLOAT:
290297
case RedisCommand.HMSET:
298+
case RedisCommand.HPERSIST:
291299
case RedisCommand.HPEXPIRE:
292300
case RedisCommand.HPEXPIREAT:
293301
case RedisCommand.HSET:
302+
case RedisCommand.HSETF:
294303
case RedisCommand.HSETNX:
295304
case RedisCommand.INCR:
296305
case RedisCommand.INCRBY:
@@ -386,11 +395,14 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
386395
case RedisCommand.GETRANGE:
387396
case RedisCommand.HELLO:
388397
case RedisCommand.HEXISTS:
398+
case RedisCommand.HEXPIRETIME:
389399
case RedisCommand.HGET:
390400
case RedisCommand.HGETALL:
391401
case RedisCommand.HKEYS:
392402
case RedisCommand.HLEN:
393403
case RedisCommand.HMGET:
404+
case RedisCommand.HPEXPIRETIME:
405+
case RedisCommand.HPTTL:
394406
case RedisCommand.HRANDFIELD:
395407
case RedisCommand.HSCAN:
396408
case RedisCommand.HSTRLEN:

src/StackExchange.Redis/Interfaces/IDatabase.cs

Lines changed: 193 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,84 @@ public interface IDatabase : IRedis, IDatabaseAsync
394394
/// </returns>
395395
ExpireResult[]? HashFieldExpire(RedisKey key, RedisValue[] hashFields, DateTime expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);
396396

397+
/// <summary>
398+
/// For specified field, it gets the expiration time as a Unix timestamp in milliseconds (milliseconds since the Unix epoch)
399+
/// </summary>
400+
/// <param name="key">The key of the hash.</param>
401+
/// <param name="hashField">The field in the hash to get expire time.</param>
402+
/// <param name="flags">The flags to use for this operation.</param>
403+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given field
404+
/// expiration time: as a UNIX timestamp in milliseconds
405+
/// -1: if field has no associated expiration time
406+
/// -2: no such field
407+
/// </returns>
408+
long? HashFieldExpireTime(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);
409+
410+
/// <summary>
411+
/// For each specified field, it gets the expiration time as a Unix timestamp in milliseconds (milliseconds since the Unix epoch)
412+
/// </summary>
413+
/// <param name="key">The key of the hash.</param>
414+
/// <param name="hashFields">The fields in the hash to get expire time.</param>
415+
/// <param name="flags">The flags to use for this operation.</param>
416+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
417+
/// expiration time: as a UNIX timestamp in milliseconds
418+
/// -1: if field has no associated expiration time
419+
/// -2: no such field
420+
/// </returns>
421+
long[]? HashFieldExpireTime(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
422+
423+
/// <summary>
424+
/// For specified field, it removes the expiration time
425+
/// </summary>
426+
/// <param name="key">The key of the hash.</param>
427+
/// <param name="hashField">The field in the hash to remove expire time.</param>
428+
/// <param name="flags">The flags to use for this operation.</param>
429+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given field
430+
/// 1: if the expiration time was removed
431+
/// -1: if field has no associated expiration time
432+
/// -2: no such field
433+
/// </returns>
434+
long? HashFieldPersist(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);
435+
436+
/// <summary>
437+
/// For each specified field, it removes the expiration time
438+
/// </summary>
439+
/// <param name="key">The key of the hash.</param>
440+
/// <param name="hashFields">The fields in the hash to remove expire time.</param>
441+
/// <param name="flags">The flags to use for this operation.</param>
442+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
443+
/// 1: if the expiration time was removed
444+
/// -1: if field has no associated expiration time
445+
/// -2: no such field
446+
/// </returns>
447+
long[]? HashFieldPersist(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
448+
449+
/// <summary>
450+
/// For specified field, it gets the remaining time to live in milliseconds
451+
/// </summary>
452+
/// <param name="key">The key of the hash.</param>
453+
/// <param name="hashField">The field in the hash to get expire time.</param>
454+
/// <param name="flags">The flags to use for this operation.</param>
455+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given field
456+
/// time to live: in milliseconds
457+
/// -1: if field has no associated expiration time
458+
/// -2: no such field
459+
/// </returns>
460+
long? HashFieldTimeToLive(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);
461+
462+
/// <summary>
463+
/// For each specified field, it gets the remaining time to live in milliseconds
464+
/// </summary>
465+
/// <param name="key">The key of the hash.</param>
466+
/// <param name="hashFields">The fields in the hash to get expire time.</param>
467+
/// <param name="flags">The flags to use for this operation.</param>
468+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
469+
/// time to live: in milliseconds
470+
/// -1: if field has no associated expiration time
471+
/// -2: no such field
472+
/// </returns>
473+
long[]? HashFieldTimeToLive(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
474+
397475
/// <summary>
398476
/// Returns the value associated with field in the hash stored at key.
399477
/// </summary>
@@ -426,6 +504,120 @@ public interface IDatabase : IRedis, IDatabaseAsync
426504
/// <remarks><seealso href="https://redis.io/commands/hmget"/></remarks>
427505
RedisValue[] HashGet(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
428506

507+
/// <summary>
508+
/// For each specified field, it gets the value and sets the field's remaining time to live
509+
/// </summary>
510+
/// <param name="key">The key of the hash.</param>
511+
/// <param name="hashFields">The fields in the hash for this operation.</param>
512+
/// <param name="expireDuration">The time out to set in milliseconds</param>
513+
/// <param name="when">under which condition the expiration will be set using <see cref="ExpireWhen"/>.</param>
514+
/// <param name="flags">The flags to use for this operation.</param>
515+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
516+
/// value: value of field
517+
/// nil: if no such field exists
518+
/// </returns>
519+
RedisValue[]? HashGet(RedisKey key, RedisValue[] hashFields, TimeSpan expireDuration, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);
520+
521+
/// <summary>
522+
/// For each specified field, it gets the value and sets the field's expiration timestamp
523+
/// </summary>
524+
/// <param name="key">The key of the hash.</param>
525+
/// <param name="hashFields">The fields in the hash for this operation.</param>
526+
/// <param name="expireTime">The exact date to expiry to set.</param>
527+
/// <param name="when">under which condition the expiration will be set using <see cref="ExpireWhen"/>.</param>
528+
/// <param name="flags">The flags to use for this operation.</param>
529+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
530+
/// value: value of field
531+
/// nil: if no such field exists
532+
/// </returns>
533+
RedisValue[]? HashGet(RedisKey key, RedisValue[] hashFields, DateTime expireTime, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);
534+
535+
/// <summary>
536+
/// For each specified field, it gets the value and removes the field's expiration
537+
/// </summary>
538+
/// <param name="key">The key of the hash.</param>
539+
/// <param name="hashFields">The fields in the hash for this operation.</param>
540+
/// <param name="flags">The flags to use for this operation.</param>
541+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
542+
/// value: value of field
543+
/// nil: if no such field exists
544+
/// </returns>
545+
RedisValue[]? HashGetPersistFields(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
546+
547+
/// <summary>
548+
/// For each specified field, it sets the value and optionally sets the fields expiration
549+
/// Depending on HashFieldFlags it creates the hash key or its fields in case they dont exist
550+
/// - When DC is not specified: if key does not exist: create key
551+
/// - When DC (“Don’t Create”) is specified: if key does not exist: do nothing (don’t create key)
552+
/// - When neither DCF nor DOF are specified: for each specified field: if such field does not exist: create field; set all fields' value and expiration time
553+
/// - When DCF (“Don’t Create Fields”) is specified: for each specified field: if the field already exists: set the field's value and expiration time; ignore fields that do not exist
554+
/// - When DOF (“Don’t Overwrite Fields”) is specified: for each specified field: if such field does not exist: create field and set its value and expiration time; ignore fields that already exists
555+
/// </summary>
556+
/// <param name="key">The key of the hash.</param>
557+
/// <param name="hashFields">The fields in the hash for this operation.</param>
558+
/// <param name="keepExpiry">Whether to maintain the existing expiration.</param>
559+
/// <param name="fieldFlags"></param>
560+
/// <param name="flags">The flags to use for this operation.</param>
561+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
562+
/// return value depends on the HashFieldFlags (GETNEW/GETOLD)
563+
/// if GETNEW is specified: the value of field after command execution
564+
/// if GETOLD is specified: the value of field before command execution
565+
/// if GETNEW or GETOLD is not specified: a + b where
566+
/// a: 1 if the field's value was set or 0 if not (DCF/DOF met)
567+
/// b: 2 if the field's expiration time was set/discarded or 0 if not (DCF/DOF met, NX/XX/GT/LT not met)
568+
/// </returns>
569+
RedisValue[]? HashSet(RedisKey key, HashEntry[] hashFields, bool keepExpiry, HashFieldFlags fieldFlags = HashFieldFlags.None, CommandFlags flags = CommandFlags.None);
570+
571+
/// <summary>
572+
/// For each specified field, it sets the value and optionally sets the fields expiration
573+
/// Depending on HashFieldFlags it creates the hash key or its fields in case they dont exist
574+
/// - When DC is not specified: if key does not exist: create key
575+
/// - When DC (“Don’t Create”) is specified: if key does not exist: do nothing (don’t create key)
576+
/// - When neither DCF nor DOF are specified: for each specified field: if such field does not exist: create field; set all fields' value and expiration time
577+
/// - When DCF (“Don’t Create Fields”) is specified: for each specified field: if the field already exists: set the field's value and expiration time; ignore fields that do not exist
578+
/// - When DOF (“Don’t Overwrite Fields”) is specified: for each specified field: if such field does not exist: create field and set its value and expiration time; ignore fields that already exists
579+
/// </summary>
580+
/// <param name="key">The key of the hash.</param>
581+
/// <param name="hashFields">The fields in the hash for this operation.</param>
582+
/// <param name="expireDuration">The time out to set in milliseconds</param>
583+
/// <param name="when">under which condition the expiration will be set using <see cref="ExpireWhen"/>.</param>
584+
/// <param name="fieldFlags"></param>
585+
/// <param name="flags">The flags to use for this operation.</param>
586+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
587+
/// return value depends on the HashFieldFlags (GETNEW/GETOLD)
588+
/// if GETNEW is specified: the value of field after command execution
589+
/// if GETOLD is specified: the value of field before command execution
590+
/// if GETNEW or GETOLD is not specified: a + b where
591+
/// a: 1 if the field's value was set or 0 if not (DCF/DOF met)
592+
/// b: 2 if the field's expiration time was set/discarded or 0 if not (DCF/DOF met, NX/XX/GT/LT not met)
593+
/// </returns>
594+
RedisValue[]? HashSet(RedisKey key, HashEntry[] hashFields, TimeSpan expireDuration, ExpireWhen when = ExpireWhen.Always, HashFieldFlags fieldFlags = HashFieldFlags.None, CommandFlags flags = CommandFlags.None);
595+
596+
/// <summary>
597+
/// For each specified field, it sets the value and optionally sets the fields expiration
598+
/// Depending on HashFieldFlags it creates the hash key or its fields in case they dont exist
599+
/// - When DC is not specified: if key does not exist: create key
600+
/// - When DC (“Don’t Create”) is specified: if key does not exist: do nothing (don’t create key)
601+
/// - When neither DCF nor DOF are specified: for each specified field: if such field does not exist: create field; set all fields' value and expiration time
602+
/// - When DCF (“Don’t Create Fields”) is specified: for each specified field: if the field already exists: set the field's value and expiration time; ignore fields that do not exist
603+
/// - When DOF (“Don’t Overwrite Fields”) is specified: for each specified field: if such field does not exist: create field and set its value and expiration time; ignore fields that already exists
604+
/// </summary>
605+
/// <param name="key">The key of the hash.</param>
606+
/// <param name="hashFields">The fields in the hash for this operation.</param>
607+
/// <param name="expireTime">The exact date to expiry to set.</param>
608+
/// <param name="when">under which condition the expiration will be set using <see cref="ExpireWhen"/>.</param>
609+
/// <param name="fieldFlags"></param>
610+
/// <param name="flags">The flags to use for this operation.</param>
611+
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
612+
/// return value depends on the HashFieldFlags (GETNEW/GETOLD)
613+
/// if GETNEW is specified: the value of field after command execution
614+
/// if GETOLD is specified: the value of field before command execution
615+
/// if GETNEW or GETOLD is not specified: a + b where
616+
/// a: 1 if the field's value was set or 0 if not (DCF/DOF met)
617+
/// b: 2 if the field's expiration time was set/discarded or 0 if not (DCF/DOF met, NX/XX/GT/LT not met)
618+
/// </returns>
619+
RedisValue[]? HashSet(RedisKey key, HashEntry[] hashFields, DateTime expireTime, ExpireWhen when = ExpireWhen.Always, HashFieldFlags fieldFlags = HashFieldFlags.None, CommandFlags flags = CommandFlags.None);
620+
429621
/// <summary>
430622
/// Returns all fields and values of the hash stored at key.
431623
/// </summary>
@@ -1697,7 +1889,7 @@ public interface IDatabase : IRedis, IDatabaseAsync
16971889

16981890
/// <inheritdoc cref="SortedSetAdd(RedisKey, RedisValue, double, SortedSetWhen, CommandFlags)" />
16991891
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
1700-
bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when, CommandFlags flags= CommandFlags.None);
1892+
bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when, CommandFlags flags = CommandFlags.None);
17011893

17021894
/// <summary>
17031895
/// Adds the specified member with the specified score to the sorted set stored at key.

0 commit comments

Comments
 (0)