3

I have a map like this, which I want to save/retrive from redis using redigo:

animals := map[string]bool{
    "cat": true,
    "dog":   false,
    "fox": true,
}

The length of the map may vary.

I tried these function:

func SetHash(key string, value map[string]bool) error {
    conn := Pool.Get()
    defer conn.Close()
    _, err := conn.Do("HMSET", key, value)
    if err != nil {
        return fmt.Errorf("error setting key %s to %s: %v", key, value, err)
    }
    return err
}


func GetHash(key string) (map[string]bool, error) {
    conn := Pool.Get()
    defer conn.Close()
    val, err := conn.Do("HGETALL", key)
    if err != nil {
        fmt.Errorf("error setting key %s to %s: %v", key, nil, err)
        return nil,  err
    }
    return val, err
}

But can not make GetHash correctly. I've checked the docs examples and it was not helpful. So appreciate your help to have a working example.

Smn
  • 145
  • 1
  • 3
  • 9

2 Answers2

6

HMSET is deprecated, use HSET instead, no effect here though.

The map[string]bool may be flattened with AddFlat() for SetHash().

c.Do("HSET", redis.Args{}.Add("key").AddFlat(value)...)

For GetHash(), use Values(). You may use ScanStruct() to map to a struct or loop through the values to create a map dynamically.

v, err := redis.Values(c.Do("HGETALL", key))
redis.ScanStruct(v, &myStruct);

See example from redigo tests in scan_test.go.

LeoMurillo
  • 6,048
  • 1
  • 19
  • 34
2

The application is responsible for converting structured types to and from the types understood by Redis.

Flatten the map into a list of arguments:

func SetHash(key string, value map[string]bool) error {
    conn := Pool.Get()
    defer conn.Close()

    // Create arguments: key field value [field value]...
    var args = []interface{}{key}
    for k, v := range value {
        args = append(args, k, v)
    }

    _, err := conn.Do("HMSET", args...)
    if err != nil {
        return fmt.Errorf("error setting key %s to %v: %v", key, value, err)
    }
    return err
}

Convert the returned field value pairs to a map:

func GetHash(key string) (map[string]bool, error) {
    conn := Pool.Get()
    defer conn.Close()
    values, err := redis.Strings(conn.Do("HGETALL", key))
    if err != nil {
        return nil, err
    }

        // Loop through [field value]... and parse value as bool.
    m := map[string]bool{}
    for i := 0; i < len(values); i += 2 {
        b, err := strconv.ParseBool(value)
        if err != nil {
            return nil, errors.New("value not a bool")
        }
        m[key] = b
    }
    return m, nil
}
Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
  • Thanks for the tips. Will you please tell me how can I set a timeout for the key in to`SetHash`? – Smn Jan 30 '20 at 19:24
  • 2
    Use `conn.Do("EXPIRE", key, secs)`. See https://redis.io/commands/expire. You are better-off [pipelining](https://godoc.org/github.com/gomodule/redigo/redis#hdr-Pipelining) it: – LeoMurillo Jan 30 '20 at 19:30