func (r RedisPipeline) Exists(key string) *redis.BoolCmd {
	return r.pipeline.Exists(key)
}
Exists implements RedisClient Exists for pipeline
package gokvstores
import (
	"fmt"
	"net"
	"time"
	redis "gopkg.in/redis.v5"
)
type RedisClient interface {
	Ping() *redis.StatusCmd
	Exists(key string) *redis.BoolCmd
	Del(keys ...string) *redis.IntCmd
	FlushDb() *redis.StatusCmd
	Close() error
	Process(cmd redis.Cmder) error
	Get(key string) *redis.StringCmd
	Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd
	MGet(keys ...string) *redis.SliceCmd
	HDel(key string, fields ...string) *redis.IntCmd
	HGetAll(key string) *redis.StringStringMapCmd
	HMSet(key string, fields map[string]string) *redis.StatusCmd
	SMembers(key string) *redis.StringSliceCmd
	SAdd(key string, members ...interface{}) *redis.IntCmd
	Keys(pattern string) *redis.StringSliceCmd
	Pipeline() *redis.Pipeline
}
type RedisPipeline struct {
	pipeline *redis.Pipeline
}
type RedisClientOptions struct {
	Network            string
	Addr               string
	Dialer             func() (net.Conn, error)
	Password           string
	DB                 int
	MaxRetries         int
	DialTimeout        time.Duration
	ReadTimeout        time.Duration
	WriteTimeout       time.Duration
	PoolSize           int
	PoolTimeout        time.Duration
	IdleTimeout        time.Duration
	IdleCheckFrequency time.Duration
	ReadOnly           bool
}
type RedisClusterOptions struct {
	Addrs              []string
	MaxRedirects       int
	ReadOnly           bool
	RouteByLatency     bool
	Password           string
	DialTimeout        time.Duration
	ReadTimeout        time.Duration
	WriteTimeout       time.Duration
	PoolSize           int
	PoolTimeout        time.Duration
	IdleTimeout        time.Duration
	IdleCheckFrequency time.Duration
}
type RedisStore struct {
	client     RedisClient
	expiration time.Duration
}
func (r *RedisStore) Get(key string) (interface{}, error) {
	cmd := redis.NewCmd("get", key)
	if err := r.client.Process(cmd); err != nil {
		if err == redis.Nil {
			return nil, nil
		}
		return nil, err
	}
	return cmd.Val(), cmd.Err()
}
func (r *RedisStore) MGet(keys []string) (map[string]interface{}, error) {
	values, err := r.client.MGet(keys...).Result()
	newValues := make(map[string]interface{}, len(keys))
	for k, v := range keys {
		value := values[k]
		if err != nil {
			return nil, err
		}
		newValues[v] = value
	}
	return newValues, nil
}
func (r *RedisStore) Set(key string, value interface{}) error {
	return r.client.Set(key, value, r.expiration).Err()
}
func (r *RedisStore) SetWithExpiration(key string, value interface{}, expiration time.Duration) error {
	return r.client.Set(key, value, expiration).Err()
}
func (r *RedisStore) GetMap(key string) (map[string]interface{}, error) {
	values, err := r.client.HGetAll(key).Result()
	if err != nil {
		return nil, err
	}
	if len(values) == 0 {
		return nil, nil
	}
	newValues := make(map[string]interface{}, len(values))
	for k, v := range values {
		newValues[k] = v
	}
	return newValues, nil
}
func (r *RedisStore) SetMap(key string, values map[string]interface{}) error {
	newValues := make(map[string]string, len(values))
	for k, v := range values {
		switch vv := v.(type) {
		case string:
			newValues[k] = vv
		default:
			newValues[k] = fmt.Sprintf("%v", vv)
		}
	}
	return r.client.HMSet(key, newValues).Err()
}
func (r *RedisStore) DeleteMap(key string, fields ...string) error {
	return r.client.HDel(key, fields...).Err()
}
func (r *RedisStore) GetSlice(key string) ([]interface{}, error) {
	values, err := r.client.SMembers(key).Result()
	if err != nil {
		return nil, err
	}
	if len(values) == 0 {
		return nil, nil
	}
	newValues := make([]interface{}, len(values))
	for i := range values {
		newValues[i] = values[i]
	}
	return newValues, nil
}
func (r *RedisStore) SetSlice(key string, values []interface{}) error {
	for _, v := range values {
		if v != nil {
			if err := r.client.SAdd(key, v).Err(); err != nil {
				return err
			}
		}
	}
	return nil
}
func (r *RedisStore) AppendSlice(key string, values ...interface{}) error {
	return r.SetSlice(key, values)
}
func (r *RedisStore) Exists(key string) (bool, error) {
	cmd := r.client.Exists(key)
	return cmd.Val(), cmd.Err()
}
func (r *RedisStore) Delete(key string) error {
	return r.client.Del(key).Err()
}
func (r *RedisStore) Keys(pattern string) ([]interface{}, error) {
	values, err := r.client.Keys(pattern).Result()
	if len(values) == 0 {
		return nil, err
	}
	newValues := make([]interface{}, len(values))
	for k, v := range values {
		newValues[k] = v
	}
	return newValues, err
}
func (r *RedisStore) Flush() error {
	return r.client.FlushDb().Err()
}
func (r *RedisStore) Close() error {
	return r.client.Close()
}
func NewRedisClientStore(options *RedisClientOptions, expiration time.Duration) (KVStore, error) {
	opts := &redis.Options{
		Network:            options.Network,
		Addr:               options.Addr,
		Dialer:             options.Dialer,
		Password:           options.Password,
		DB:                 options.DB,
		MaxRetries:         options.MaxRetries,
		DialTimeout:        options.DialTimeout,
		ReadTimeout:        options.ReadTimeout,
		WriteTimeout:       options.WriteTimeout,
		PoolSize:           options.PoolSize,
		PoolTimeout:        options.PoolTimeout,
		IdleTimeout:        options.IdleTimeout,
		IdleCheckFrequency: options.IdleCheckFrequency,
		ReadOnly:           options.ReadOnly,
	}
	client := redis.NewClient(opts)
	if err := client.Ping().Err(); err != nil {
		return nil, err
	}
	return &RedisStore{
		client:     client,
		expiration: expiration,
	}, nil
}
func NewRedisClusterStore(options *RedisClusterOptions, expiration time.Duration) (KVStore, error) {
	opts := &redis.ClusterOptions{
		Addrs:              options.Addrs,
		MaxRedirects:       options.MaxRedirects,
		ReadOnly:           options.ReadOnly,
		RouteByLatency:     options.RouteByLatency,
		Password:           options.Password,
		DialTimeout:        options.DialTimeout,
		ReadTimeout:        options.ReadTimeout,
		WriteTimeout:       options.WriteTimeout,
		PoolSize:           options.PoolSize,
		PoolTimeout:        options.PoolTimeout,
		IdleTimeout:        options.IdleTimeout,
		IdleCheckFrequency: options.IdleCheckFrequency,
	}
	client := redis.NewClusterClient(opts)
	if err := client.Ping().Err(); err != nil {
		return nil, err
	}
	return &RedisStore{
		client:     client,
		expiration: expiration,
	}, nil
}
func (r *RedisStore) Pipeline(f func(r *RedisStore) error) ([]redis.Cmder, error) {
	pipe := r.client.Pipeline()
	redisPipeline := RedisPipeline{
		pipeline: pipe,
	}
	store := &RedisStore{
		client:     redisPipeline,
		expiration: r.expiration,
	}
	err := f(store)
	if err != nil {
		return nil, err
	}
	cmds, err := pipe.Exec()
	return cmds, err
}
func (r *RedisStore) GetMaps(keys []string) (map[string]map[string]interface{}, error) {
	commands, err := r.Pipeline(func(r *RedisStore) error {
		for _, key := range keys {
			r.client.HGetAll(key)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	newValues := make(map[string]map[string]interface{}, len(keys))
	for i, key := range keys {
		cmd := commands[i]
		values, _ := cmd.(*redis.StringStringMapCmd).Result()
		if values != nil {
			valueMap := make(map[string]interface{}, len(values))
			for k, v := range values {
				valueMap[k] = v
			}
			newValues[key] = valueMap
		} else {
			newValues[key] = nil
		}
	}
	return newValues, nil
}
func (r *RedisStore) SetMaps(maps map[string]map[string]interface{}) error {
	_, err := r.Pipeline(func(r *RedisStore) error {
		for k, v := range maps {
			r.SetMap(k, v)
		}
		return nil
	})
	return err
}
func (r RedisPipeline) Pipeline() *redis.Pipeline {
	return r.pipeline
}
func (r RedisPipeline) Ping() *redis.StatusCmd {
	return r.pipeline.Ping()
}