func (s *Redis) DeleteMultiple(items []store.Item) (int, error) {
	c := s.pool.Get()
	defer c.Close()
	keys := make([]interface{}, len(items))
	for i, item := range items {
		value := reflect.ValueOf(item).Elem()
		if len(item.Key()) > 0 {
			keys[i] = fmt.Sprintf("%s:%s", s.typeName(value), item.Key())
		}
	}
	count, err := driver.Int(c.Do("DEL", keys...))
	if err != nil {
		return 0, err
	}
	if count != len(keys) {
		return count, store.ErrKeyNotFound
	}
	return count, nil
}
DeleteMultiple deletes multiple items i from the store. It returns the count
of items successfully deleted. It returns an error if any of the items do
not exist or can't be deleted. It will delete the other items, in that case.
package redis
import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"
	driver "github.com/garyburd/redigo/redis"
	"github.com/google/uuid"
	"github.com/gosuri/go-store/store"
)
const MaxItems = 1024
var DefaultRedisURLEnv = "REDIS_URL"
var DefaultRedisURL = "redis://@127.0.0.1:6379"
type item struct {
	prefix string
	key    string
	data   map[string]interface{}
}
func (i *item) Key() string {
	return i.prefix + ":" + i.key
}
type Config struct {
	Host, Port, Pass string
	Db               int
	Namespace string
}
type Redis struct {
	pool      *driver.Pool
	namespace string
}
func New(config *Config) (r *Redis, err error) {
	if config == nil {
		config, err = NewConfig(DefaultRedisURL)
		if err != nil {
			return nil, err
		}
	}
	return &Redis{pool: NewPool(config), namespace: config.Namespace}, nil
}
func NewStore(connURL, namespace string) (store.Store, error) {
	config, err := NewConfig(connURL)
	if err != nil {
		return &Redis{}, err
	}
	return &Redis{pool: NewPool(config), namespace: namespace}, nil
}
func NewConfig(connURL string) (*Config, error) {
	config := &Config{}
	if len(connURL) == 0 {
		connURL = os.Getenv(DefaultRedisURLEnv)
	}
	if len(connURL) == 0 {
		connURL = DefaultRedisURL
	}
	rURL, err := url.Parse(connURL)
	if err != nil {
		return config, err
	}
	rHost := strings.Split(rURL.Host, ":")
	config.Host = rHost[0]
	config.Port = rHost[1]
	rPass, passOk := rURL.User.Password()
	if passOk {
		config.Pass = rPass
	}
	rDb := strings.TrimPrefix(rURL.Path, "/")
	if len(rDb) > 0 {
		if config.Db, err = strconv.Atoi(rDb); err != nil {
			return config, err
		}
	}
	return config, nil
}
func NewPool(config *Config) *driver.Pool {
	return &driver.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (driver.Conn, error) {
			c, err := driver.Dial("tcp", config.Host+":"+config.Port)
			if err != nil {
				return nil, err
			}
			if len(config.Pass) > 0 {
				if _, err := c.Do("AUTH", config.Pass); err != nil {
					c.Close()
					return nil, err
				}
			}
			if config.Db > 0 {
				if _, err := c.Do("SELECT", config.Db); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, err
		},
		TestOnBorrow: func(c driver.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
}
func (s *Redis) Pool() *driver.Pool {
	if s.pool == nil {
		s.pool = NewPool(nil)
	}
	return s.pool
}
func (s *Redis) Read(i store.Item) error {
	c := s.pool.Get()
	defer c.Close()
	value := reflect.ValueOf(i).Elem()
	if len(i.Key()) == 0 {
		return store.ErrEmptyKey
	}
	ri := &item{
		key:    i.Key(),
		prefix: s.nameInNamespace(value.Type().Name()),
	}
	reply, err := driver.Values(c.Do("HGETALL", ri.Key()))
	if err != nil {
		return err
	}
	if len(reply) == 0 {
		return store.ErrKeyNotFound
	}
	if err := driver.ScanStruct(reply, i); err != nil {
		return err
	}
	return nil
}
func (s *Redis) ReadMultiple(i interface{}) error {
	v := reflect.ValueOf(i)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if v.Kind() != reflect.Slice {
		return errors.New("store: value must be a a slice")
	}
	c := s.pool.Get()
	defer c.Close()
	var key string
	var err error
	prefix := s.typeName(v) + ":"
	c.Send("MULTI")
	for y := 0; y < v.Len(); y++ {
		if key = v.Index(y).Addr().MethodByName("Key").Call(nil)[0].String(); len(key) == 0 {
			return store.ErrEmptyKey
		}
		if err = c.Send("HGETALL", prefix+key); err != nil {
			return err
		}
	}
	if err = c.Flush(); err != nil {
		return err
	}
	reply, err := c.Do("EXEC")
	if err != nil {
		return err
	}
	replyValue := reflect.ValueOf(reply)
	var values []interface{}
	for y := 0; y < replyValue.Len(); y++ {
		itemPtrV := reflect.New(v.Type().Elem())
		if values, err = driver.Values(replyValue.Index(y).Interface(), nil); err != nil {
			return err
		}
		driver.ScanStruct(values, itemPtrV.Interface())
		v.Index(y).Set(itemPtrV.Elem())
	}
	return nil
}
func (s *Redis) WriteMultiple(i []store.Item) error {
	return errors.New("Implementation pending")
}
func (s *Redis) Write(i store.Item) error {
	c := s.pool.Get()
	defer c.Close()
	value := reflect.ValueOf(i).Elem()
	ri := &item{
		prefix: s.typeName(value),
		data:   make(map[string]interface{}),
	}
	ri.key = i.Key()
	if len(ri.key) == 0 {
		ri.key = uuid.New().String()
	}
	i.SetKey(ri.key)
	if err := marshall(value, ri); err != nil {
		return err
	}
	for key, val := range ri.data {
		if err := c.Send("HSET", ri.Key(), key, val); err != nil {
			return err
		}
		c.Flush()
	}
	return nil
}