package redis import ( "fmt" "strings" "time" ) // PrefixedRedisClient struct type PrefixedRedisClient struct { prefix string client Client } // Prefix returns the prefix attached to the client func (p *PrefixedRedisClient) Prefix() string { return p.prefix } // ClusterMode returns true if the client is working in cluster mode func (p *PrefixedRedisClient) ClusterMode() bool { return p.client.ClusterMode() } // ClusterSlotForKey returns the slot for the supplied key func (p *PrefixedRedisClient) ClusterSlotForKey(key string) (int64, error) { return p.client.ClusterSlotForKey(withPrefix(p.prefix, key)).Result() } // ClusterCountKeysInSlot returns the number of keys in slot func (p *PrefixedRedisClient) ClusterCountKeysInSlot(slot int) (int64, error) { return p.client.ClusterCountKeysInSlot(slot).Result() } // ClusterKeysInSlot returns all the keys in the supplied slot func (p *PrefixedRedisClient) ClusterKeysInSlot(slot int, count int) ([]string, error) { keys, err := p.client.ClusterKeysInSlot(slot, count).Multi() if err != nil { return nil, err } woPrefix := make([]string, len(keys)) for index, key := range keys { woPrefix[index] = withoutPrefix(p.prefix, key) } return woPrefix, nil } // Get wraps around redis get method by adding prefix and returning string and error directly func (p *PrefixedRedisClient) Get(key string) (string, error) { return p.client.Get(withPrefix(p.prefix, key)).ResultString() } // Set wraps around redis get method by adding prefix and returning error directly func (p *PrefixedRedisClient) Set(key string, value interface{}, expiration time.Duration) error { return p.client.Set(withPrefix(p.prefix, key), value, expiration).Err() } // Keys wraps around redis keys method by adding prefix and returning []string and error directly func (p *PrefixedRedisClient) Keys(pattern string) ([]string, error) { keys, err := p.client.Keys(withPrefix(p.prefix, pattern)).Multi() if err != nil { return nil, err } woPrefix := make([]string, len(keys)) for index, key := range keys { woPrefix[index] = withoutPrefix(p.prefix, key) } return woPrefix, nil } // Del wraps around redis del method by adding prefix and returning int64 and error directly func (p *PrefixedRedisClient) Del(keys ...string) (int64, error) { prefixedKeys := make([]string, len(keys)) for i, k := range keys { prefixedKeys[i] = withPrefix(p.prefix, k) } return p.client.Del(prefixedKeys...).Result() } // SMembers returns a slice with all the members of a set func (p *PrefixedRedisClient) SMembers(key string) ([]string, error) { return p.client.SMembers(withPrefix(p.prefix, key)).Multi() } // SIsMember returns true if members is in the set func (p *PrefixedRedisClient) SIsMember(key string, member interface{}) bool { return p.client.SIsMember(withPrefix(p.prefix, key), member).Bool() } // SAdd adds new members to a set func (p *PrefixedRedisClient) SAdd(key string, members ...interface{}) (int64, error) { return p.client.SAdd(withPrefix(p.prefix, key), members...).Result() } // SRem removes members from a set func (p *PrefixedRedisClient) SRem(key string, members ...interface{}) (int64, error) { return p.client.SRem(withPrefix(p.prefix, key), members...).Result() } // Exists returns true if a key exists in redis func (p *PrefixedRedisClient) Exists(keys ...string) (int64, error) { prefixedKeys := make([]string, len(keys)) for i, k := range keys { prefixedKeys[i] = withPrefix(p.prefix, k) } val, err := p.client.Exists(prefixedKeys...).Result() return val, err } // Incr increments a key. Sets it in one if it doesn't exist func (p *PrefixedRedisClient) Incr(key string) (int64, error) { return p.client.Incr(withPrefix(p.prefix, key)).Result() } // Decr increments a key. Sets it in one if it doesn't exist func (p *PrefixedRedisClient) Decr(key string) (int64, error) { return p.client.Decr(withPrefix(p.prefix, key)).Result() } // RPush insert all the specified values at the tail of the list stored at key func (p *PrefixedRedisClient) RPush(key string, values ...interface{}) (int64, error) { return p.client.RPush(withPrefix(p.prefix, key), values...).Result() } // LRange Returns the specified elements of the list stored at key func (p *PrefixedRedisClient) LRange(key string, start, stop int64) ([]string, error) { return p.client.LRange(withPrefix(p.prefix, key), start, stop).Multi() } // LTrim Trim an existing list so that it will contain only the specified range of elements specified func (p *PrefixedRedisClient) LTrim(key string, start, stop int64) error { return p.client.LTrim(withPrefix(p.prefix, key), start, stop).Err() } // LLen Returns the length of the list stored at key func (p *PrefixedRedisClient) LLen(key string) (int64, error) { return p.client.LLen(withPrefix(p.prefix, key)).Result() } // Expire set expiration time for particular key func (p *PrefixedRedisClient) Expire(key string, value time.Duration) bool { return p.client.Expire(withPrefix(p.prefix, key), value).Bool() } // TTL for particular key func (p *PrefixedRedisClient) TTL(key string) time.Duration { return p.client.TTL(withPrefix(p.prefix, key)).Duration() } // MGet fetchs multiple results func (p *PrefixedRedisClient) MGet(keys []string) ([]interface{}, error) { keysWithPrefix := make([]string, 0) for _, key := range keys { keysWithPrefix = append(keysWithPrefix, withPrefix(p.prefix, key)) } return p.client.MGet(keysWithPrefix).MultiInterface() } // SCard implements SCard wrapper for redis func (p *PrefixedRedisClient) SCard(key string) (int64, error) { return p.client.SCard(withPrefix(p.prefix, key)).Result() } // Eval implements Eval wrapper for redis func (p *PrefixedRedisClient) Eval(script string, keys []string, args ...interface{}) error { return p.client.Eval(script, keys, args...).Err() } // HIncrBy implements HIncrBy wrapper for redis func (p *PrefixedRedisClient) HIncrBy(key string, field string, value int64) (int64, error) { return p.client.HIncrBy(withPrefix(p.prefix, key), field, value).Result() } // HSet implements HGetAll wrapper for redis func (p *PrefixedRedisClient) HSet(key string, hashKey string, value interface{}) error { return p.client.HSet(withPrefix(p.prefix, key), hashKey, value).Err() } // HGetAll implements HGetAll wrapper for redis func (p *PrefixedRedisClient) HGetAll(key string) (map[string]string, error) { return p.client.HGetAll(withPrefix(p.prefix, key)).MapStringString() } // Type implements Type wrapper for redis with prefix func (p *PrefixedRedisClient) Type(key string) (string, error) { return p.client.Type(withPrefix(p.prefix, key)).ResultString() } func (p *PrefixedRedisClient) Scan(cursor uint64, match string, count int64) ([]string, uint64, error) { toReturn := make([]string, 0) res := p.client.Scan(cursor, withPrefix(p.prefix, match), count) if res.Err() != nil { return nil, 0, res.Err() } cursor = uint64(res.Int()) keys, err := res.Multi() if err != nil { return nil, 0, err } for _, key := range keys { toReturn = append(toReturn, withoutPrefix(p.prefix, key)) } return toReturn, cursor, nil } // Pipeline wrapper func (p *PrefixedRedisClient) Pipeline() Pipeline { return &PrefixedPipeline{wrapped: p.client.Pipeline(), prefix: p.prefix} } // NewPrefixedRedisClient returns a new Prefixed Redis Client func NewPrefixedRedisClient(redisClient Client, prefix string) (*PrefixedRedisClient, error) { return &PrefixedRedisClient{ client: redisClient, prefix: prefix, }, nil } // PrefixedPipeline adds a prefix to all pipelined operations involving keys type PrefixedPipeline struct { prefix string wrapped Pipeline } // LRange schedules an lrange operation on this pipeline func (p *PrefixedPipeline) LRange(key string, start, stop int64) { p.wrapped.LRange(withPrefix(p.prefix, key), start, stop) } // LTrim schedules an ltrim operation on this pipeline func (p *PrefixedPipeline) LTrim(key string, start, stop int64) { p.wrapped.LTrim(withPrefix(p.prefix, key), start, stop) } // LLen schedules an llen operation on this pipeline func (p *PrefixedPipeline) LLen(key string) { p.wrapped.LLen(withPrefix(p.prefix, key)) } // HIncrBy schedules an hincrby operation on this pipeline func (p *PrefixedPipeline) HIncrBy(key string, field string, value int64) { p.wrapped.HIncrBy(withPrefix(p.prefix, key), field, value) } // HLen schedules an HLen operation on this pipeline func (p *PrefixedPipeline) HLen(key string) { p.wrapped.HLen(withPrefix(p.prefix, key)) } // Set schedules a Set operation on this pipeline func (p *PrefixedPipeline) Set(key string, value interface{}, expiration time.Duration) { p.wrapped.Set(withPrefix(p.prefix, key), value, expiration) } // Incr schedules an Incr operation on this pipeline func (p *PrefixedPipeline) Incr(key string) { p.wrapped.Incr(withPrefix(p.prefix, key)) } // Decr schedules a Decr operation on this pipeline func (p *PrefixedPipeline) Decr(key string) { p.wrapped.Decr(withPrefix(p.prefix, key)) } // SAdd schedules a SAdd operation on this pipeline func (p *PrefixedPipeline) SAdd(key string, members ...interface{}) { p.wrapped.SAdd(withPrefix(p.prefix, key), members...) } // SRem schedules a SRem operation on this pipeline func (p *PrefixedPipeline) SRem(key string, members ...interface{}) { p.wrapped.SRem(withPrefix(p.prefix, key), members...) } // SMembers schedules a SMembers operation on this pipeline func (p *PrefixedPipeline) SMembers(key string) { p.wrapped.SMembers(withPrefix(p.prefix, key)) } // Del schedules a Del operation on this pipeline func (p *PrefixedPipeline) Del(keys ...string) { prefixedKeys := make([]string, len(keys)) for i, k := range keys { prefixedKeys[i] = withPrefix(p.prefix, k) } p.wrapped.Del(prefixedKeys...) } // Exec executes the pipeline func (p *PrefixedPipeline) Exec() ([]Result, error) { return p.wrapped.Exec() } // withPrefix adds a prefix to the key if the prefix supplied has a length greater than 0 func withPrefix(prefix string, key string) string { if len(prefix) > 0 { return fmt.Sprintf("%s.%s", prefix, key) } return key } // withoutPrefix removes the prefix from a key if the prefix has a length greater than 0 func withoutPrefix(prefix string, key string) string { if len(prefix) > 0 { return strings.Replace(key, fmt.Sprintf("%s.", prefix), "", 1) } return key }