github.com/15mga/kiwi@v0.0.2-0.20240324021231-b95d5c3ac751/util/rds/redis.go (about) 1 package rds 2 3 import ( 4 "strconv" 5 "strings" 6 7 "github.com/15mga/kiwi/util" 8 "github.com/go-redsync/redsync/v4" 9 "github.com/gomodule/redigo/redis" 10 ) 11 12 const ( 13 DEL = "DEL" // DEL 用于在key存在时删除key 14 DUMP = "DUMP" // DUMP 返回指定key序列化的值 15 EXIST = "EXIST" // EXIST key是否存在 16 EXPIRE = "EXPIRE" // EXPIRE 设置秒为单位的过期时间 17 EXPIREAT = "EXPIREAT" // EXPIREAT 设置时间戳为过期时间 18 PEXPIRE = "PEXPIRE" // PEXPIRE 设置毫秒为单位的过期时间 19 KEYS = "KEYS" // KEYS 获取所有符合模式的键 20 MOVE = "MOVE" // MOVE 将当前数据库的键值对移动到指定数据库 21 PERSIST = "PERSIST" // PERSIST 移除key的过期时间 22 PTTL = "PTTL" // PTTL 返回key以毫秒为单位的过期时间 23 TTL = "TTL" // TTL 返回key以秒为单位的过期时间 24 RENAME = "RENAME" // RENAME 修改key名称 25 RENAMENX = "RENAMENX" // RENAMENX 将key修改为不存在的新key 26 TYPE = "TYPE" // TYPE 返回key对应值的类型 27 SCAN = "SCAN" // SCAN 迭代数据库中的数据库键 28 MATCH = "MATCH" // MATCH 匹配 29 30 SET = "SET" // SET 设置健值 31 GET = "GET" // GET 获取指定key的值 32 GETRANGE = "GETRANGE" // GETRANGE 返回指定key的值的子字符串 33 GETSET = "GETSET" // GETSET 将给定key的值设为value,并返回key的旧值 34 GETBIT = "GETBIT" // GETBIT 对 key 所储存的字符串值,获取指定偏移量上的位(bit) 35 MGET = "MGET" // MGET 获取所有(一个或多个)给定 key 的值。 36 SETBIT = "SETBIT" // SETBIT 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit) 37 SETEX = "SETEX" // SETEX 将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。 38 SETNX = "SETNX " // SETNX 只有在 key 不存在时设置 key 的值 39 SETRANGE = "SETRANGE" // SETRANGE 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始 40 STRLEN = "STRLEN" // STRLEN 返回 key 所储存的字符串值的长度 41 MSET = "MSET" // MSET 同时设置一个或多个 key-value 对 42 MSETNX = "MSETNX" // MSETNX 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在 43 PSETEX = "PSETEX" // PSETEX 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位 44 INCR = "INCR" // INCR 将 key 中储存的数字值增一 45 INCRBY = "INCRBY" // INCRBY 将 key 所储存的值加上给定的增量值(increment) 46 INCRBYFLOAT = "INCRBYFLOAT" // INCRBYFLOAT 将 key 所储存的值加上给定的浮点增量值(increment) 47 DECR = "DECR" // DECR 将 key 中储存的数字值减一 48 DECRBY = "DECRBY" // DECRBY key 所储存的值减去给定的减量值(decrement) 49 APPEND = "APPEND" // APPEND 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾 50 51 HDEL = "HDEL" // HDEL 删除一个或多个哈希表字段 52 HEXISTS = "HEXISTS" // HEXISTS 查看哈希表 key 中,指定的字段是否存在 53 HGET = "HGET" // HGET 获取存储在哈希表中指定字段的值 54 HGETALL = "HGETALL" // HGETALL 获取在哈希表中指定 key 的所有字段和值 55 HINCRBY = "HINCRBY" // HINCRBY 为哈希表 key 中的指定字段的整数值加上增量 increment 56 HINCRBYFLOAT = "HINCRBYFLOAT" // HINCRBYFLOAT 为哈希表 key 中的指定字段的浮点数值加上增量 increment 57 HKEYS = "HKEYS" // HKEYS 获取所有哈希表中的字段 58 HLEN = "HLEN" // HLEN 获取哈希表中字段的数量 59 HMGET = "HMGET" // HMGET 获取所有给定字段的值 60 HMSET = "HMSET" // HMSET 同时将多个 field-value (域-值)对设置到哈希表 key 中 61 HSET = "HSET" // HSET 将哈希表 key 中的字段 field 的值设为 value 62 HSETNX = "HSETNX" // HSETNX 只有在字段 field 不存在时,设置哈希表字段的值 63 HVALS = "HVALS" // HVALS 获取哈希表中所有值 64 HSCAN = "HSCAN" // HSCAN 迭代哈希表中的键值对 65 66 BLPOP = "BLPOP" // 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 67 BRPOP = "BRPOP" // 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 68 BRPOPLPUSH = "BRPOPLPUSH" // 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 69 LINDEX = "LINDEX" // 通过索引获取列表中的元素 70 LINSERT = "LINSERT" // 在列表的元素前或者后插入元素 71 BEFORE = "BEFORE" 72 AFTER = "AFTER" 73 LLEN = "LLEN" // 获取列表长度 74 LPOP = "LPOP" // 移出并获取列表的第一个元素 75 LPUSH = "LPUSH" // 将一个或多个值插入到列表头部 76 LPUSHX = "LPUSHX" // 将一个值插入到已存在的列表头部 77 LRANGE = "LRANGE" // 获取列表指定范围内的元素 78 LREM = "LREM" // 移除列表元素 79 LSET = "LSET" // 通过索引设置列表元素的值 80 LTRIM = "LTRIM" // 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除 81 RPOP = "RPOP" // 移除列表的最后一个元素,返回值为移除的元素 82 RPOPLPUSH = "RPOPLPUSH" // 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 83 RPUSH = "RPUSH" // 在列表中添加一个或多个值 84 RPUSHX = "RPUSHX" // 为已存在的列表添加值 85 86 SADD = "SADD" // SADD 向集合添加一个或多个成员 87 SCARD = "SCARD" // SCARD 获取集合的成员数 88 SDIFF = "SDIFF" // SDIFF 返回第一个集合与其他集合之间的差异 89 SDIFFSTORE = "SDIFFSTORE" // SDIFFSTORE 返回给定所有集合的差集并存储在 destination 中 90 SINTER = "SINTER" // SINTER 返回给定所有集合的交集 91 SINTERSTORE = "SINTERSTORE" // SINTERSTORE 返回给定所有集合的交集并存储在 destination 中 92 SISMEMBER = "SISMEMBER" // SISMEMBER 判断 member 元素是否是集合 key 的成员 93 SMEMBERS = "SMEMBERS" // SMEMBERS 返回集合中的所有成员 94 SMOVE = "SMOVE" // SMOVE 将 member 元素从 source 集合移动到 destination 集合 95 SPOP = "SPOP" // SPOP 移除并返回集合中的一个随机元素 96 SRANDMEMBER = "SRANDMEMBER" // SRANDMEMBER 返回集合中一个或多个随机数 97 SREM = "SREM" // SREM 移除集合中一个或多个成员 98 SUNION = "SUNION" // SUNION 返回所有给定集合的并集 99 SUNIONSTORE = "SUNIONSTORE" // SUNIONSTORE 所有给定集合的并集存储在 destination 集合中 100 SSCAN = "SSCAN" // SSCAN 迭代集合中的元素 101 102 ZADD = "ZADD" // ZADD 向有序集合添加一个或多个成员,或者更新已存在成员的分数 103 ZCARD = "ZCARD" // ZCARD 获取有序集合的成员数 104 ZCOUNT = "ZCOUNT" // ZCOUNT 计算在有序集合中指定区间分数的成员数 105 ZINCRBY = "ZINCRBY" // ZINCRBY 有序集合中对指定成员的分数加上增量 increment 106 ZINTERSTOR = "ZINTERSTOR" // ZINTERSTOR 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中 107 ZLEXCOUNT = "ZLEXCOUNT" // ZLEXCOUNT 在有序集合中计算指定字典区间内成员数量 108 ZRANGE = "ZRANGE" // ZRANGE 通过索引区间返回有序集合指定区间内的成员 109 ZRANGEBYLEX = "ZRANGEBYLEX" // ZRANGEBYLEX 通过字典区间返回有序集合的成员 110 ZRANGEBYSCORE = "ZRANGEBYSCORE" // ZRANGEBYSCORE 通过分数返回有序集合指定区间内的成员 111 ZRANK = "ZRANK" // ZRANK 返回有序集合中指定成员的索引 112 ZREM = "ZREM" // ZREM 移除有序集合中的一个或多个成员 113 ZREMRANGEBYLEX = "ZREMRANGEBYLEX" // ZREMRANGEBYLEX 移除有序集合中给定的字典区间的所有成员 114 ZREMRANGEBYRANK = "ZREMRANGEBYRANK" // ZREMRANGEBYRANK 移除有序集合中给定的排名区间的所有成员 115 ZREMRANGEBYSCORE = "ZREMRANGEBYSCORE" // ZREMRANGEBYSCORE 移除有序集合中给定的分数区间的所有成员 116 ZREVRANGE = "ZREVRANGE" // ZREVRANGE 返回有序集中指定区间内的成员,通过索引,分数从高到低 117 ZREVRANGEBYSCORE = "ZREVRANGEBYSCORE" // ZREVRANGEBYSCORE 返回有序集中指定分数区间内的成员,分数从高到低排序 118 ZREVRANK = "ZREVRANK" // ZREVRANK 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 119 ZSCORE = "ZSCORE" // ZSCORE 返回有序集中,成员的分数值 120 ZUNIONSTORE = "ZUNIONSTORE" // ZUNIONSTORE 计算给定的一个或多个有序集的并集,并存储在新的 key 中 121 ZSCAN = "ZSCAN" // ZSCAN 迭代有序集合中的元素(包括元素成员和元素分值) 122 123 PFADD = "PFADD" // PFADD 添加指定元素到 HyperLogLog 中 124 PFCOUNT = "PFCOUNT" // PFCOUNT 返回给定 HyperLogLog 的基数估算值 125 PFMERGE = "PFMERGE" // PFMERGE 将多个 HyperLogLog 合并为一个 HyperLogLog 126 127 PSUBSCRIBE = "PSUBSCRIBE" // PSUBSCRIBE 订阅一个或多个符合给定模式的频道 128 PUBSUB = "PUBSUB" // PUBSUB 查看订阅与发布系统状态 129 PUBLISH = "PUBLISH" // PUBLISH 将信息发送到指定的频道 130 PUNSUBSCRIBE = "PUNSUBSCRIBE" // PUNSUBSCRIBE 退订所有给定模式的频道 131 SUBSCRIBE = "SUBSCRIBE" // SUBSCRIBE 订阅给定的一个或多个频道的信息 132 UNSUBSCRIBE = "UNSUBSCRIBE" // UNSUBSCRIBE 指退订给定的频道 133 134 DISCARD = "DISCARD" // DISCARD 取消事务,放弃执行事务块内的所有命令 135 EXEC = "EXEC" // EXEC 执行所有事务块内的命令 136 MULTI = "MULTI" // MULTI 标记一个事务块的开始 137 UNWATCH = "UNWATCH" // UNWATCH 取消 WATCH 命令对所有 key 的监视 138 WATCH = "WATCH" // 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断 139 140 EVAL = "EVAL" // EVAL 执行 Lua 脚本 141 EVALSHA = "EVALSHA" // EVALSHA 执行 Lua 脚本 142 SCRIPT_EXISTS = "SCRIPT EXISTS" // SCRIPT_EXISTS 查看指定的脚本是否已经被保存在缓存当中 143 SCRIPT_FLUSH = "SCRIPT FLUSH" // SCRIPT_FLUSH 从脚本缓存中移除所有脚本 144 SCRIPT_KILL = "SCRIPT KILL" // SCRIPT_KILL 杀死当前正在运行的 Lua 脚本 145 SCRIPT_LOAD = "SCRIPT LOAD" // SCRIPT_LOAD 将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本 146 147 AUTH = "AUTH" // AUTH 验证密码是否正确 148 ECHO = "ECHO" // ECHO 打印字符串 149 PING = "PING" // PING 查看服务是否运行 150 QUIT = "QUIT" // QUIT 关闭当前连接 151 SELECT = "SELECT" // SELECT 切换到指定的数据库 152 153 BGREWRITEAOF = "BGREWRITEAOF" // BGREWRITEAOF 异步执行一个 AOF(AppendOnly File) 文件重写操作 154 BGSAVE = "BGSAVE" // BGSAVE 在后台异步保存当前数据库的数据到磁盘 155 CLIENT_KILL = "CLIENT KILL" // CLIENT_KILL 关闭客户端连接 156 CLIENT_LIST = "CLIENT LIST" // CLIENT_LIST 获取连接到服务器的客户端连接列表 157 CLIENT_GETNAME = "CLIENT GETNAME" // CLIENT_GETNAME 获取连接的名称 158 CLIENT_PAUSE = "CLIENT PAUSE" // CLIENT_PAUSE 在指定时间内终止运行来自客户端的命令 159 CLIENT_SETNAME = "CLIENT SETNAME" // CLIENT_SETNAME 设置当前连接的名称 160 CLUSTER_SLOTS = "CLUSTER SLOTS" // CLUSTER_SLOTS 获取集群节点的映射数组 161 COMMAND = "COMMAND" // COMMAND 获取 Redis 命令详情数组 162 COMMAND_COUNT = "COMMAND COUNT" // COMMAND_COUNT 获取 Redis 命令总数 163 COMMAND_GETKEYS = "COMMAND GETKEYS" // COMMAND_GETKEYS 获取给定命令的所有键 164 TIME = "TIME" // TIME 返回当前服务器时间 165 COMMAND_INFO = "COMMAND INFO" // COMMAND_INFO 获取指定 Redis 命令描述的数组 166 CONFIG_GET = "CONFIG GET" // CONFIG_GET 获取指定配置参数的值 167 CONFIG_REWRITE = "CONFIG REWRITE" // CONFIG_REWRITE 对启动 Redis 服务器时所指定的 redis.conf 配置文件进行改写 168 CONFIG_SET = "CONFIG SET" // CONFIG_SET 修改 redis 配置参数,无需重启 169 CONFIG_RESETSTAT = "CONFIG RESETSTAT" // CONFIG_RESETSTAT 重置 INFO 命令中的某些统计数据 170 DBSIZE = "DBSIZE" // DBSIZE 返回当前数据库的 key 的数量 171 DEBUG_OBJECT = "DEBUG OBJECT" // DEBUG_OBJECT 获取 key 的调试信息 172 DEBUG_SEGFAULT = "DEBUG SEGFAULT" // DEBUG_SEGFAULT 让 Redis 服务崩溃 173 FLUSHALL = "FLUSHALL" // FLUSHALL 删除所有数据库的所有key 174 FLUSHDB = "FLUSHDB" // FLUSHDB 删除当前数据库的所有key 175 INFO = "INFO" // INFO 获取 Redis 服务器的各种信息和统计数值 176 LASTSAVE = "LASTSAVE" // LASTSAVE 返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示 177 MONITOR = "MONITOR" // MONITOR 实时打印出 Redis 服务器接收到的命令,调试用 178 ROLE = "ROLE" // ROLE 返回主从实例所属的角色 179 SAVE = "SAVE" // SAVE 同步保存数据到硬盘 180 SHUTDOWN = "SHUTDOWN" // SHUTDOWN 异步保存数据到硬盘,并关闭服务器 181 SLAVEOF = "SLAVEOF" // SLAVEOF 将当前服务器转变为指定服务器的从属服务器(slave server) 182 SLOWLOG = "SLOWLOG" // SLOWLOG 管理 redis 的慢日志 183 SYNC = "SYNC" // SYNC 用于复制功能(replication)的内部命令 184 185 GEOADD = "GEOADD" // GEOADD 存储指定的地理空间位置 186 GEOPOS = "GEOPOS " // GEOPOS 获取key的地理位置 187 GEODIST = "GEODIST" // GEODIST 返回2个点之间的距离 188 GEORADIUS = "GEORADIUS" // GEORADIUS 返回以指定经纬中心范围内的的地理位置 189 GEORADIUSBYMEMBER = "GEORADIUSBYMEMBER" // GEORADIUSBYMEMBER 返回以指定位置中心范围内的的地理位置 190 GEOHASH = "GEOHASH" // GEOHASH 使用 geohash 来保存地理位置的坐标 191 192 XADD = "XADD" // XADD 添加消息到末尾 193 XTRIM = "XTRIM" // XTRIM 对流进行修剪,限制长度 194 XDEL = "XDEL" // XDEL 删除消息 195 XLEN = "XLEN" // XLEN 获取流包含的元素数量,即消息长度 196 XRANGE = "XRANGE" // XRANGE 获取消息列表,会自动过滤已经删除的消息 197 XREVRANGE = "XREVRANGE" // XREVRANGE 反向获取消息列表,ID 从大到小 198 XREAD = "XREAD" // XREAD 以阻塞或非阻塞方式获取消息列表 199 STREAMS = "STREAMS" // STREAMS 流 200 COUNT = "COUNT" // COUNT 数量 201 XGROUP = "XGROUP" // XGROUP 消费者组 202 GROUP = "GROUP" // GROUP 消费者组 203 CREATE = "CREATE" // CREATE 创建 204 CREATECONSUMER = "CREATECONSUMER" // CREATECONSUMER 创建消费者 205 XREADGROUP = "XREADGROUP" // XREADGROUP 读取消费者组中的消息 206 XACK = "XACK" // XACK 将消息标记为已处理 207 XGROUP_SETID = "XGROUP SETID" // XGROUP_SETID 为消费者组设置新的最后递送消息ID 208 XGROUP_DELCONSUMER = "XGROUP DELCONSUMER" // XGROUP_DELCONSUMER 删除消费者 209 XGROUP_DESTROY = "XGROUP DESTROY" // XGROUP_DESTROY 删除消费者组 210 XPENDING = "XPENDING" // XPENDING 显示待处理消息的相关信息 211 XCLAIM = "XCLAIM" // XCLAIM 转移消息的归属权 212 XINFO = "XINFO" // XINFO 查看流和消费者组的相关信息 213 XINFO_GROUPS = "XINFO GROUPS" // XINFO_GROUPS 打印消费者组的信息 214 XINFO_STREAM = "XINFO STREAM" // XINFO_STREAM 打印流信息 215 216 Json = "$" 217 JSON_ARR_APPEND = "JSON.ARRAPPEND" //json数组添加元素 218 JSON_ARR_INDEX = "JSON.ARRINDEX" // 219 JSON_ARR_INSERT = "JSON.ARRINSERT" // 220 JSON_ARR_LEN = "JSON.ARRLEN" // 221 JSON_ARR_POP = "JSON.ARRPOP" // 222 JSON_ARR_TRIM = "JSON.ARRTRIM" // 223 JSON_CLEAR = "JSON.CLEAR" // 224 JSON_DEBUG = "JSON.DEBUG" // 225 JSON_DEBUG_MEMORY = "JSON.DEBUG MEMORY" // 226 JSON_DEL = "JSON.DEL" // 227 JSON_FORGET = "JSON.FORGET" // 228 JSON_GET = "JSON.GET" // 229 JSON_MGET = "JSON.MGET" // 230 JSON_NUM_INCRBY = "JSON.NUMINCRBY" // 231 JSON_NUM_MULTBY = "JSON.NUMMULTBY" // 232 JSON_OBJ_KEYS = "JSON.OBJKEYS" // 233 JSON_OBJLEN = "JSON.OBJLEN" // 234 JSON_RESP = "JSON.RESP" // 235 JSON_SET = "JSON.SET" // 236 JSON_STR_APPEND = "JSON.STRAPPEND" // 237 JSON_STR_LEN = "JSON.STRLEN" // 238 JSON_TOGGLE = "JSON.TOGGLE" // 239 JSON_TYPE = "JSON.TYPE" // 240 ) 241 242 type ( 243 ToRedisConn func() redis.Conn 244 ToRedisConnError func() (redis.Conn, error) 245 ToRedisConnErr func() (redis.Conn, *util.Err) 246 ConnToErr func(redis.Conn) *util.Err 247 redisRedisOption struct { 248 connFac ToRedisConnError 249 connPool *redis.Pool 250 } 251 RedisOption func(*redisRedisOption) 252 ) 253 254 func ConnFac(fac ToRedisConnError) RedisOption { 255 return func(opt *redisRedisOption) { 256 opt.connFac = fac 257 } 258 } 259 260 func ConnPool(pool *redis.Pool) RedisOption { 261 return func(opt *redisRedisOption) { 262 opt.connPool = pool 263 } 264 } 265 266 func InitRedis(opts ...RedisOption) { 267 _Redis = NewRedis(opts...) 268 } 269 270 func NewRedis(opts ...RedisOption) *Redis { 271 opt := &redisRedisOption{} 272 for _, o := range opts { 273 o(opt) 274 } 275 return &Redis{ 276 redisRedisOption: opt, 277 } 278 } 279 280 type Redis struct { 281 redisRedisOption *redisRedisOption 282 locker *redsync.Redsync 283 } 284 285 func (r *Redis) GetConn() (redis.Conn, *util.Err) { 286 conn, err := r.redisRedisOption.connFac() 287 return conn, util.WrapErr(util.EcRedisErr, err) 288 } 289 290 func (r *Redis) SpawnConn() redis.Conn { 291 return r.redisRedisOption.connPool.Get() 292 } 293 294 func (r *Redis) Lock(key string, fn func(), opts ...redsync.Option) *util.Err { 295 if fn == nil { 296 return nil 297 } 298 m := r.locker.NewMutex(key, opts...) 299 e := m.Lock() 300 if e != nil { 301 return util.WrapErr(util.EcRedisErr, e) 302 } 303 fn() 304 _, e = m.Unlock() 305 return util.WrapErr(util.EcRedisErr, e) 306 } 307 308 var ( 309 _Redis *Redis 310 ) 311 312 func GetConn() (redis.Conn, *util.Err) { 313 return _Redis.GetConn() 314 } 315 316 func SpawnConn() redis.Conn { 317 return _Redis.SpawnConn() 318 } 319 320 func FnSpawnConn(fn ConnToErr) (err *util.Err) { 321 conn := _Redis.SpawnConn() 322 err = fn(conn) 323 _ = conn.Close() 324 return 325 } 326 327 func Lock(key string, fn func(), opts ...redsync.Option) *util.Err { 328 return _Redis.Lock(key, fn, opts...) 329 } 330 331 func JsonSet(conn redis.Conn, key string, obj any, fields ...string) *util.Err { 332 str, _ := util.JsonMarshal(obj) 333 f := mergeJsonFields(fields...) 334 _, e := conn.Do(JSON_SET, key, f, str) 335 if e != nil { 336 return util.NewErr(util.EcRedisErr, util.M{ 337 "error": "empty or more than one", 338 "key": key, 339 "obj": obj, 340 "fields": fields, 341 }) 342 } 343 return nil 344 } 345 346 func JsonSetStr(conn redis.Conn, key string, str string, fields ...string) *util.Err { 347 f := mergeJsonFields(fields...) 348 _, e := conn.Do(JSON_SET, key, f, str) 349 if e != nil { 350 return util.NewErr(util.EcRedisErr, util.M{ 351 "error": e.Error(), 352 "key": key, 353 "str": str, 354 "fields": fields, 355 }) 356 } 357 return nil 358 } 359 360 func JsonGet[T any](conn redis.Conn, key string, v *T, fields ...string) *util.Err { 361 var values []T 362 err := JsonGetSlice[T](conn, key, &values, fields...) 363 if err != nil { 364 return err 365 } 366 if len(values) != 1 { 367 return util.NewErr(util.EcRedisErr, util.M{ 368 "error": "empty or more than one", 369 "key": key, 370 "fields": fields, 371 }) 372 } 373 *v = values[0] 374 return nil 375 } 376 377 func mergeJsonFields(fields ...string) string { 378 f := Json 379 if len(fields) > 0 { 380 f += "." + strings.Join(fields, ".") 381 } 382 return f 383 } 384 385 func JsonGetSlice[T any](conn redis.Conn, key string, v *[]T, fields ...string) *util.Err { 386 f := mergeJsonFields(fields...) 387 reply, e := redis.Bytes(conn.Do(JSON_GET, key, f)) 388 if e != nil { 389 return util.NewErr(util.EcRedisErr, util.M{ 390 "error": e.Error(), 391 "key": key, 392 "fields": fields, 393 }) 394 } 395 err := util.JsonUnmarshal(reply, v) 396 if err != nil { 397 return util.NewErr(util.EcRedisErr, util.M{ 398 "error": err.Error(), 399 "key": key, 400 "fields": fields, 401 }) 402 } 403 return nil 404 } 405 406 func Scan(conn redis.Conn, match string, step int, fn util.FnStrSlc) *util.Err { 407 cursor := 0 408 for { 409 slc, e := redis.Values(conn.Do(SCAN, cursor, MATCH, match, COUNT, step)) 410 if e != nil { 411 return util.WrapErr(util.EcRedisErr, e) 412 } 413 items := slc[1].([]any) 414 keys := make([]string, len(items)) 415 for i, item := range items { 416 keys[i] = string(item.([]byte)) 417 } 418 fn(keys) 419 idxStr := string(slc[0].([]byte)) 420 cursor, e = strconv.Atoi(idxStr) 421 if e != nil { 422 return util.NewErr(util.EcRedisErr, util.M{ 423 "error": e.Error(), 424 "match": match, 425 "step": step, 426 }) 427 } 428 if cursor == 0 { 429 return nil 430 } 431 } 432 }