265 lines
11 KiB
C#
265 lines
11 KiB
C#
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
|
||
} |