// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Threading.Tasks; using EpicGames.Core; using StackExchange.Redis; namespace EpicGames.Redis { /// /// Represents a typed Redis sorted set with a given key /// /// The type of element stored in the set public record struct RedisSortedSetKey(RedisKey Inner) : IRedisTypedKey { /// /// Implicit conversion to typed redis key. /// /// Key to convert public static implicit operator RedisSortedSetKey(string key) => new RedisSortedSetKey(new RedisKey(key)); } /// /// Typed implementation of /// /// The element type public readonly struct SortedSetEntry : IEquatable>, IComparable, IComparable> { /// /// Accessor for the element type /// public readonly T Element { get; } /// /// The encoded element value /// public readonly RedisValue ElementValue { get; } /// /// Score for the entry /// public readonly double Score { get; } /// /// Constructor /// /// public SortedSetEntry(SortedSetEntry entry) { Element = RedisSerializer.Deserialize(entry.Element)!; ElementValue = entry.Element; Score = entry.Score; } /// /// Constructor /// /// /// public SortedSetEntry(T element, double score) { Element = element; ElementValue = RedisSerializer.Serialize(element); Score = score; } /// /// Deconstruct this item into a tuple /// /// /// public void Deconstruct(out T outElement, out double outScore) { outElement = Element; outScore = Score; } /// public override bool Equals(object? obj) => obj is SortedSetEntry ssObj && Equals(ssObj); /// public override int GetHashCode() => HashCode.Combine(Score, Element); /// public bool Equals(SortedSetEntry other) => Score == other.Score && Equals(Element, other.Element); /// public int CompareTo(SortedSetEntry other) => Score.CompareTo(other.Score); /// public int CompareTo(object? obj) => obj is SortedSetEntry ssObj ? CompareTo(ssObj) : -1; /// public static bool operator ==(SortedSetEntry a, SortedSetEntry b) => a.Equals(b); /// public static bool operator !=(SortedSetEntry a, SortedSetEntry b) => !a.Equals(b); /// public static bool operator <=(SortedSetEntry a, SortedSetEntry b) => a.CompareTo(b) <= 0; /// public static bool operator <(SortedSetEntry a, SortedSetEntry b) => a.CompareTo(b) < 0; /// public static bool operator >=(SortedSetEntry a, SortedSetEntry b) => a.CompareTo(b) >= 0; /// public static bool operator >(SortedSetEntry a, SortedSetEntry b) => a.CompareTo(b) > 0; } /// /// Extension methods for sets /// public static class RedisSortedSetKeyExtensions { #region Conditions /// public static Condition SortedSetContains(this RedisSortedSetKey key, TElement value) => Condition.SortedSetContains(key.Inner, RedisSerializer.Serialize(value)); /// public static Condition SortedSetEqual(this RedisSortedSetKey key, TElement value, RedisValue score) => Condition.SortedSetEqual(key.Inner, RedisSerializer.Serialize(value), score); /// public static Condition SortedSetLengthEqual(this RedisSortedSetKey key, long length) => Condition.SortedSetLengthEqual(key.Inner, length); /// public static Condition SortedSetLengthGreaterThan(this RedisSortedSetKey key, long length) => Condition.SortedSetLengthGreaterThan(key.Inner, length); /// public static Condition SortedSetLengthLessThan(this RedisSortedSetKey key, long length) => Condition.SortedSetLengthLessThan(key.Inner, length); /// public static Condition SortedSetNotContains(this RedisSortedSetKey key, TElement value) => Condition.SortedSetNotContains(key.Inner, RedisSerializer.Serialize(value)); /// public static Condition SortedSetNotEqual(this RedisSortedSetKey key, TElement value, RedisValue score) => Condition.SortedSetNotEqual(key.Inner, RedisSerializer.Serialize(value), score); /// public static Condition SortedSetScoreExists(this RedisSortedSetKey key, RedisValue score) => Condition.SortedSetScoreExists(key.Inner, score); /// public static Condition SortedSetScoreExists(this RedisSortedSetKey key, RedisValue score, RedisValue count) => Condition.SortedSetScoreExists(key.Inner, score, count); #endregion #region SortedSetAddAsync /// public static Task SortedSetAddAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement value, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None) { return target.SortedSetAddAsync(key.Inner, RedisSerializer.Serialize(value), score, when, flags); } /// public static Task SortedSetAddAsync(this IDatabaseAsync target, RedisSortedSetKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None) { return target.SortedSetAddAsync(key.Inner, values.ConvertAll(x => new SortedSetEntry(x.ElementValue, x.Score)), when, flags); } #endregion #region SortedSetLengthAsync /// public static Task SortedSetLengthAsync(this IDatabaseAsync target, RedisSortedSetKey key, double min = Double.NegativeInfinity, double max = Double.PositiveInfinity, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { return target.SortedSetLengthAsync(key.Inner, min, max, exclude, flags); } #endregion #region SortedSetLengthByValueAsync /// public static Task SortedSetLengthByValueAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement min, TElement max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { return target.SortedSetLengthByValueAsync(key.Inner, RedisSerializer.Serialize(min), RedisSerializer.Serialize(max), exclude, flags); } #endregion #region SortedSetRandomMemberAsync /// public static Task SortedSetRandomMemberAsync(this IDatabaseAsync target, RedisSortedSetKey key, CommandFlags flags = CommandFlags.None) { return target.SortedSetRandomMemberAsync(key.Inner, flags).DeserializeAsync(); } #endregion #region SortedSetRandomMembersAsync /// public static Task SortedSetRandomMembersAsync(this IDatabaseAsync target, RedisSortedSetKey key, long count, CommandFlags flags = CommandFlags.None) { return target.SortedSetRandomMembersAsync(key.Inner, count, flags).DeserializeAsync(); } #endregion #region SortedSetRangeByRankAsync /// public static Task SortedSetRangeByRankAsync(this IDatabaseAsync target, RedisSortedSetKey key, long start = 0, long stop = -1, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { return target.SortedSetRangeByRankAsync(key.Inner, start, stop, order, flags).DeserializeAsync(); } #endregion #region SortedSetRangeByRankWithScoresAsync /// public static async Task[]> SortedSetRangeByRankWithScoresAsync(this IDatabaseAsync target, RedisSortedSetKey key, long start = 0, long stop = -1, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { SortedSetEntry[] values = await target.SortedSetRangeByRankWithScoresAsync(key.Inner, start, stop, order, flags); return Array.ConvertAll(values, x => new SortedSetEntry(x)); } #endregion #region SortedSetRangeByScoreAsync /// public static Task SortedSetRangeByScoreAsync(this IDatabaseAsync target, RedisSortedSetKey key, double start = Double.NegativeInfinity, double stop = Double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { return target.SortedSetRangeByScoreAsync(key.Inner, start, stop, exclude, order, skip, take, flags).DeserializeAsync(); } #endregion #region SortedSetRangeByScoreWithScoresAsync /// public static async Task[]> SortedSetRangeByScoreWithScoresAsync(this IDatabaseAsync target, RedisSortedSetKey key, double start = Double.NegativeInfinity, double stop = Double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { SortedSetEntry[] values = await target.SortedSetRangeByScoreWithScoresAsync(key.Inner, start, stop, exclude, order, skip, take, flags); return Array.ConvertAll(values, x => new SortedSetEntry(x)); } #endregion #region SortedSetRangeByValueAsync /// public static Task SortedSetRangeByValueAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement min, TElement max, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { return target.SortedSetRangeByValueAsync(key.Inner, RedisSerializer.Serialize(min), RedisSerializer.Serialize(max), exclude, order, skip, take, flags).DeserializeAsync(); } #endregion #region SortedSetRankAsync /// public static Task SortedSetRankAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement item, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { return target.SortedSetRankAsync(key.Inner, RedisSerializer.Serialize(item), order, flags); } #endregion #region SortedSetRemoveAsync /// public static Task SortedSetRemoveAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement value, CommandFlags flags = CommandFlags.None) { return target.SortedSetRemoveAsync(key.Inner, RedisSerializer.Serialize(value), flags); } /// public static Task SortedSetRemoveAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement[] values, CommandFlags flags = CommandFlags.None) { return target.SortedSetRemoveAsync(key.Inner, RedisSerializer.Serialize(values), flags); } #endregion #region SortedSetRemoveRangeByRankAsync /// public static Task SortedSetRemoveRangeByRankAsync(this IDatabaseAsync target, RedisSortedSetKey key, long start, long stop, CommandFlags flags = CommandFlags.None) { return target.SortedSetRemoveRangeByRankAsync(key.Inner, start, stop, flags); } #endregion #region SortedSetRemoveRangeByScoreAsync /// public static Task SortedSetRemoveRangeByScoreAsync(this IDatabaseAsync target, RedisSortedSetKey key, double start, double stop, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { return target.SortedSetRemoveRangeByScoreAsync(key.Inner, start, stop, exclude, flags); } #endregion #region SortedSetRemoveRangeByValueAsync /// public static Task SortedSetRemoveRangeByValueAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement min, TElement max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { return target.SortedSetRemoveRangeByValueAsync(key.Inner, RedisSerializer.Serialize(min), RedisSerializer.Serialize(max), exclude, flags); } #endregion #region SortedSetScanAsync /// public static async IAsyncEnumerable> SortedSetScanAsync(this IDatabaseAsync target, RedisSortedSetKey key, RedisValue pattern = default, int pageSize = 250, long cursor = 0, int pageOffset = 0, CommandFlags flags = CommandFlags.None) { await foreach (SortedSetEntry entry in target.SortedSetScanAsync(key.Inner, pattern, pageSize, cursor, pageOffset, flags)) { yield return new SortedSetEntry(entry); } } #endregion #region SortedSetScoreAsync /// public static Task SortedSetScoreAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement member, CommandFlags flags = CommandFlags.None) { return target.SortedSetScoreAsync(key.Inner, RedisSerializer.Serialize(member), flags); } #endregion #region SortedSetUpdateAsync /// public static Task SortedSetUpdateAsync(this IDatabaseAsync target, RedisSortedSetKey key, TElement member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None) { return target.SortedSetUpdateAsync(key.Inner, RedisSerializer.Serialize(member), score, when, flags); } /// public static Task SortedSetUpdateAsync(this IDatabaseAsync target, RedisSortedSetKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None) { return target.SortedSetUpdateAsync(key.Inner, values.ConvertAll(x => new SortedSetEntry(RedisSerializer.Serialize(x.Element), x.Score)), when, flags); } #endregion } }