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
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.