Redis/NewLife.Redis/RedisSortedSet.cs

265 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using NewLife.Data;
using NewLife.Reflection;
namespace NewLife.Caching;
/// <summary>有序集合ZSET</summary>
public class RedisSortedSet<T> : RedisBase
{
#region
/// <summary>个数</summary>
public Int32 Count => Execute((rc, k) => rc.Execute<Int32>("ZCARD", Key));
#endregion
#region
/// <summary>实例化有序集合</summary>
/// <param name="redis"></param>
/// <param name="key"></param>
public RedisSortedSet(Redis redis, String key) : base(redis, key) { }
#endregion
#region
/// <summary>添加元素并指定分数,返回添加到集合的成员数量</summary>
/// <param name="member">元素</param>
/// <param name="score">分数</param>
/// <returns>添加到有序集合的成员数量,不包括已经存在更新分数的成员</returns>
public Int32 Add(T member, Double score) => Execute((rc, k) => rc.Execute<String>("ZADD", Key, score, member), true).ToInt(-1);
/// <summary>添加元素并指定分数,返回添加到集合的成员数量</summary>
/// <param name="member">元素</param>
/// <param name="score">分数</param>
/// <returns>添加到有序集合的成员数量,不包括已经存在更新分数的成员</returns>
public Int32 Add(T member, Int64 score) => Execute((rc, k) => rc.Execute<String>("ZADD", Key, score, member), true).ToInt(-1);
/// <summary>批量添加,返回添加到集合的成员数量</summary>
/// <param name="members"></param>
/// <param name="score"></param>
/// <returns>添加到有序集合的成员数量,不包括已经存在更新分数的成员</returns>
public Int32 Add(IEnumerable<T> members, Double score)
{
var args = new List<Object> { Key };
foreach (var item in members)
{
args.Add(score);
args.Add(item!);
}
return Execute((rc, k) => rc.Execute<String>("ZADD", args.ToArray()), true).ToInt(-1);
}
/// <summary>批量添加,返回添加到集合的成员数量</summary>
/// <param name="members"></param>
/// <param name="score"></param>
/// <returns>添加到有序集合的成员数量,不包括已经存在更新分数的成员</returns>
public Int32 Add(IEnumerable<T> members, Int64 score)
{
var args = new List<Object> { Key };
foreach (var item in members)
{
args.Add(score);
args.Add(item!);
}
return Execute((rc, k) => rc.Execute<String>("ZADD", args.ToArray()), true).ToInt(-1);
}
/// <summary>删除元素</summary>
/// <param name="members"></param>
/// <returns></returns>
public Int32 Remove(params T[] members) => Execute((rc, k) => rc.ExecuteByKey<T, Int32>("ZREM", Key, members), true);
/// <summary>返回有序集key中成员member的score值</summary>
/// <param name="member"></param>
/// <returns></returns>
public Double GetScore(T member) => Execute((r, k) => r.Execute<Double>("ZSCORE", Key, member), false);
#endregion
#region
/// <summary>批量添加</summary>
/// <remarks>
/// 将所有指定成员添加到键为key有序集合sorted set里面。 添加时可以指定多个分数/成员score/member对。
/// 如果指定添加的成员已经是有序集合里面的成员则会更新改成员的分数scrore并更新到正确的排序位置。
///
///
/// ZADD 命令在key后面分数/成员score/member对前面支持一些参数他们是
/// XX: 仅仅更新存在的成员,不添加新成员。
/// NX: 不更新存在的成员。只添加新成员。
/// CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数(CH 是 changed 的意思)。
/// 更改的元素是新添加的成员,已经存在的成员更新分数。 所以在命令中指定的成员有相同的分数将不被计算在内。
/// 注在通常情况下ZADD返回值只计算新添加成员的数量。
/// INCR: 当ZADD指定这个选项时成员的操作就等同ZINCRBY命令对成员的分数进行递增操作。
/// </remarks>
/// <param name="options">支持参数</param>
/// <param name="members"></param>
/// <returns>添加到有序集合的成员数量,不包括已经存在更新分数的成员</returns>
public Double Add(String options, IDictionary<T, Double> members)
{
var args = new List<Object> { Key };
// 支持参数
if (!options.IsNullOrEmpty() && options.EqualIgnoreCase("XX", "NX", "CH", "INCR")) args.Add(options);
foreach (var item in members)
{
args.Add(item.Value);
args.Add(item.Key!);
}
return Execute((rc, k) => rc.Execute<Double>("ZADD", args.ToArray()), true);
}
/// <summary>为有序集key的成员member的score值加上增量increment</summary>
/// <param name="member"></param>
/// <param name="score"></param>
/// <returns></returns>
public Double Increment(T member, Double score) => Execute((rc, k) => rc.Execute<Double>("ZINCRBY", Key, score, member));
/// <summary>删除并返回有序集合key中的最多count个具有最高得分的成员</summary>
/// <param name="count"></param>
/// <returns></returns>
public IDictionary<T, Double> PopMax(Int32 count = 1)
{
var list = Execute((rc, k) => rc.Execute<String[]>("ZPOPMAX", Key, count), true);
if (list == null || list.Length == 0) return null;
var dic = new Dictionary<T, Double>();
for (var i = 0; i < list.Length; i += 2)
{
var member = list[i].ChangeType<T>();
var score = list[i + 1].ToDouble();
dic[member] = score;
}
return dic;
}
/// <summary>删除并返回有序集合key中的最多count个具有最低得分的成员</summary>
/// <param name="count"></param>
/// <returns></returns>
public IDictionary<T, Double> PopMin(Int32 count = 1)
{
var list = Execute((rc, k) => rc.Execute<String[]>("ZPOPMIN", Key, count), true);
if (list == null || list.Length == 0) return null;
var dic = new Dictionary<T, Double>();
for (var i = 0; i < list.Length; i += 2)
{
var member = list[i].ChangeType<T>();
var score = list[i + 1].ToDouble();
dic[member] = score;
}
return dic;
}
/// <summary>
/// 返回有序集key中score值在min和max之间(默认包括score值等于min或max)的成员个数
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
/// <returns></returns>
public Int32 FindCount(Double min, Double max) => Execute((rc, k) => rc.Execute<Int32>("ZCOUNT", Key, min, max));
/// <summary>返回指定范围的列表</summary>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
public T[] Range(Int32 start, Int32 stop) => Execute((r, k) => r.Execute<T[]>("ZRANGE", Key, start, stop));
/// <summary>返回指定范围的成员分数对</summary>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
public IDictionary<T, Double> RangeWithScores(Int32 start, Int32 stop)
{
var dic = new Dictionary<T, Double>();
var rs = Execute((r, k) => r.Execute<String[]>("ZRANGE", Key, start, stop, "WITHSCORES"));
if (rs != null && rs.Length >= 2)
{
for (var i = 0; i < rs.Length - 1; i += 2)
{
var member = rs[i].ChangeType<T>();
var score = rs[i + 1].ToDouble();
dic[member] = score;
}
}
return dic;
}
/// <summary>返回指定分数区间的成员列表,低分到高分排序</summary>
/// <param name="min">低分,包含</param>
/// <param name="max">高分,包含</param>
/// <param name="offset">偏移</param>
/// <param name="count">个数</param>
/// <returns></returns>
public T[] RangeByScore(Double min, Double max, Int32 offset, Int32 count) => Execute((r, k) => r.Execute<T[]>("ZRANGEBYSCORE", Key, min, max, "LIMIT", offset, count));
/// <summary>返回指定分数区间的成员列表,低分到高分排序</summary>
/// <param name="min">低分,包含</param>
/// <param name="max">高分,包含</param>
/// <param name="offset">偏移</param>
/// <param name="count">个数</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
public Task<T[]> RangeByScoreAsync(Double min, Double max, Int32 offset, Int32 count, CancellationToken cancellationToken = default) => ExecuteAsync((r, k) => r.ExecuteAsync<T[]>("ZRANGEBYSCORE", [Key, min, max, "LIMIT", offset, count], cancellationToken));
/// <summary>返回指定分数区间的成员分数对,低分到高分排序</summary>
/// <param name="min">低分,包含</param>
/// <param name="max">高分,包含</param>
/// <param name="offset">偏移</param>
/// <param name="count">个数</param>
/// <returns></returns>
public IDictionary<T, Double> RangeByScoreWithScores(Double min, Double max, Int32 offset, Int32 count)
{
var dic = new Dictionary<T, Double>();
var rs = Execute((r, k) => r.Execute<String[]>("ZRANGEBYSCORE", Key, min, max, "WITHSCORES", "LIMIT", offset, count));
if (rs != null && rs.Length >= 2)
{
for (var i = 0; i < rs.Length - 1; i += 2)
{
var member = rs[i].ChangeType<T>();
var score = rs[i + 1].ToDouble();
dic[member] = score;
}
}
return dic;
}
/// <summary>返回有序集key中成员member的排名。其中有序集成员按score值递增(从小到大)顺序排列</summary>
/// <param name="member"></param>
/// <returns></returns>
public Int32 Rank(T member) => Execute((r, k) => r.Execute<Int32>("ZRANK", Key, member));
/// <summary>模糊搜索,支持?和*</summary>
/// <param name="pattern"></param>
/// <param name="count"></param>
/// <param name="position"></param>
/// <returns></returns>
public virtual IEnumerable<KeyValuePair<T, Double>> Search(String pattern, Int32 count, Int32 position = 0)
{
while (count > 0)
{
var rs = Execute((r, k) => r.Execute<Object[]>("ZSCAN", Key, position, "MATCH", pattern + "", "COUNT", count));
if (rs == null || rs.Length != 2) break;
position = (rs[0] as IPacket)!.ToStr().ToInt();
var ps = rs[1] as Object[];
for (var i = 0; i < ps.Length - 1; i += 2)
{
if (count-- > 0)
{
var item = (ps[i] as IPacket)!.ToStr().ChangeType<T>();
var score = (ps[i + 1] as IPacket)!.ToStr().ToDouble();
yield return new KeyValuePair<T, Double>(item, score);
}
}
if (position == 0) break;
}
}
#endregion
}