6

I would like to get ObjectID as a string because I have other storage types, so I want to avoid to use primitive.ObjectID in my struct to keep the layers independent. I'm new to Golang, thanks.

package listing

type Card struct {
    ID       string
    Hanzi    string
    Pinyin   string
    Traducao string
}

My storage file: package storage

func (m *Mongodb)GetCards() []*listing.Card {
    var list []*listing.Card

    collection := m.Client.Database("flashcards").Collection("cards")
    cur, err := collection.Find(context.TODO(), bson.M{})

    if err != nil {
        log.Fatal("Erro buscando cards:", err)
    }
    for cur.Next(context.TODO()) {
        var card listing.Card
        err = cur.Decode(&card)
        if err != nil {
            log.Fatal("Erro decodificando documento", err)
        }
        list = append(list, &card)
    }
    return list
}
Bruno Canongia
  • 514
  • 6
  • 12

4 Answers4

5

Like you, I was looking for a solution to keep modules as independent as possible and keep coupling levels low. I scratched my head A LOT but in the end, I found this. I hope it's a valid and clear, solution.

You can use a custom type that implements the ValueMarshaler interface: so you have to implement MarshalValue func!

type Card struct {
    ID       MyObjectID `bson:"_id"`
    CardSuit string     `bson:"cardSuit"`
}

type MyObjectID string

func (id MyObjectID) MarshalBSONValue() (bsontype.Type, []byte, error) {
    p, err := primitive.ObjectIDFromHex(string(id))
    if err != nil {
        return bsontype.Null, nil, err
    }

    return bson.MarshalValue(p)
}

ID is practically a string, but with the custom marshaller, it will be saved as an ObjectID in Mongo, and parsed correctly during unmarshalling.

Here is a working example, obviously, you need mongo to make it run.

Related question that helped me.

Yuriy Kirel
  • 105
  • 7
Cirelli94
  • 1,714
  • 1
  • 15
  • 24
1

Ok, I figured out. I created another struct just to hold the ObjectId

type HexId struct {
    ID primitive.ObjectID `bson:"_id"`
}

then I used Hex() to pass the value to Card.ID

err = cur.Decode(&card)
err = cur.Decode(&hexId)
card.ID = hexId.ID.Hex()

Now I can use it to make links.

Bruno Canongia
  • 514
  • 6
  • 12
0

For me it works out of the box when having the bson annotation (version 5.0.6):

type User struct {
    Id   string `bson:"_id,omitempty" json:"id,omitempty"`
    Name string `bson:"name" json:"name"`
}
zim
  • 9
  • 1
-2

Use bson:"-" for ignore ObjectID in response and json:"-" for ignoring in database. And you don't need to decode two times.

type Card struct {
    ObjectID primitive.ObjectID `json:"-" bson:"_id"`
    ID       string             `bson:"-"`
    Hanzi    string
    Pinyin   string
    Traducao string
}

err = cur.Decode(&card)
card.ID = card.ObjectID.Hex()
Eklavya
  • 17,618
  • 4
  • 28
  • 57
  • 2
    It works but as I said, I want to keep my layers independent. I don't want database specific types on this layer. However, this solution avoids decode the cursor twice. Thanks Eklavya – Bruno Canongia May 13 '20 at 00:11