valkey-io / valkey-go

A fast Golang Valkey client that supports Client Side Caching and Auto Pipelining.
Apache License 2.0
158 stars 10 forks source link

message.go AsStrMap for loop dividing values in half #8

Closed gotama closed 4 months ago

gotama commented 4 months ago

I have this function to Do commands and im trying to get an array of values from it using this code :

func myDo(cmd valkey.Completed) map[string]string {
    resp := manager.Client.Do(manager.Context, cmd)
    msg, err := resp.ToMessage()
    if err != nil {
        panic("No, not yet...")
    }
    keys, err := msg.AsStrMap()
    if err != nil {
        panic("No, not yet...")
    }
    return keys
}

When calling AsStrMap it halves the size of the return so im only getting one value instead of two.

func (m *ValkeyMessage) AsStrMap() (map[string]string, error) {
    if err := m.Error(); err != nil {
        return nil, err
    }
    if (m.IsMap() || m.IsArray()) && len(m.values)%2 == 0 {
        r := make(map[string]string, len(m.values)/2)
        for i := 0; i < len(m.values); i += 2 {
            k := m.values[i]
            v := m.values[i+1]
            r[k.string] = v.string
        }
        return r, nil
    }
    typ := m.typ
    return nil, fmt.Errorf("%w: valkey message type %s is not a map/array/set or its length is not even", errParse, typeNames[typ])
}

From the manager.Client.B().Keys().Pattern("*").Build() command this data was used.

valkey-go.ValkeyMessage { attrs: github.com/valkey-io/valkey-go.ValkeyMessage nil, string: "", values: []github.com/valkey-io/valkey-go.ValkeyMessage len: 2, cap: 2, [ ("github.com/valkey-io/valkey-go.ValkeyMessage")(), (*"github.com/valkey-io/valkey-go.ValkeyMessage")() ], integer: 0, typ: 42, ttl: [7]uint8 [0,0,0,0,0,0,0] }

However I am only getting half the array so only on value instead of two. Is this intended behavior?

This code gives me the values i expect:

func (m *ValkeyMessage) AsStrMap() (map[string]string, error) {
    if err := m.Error(); err != nil {
        return nil, err
    }
    if m.IsMap() || m.IsArray() {
        r := make(map[string]string, len(m.values))
        for i := 0; i < len(m.values); i += 1 {
            k := m.values[i]
            r[k.string] = k.string
        }
        return r, nil
    }
    typ := m.typ
    return nil, fmt.Errorf("%w: valkey message type %s is not a map/array/set or its length is not even", errParse, typeNames[typ])
}

Maybe I should not be using msg, err := resp.ToMessage() because the way the key value map[string]string gets created in the for loop im setting key and value to be the same which is probably not the best use of a key value map.

Maybe any experts could comment on my code and also offer some guidance on how to get the best use case out of AsStrMap

rueian commented 4 months ago

Hi @gotama,

Dividing values in half is an expected behavior of AsStrMap because it assumes the values should be a RESP map.

For your manager.Client.B().Keys().Pattern("*").Build() use case, you should use .AsStrSlice() instead.

gotama commented 4 months ago

Hi @rueian

Thank you so much for the advice, sorry for wasting your time I should of spent more time looking into message.go

your help has moved me forward.