-3

Please describe a way how to serialize select results in go-tarantool connector to struct to have an ability to get access to fields as tuple.key1.key2

Oleg Babin
  • 409
  • 3
  • 9
  • 1
    It's right there in [the readme](https://github.com/tarantool/go-tarantool#custom-unpacking-and-typed-selects-and-function-calls). – Marc Sep 11 '20 at 11:04

1 Answers1

0

We always use conn.*Typed() queries in our projects to do it.

First you need to define your structure that represents tuple in Tarantool. Then you need implement two interfaces for it, msgpack.CustomDecoder and msgpack.CustomEncoder .

You should be able to do something like this:

type Session struct {
    ID         string
    UserID     int64 
}

func (s *Session) EncodeMsgpack(e *msgpack.Encoder) error {
    if err := e.EncodeArrayLen(2); err != nil {
        return err
    }
  
    if err := e.EncodeString(s.ID); err != nil {
        return err
    }
  
    if err := e.EncodeInt64(s.UserID); err != nil {
        return err
    }

    return nil
}

func (s *Session) DecodeMsgpack(d *msgpack.Decoder) error {
    l, err := d.DecodeArrayLen()
    if err != nil {
        return err
    }
  
    decodedFields := 1
    if s.ID, err = d.DecodeString(); err != nil || decodedFields == l {
        return err
    }

    decodedFields++
    if s.UserID, err = d.DecodeInt64(); err != nil || decodedFields == l {
        return err
    }
 
    for i := 0; i < l-decodedFields; i++ {
        _ = d.Skip()
    }
  
    return nil
}

Pay attention to the decoder. It contains counting fields. This is necessary for non-breaking migrations.

For example, if the msgpack array has fewer fields than we are trying to decode, nothing will break.

The response from the select query is a sequential array of msgpack tuples, so if we do not skip unknown fields, the decoding of the next instance of the structure will not start from the beginning of a next tuple.

Then you can try to do query:

func() ([]Session, error) {
    const userID = 822
    
    var sessions []Session
    err := conn.SelectTyped("session", "user", 0, 10, tarantool.IterEq, []interface{}{userID}, &resp)
    if err != nil {
        return nil, err
    }

    if len(resp) == 0 {
        return nil, nil
    }
  
    return sessions, nil
}

This is the best way in my opinion as there is a minimum of reflections, type conversions and type assertions that can cause panic in production with careless use. Also this is a more performance way.