Syed Qasim Rizvi
Syed Qasim Rizvi

Reputation: 53

Accessing MongoDB from Go

I am accessing MongoDB using Go as follows:

var configRes *clientConfigData
err := clientDB.
    C(clientConfigCollection).
    Find(bson.M{}).
    One(&configRes)
if err != nil {
    return nil, errors.Wrap(err, "finding config collection")
}

Where

type clientConfigData struct {
    SMTPAssoc      int       `bson:"smtp_assoc"`
    PlanType       string    `bson:"plan_type"`
    EndDate        string    `bson:"end_date"`
}

Now since EndDate in MongoDB is stored as string so I declared EndDate as string. But I need to access this date as Go Time in clientConfigData.

Upvotes: 1

Views: 825

Answers (1)

icza
icza

Reputation: 417472

If you want to change a value or do a type conversion when marshaling / unmarshaling your values from / to MongoDB, you may do it by implementing a custom marshaling / unmarshaling logic.

You can do this by implementing the bson.Getter and bson.Setter interfaces. Inside these methods, you may do whatever you want to with the values being marshaled / unmarshaled.

Easiest is to extend your clientConfigData type with an additional field, one that will be of type time.Time, the value you need:

type clientConfigData struct {
    SMTPAssoc  int       `bson:"smtp_assoc"`
    PlanType   string    `bson:"plan_type"`
    EndDateStr string    `bson:"end_date"`
    EndDate    time.Time `bson:"-"`
}

It has tag value bson:"-", because we don't want this to appear in MongoDB.

And now the custom marshaling / unmarhsaling logic:

const endDateLayout = "2006-01-02 15:04:05" // Use your layout here

func (c *clientConfigData) SetBSON(raw bson.Raw) (err error) {
    type my clientConfigData
    if err = raw.Unmarshal((*my)(c)); err != nil {
        return
    }
    c.EndDate, err = time.Parse(endDateLayout, c.EndDateStr)
    return
}

func (c *clientConfigData) GetBSON() (interface{}, error) {
    c.EndDateStr = c.EndDate.Format(endDateLayout)
    type my *clientConfigData
    return my(c), nil
}

What happens here is that SetBSON() is responsible to "populate" your struct value with the raw value coming from MongoDB, and GetBSON() is responsible to provide a value you want to be saved (marshaled).

When loading: SetBSON() first unmarshals the value as-is, then properly sets the EndDate field (which is of type time.Time) from the string date value that came from the DB (EndDateStr).

When saving: GetBSON() first fills the EndDateStr field (the one that is saved) from the EndDate field, and then simply returns, signaling that it is ok to save.

One thing to note: both SetBSON() and GetBSON() create a new my type inside them. The reason for this is to avoid stack overflow. Simply returning a value of type clientConfigData is bad, because we implemented bson.Getter and bson.Setter, so SetBSON() and GetBSON() would get called endlessly. The new my type does not have these methods, so endless "recursion" does not happen (the type keyword creates a new type, and it does not "inherit" methods of the underlying type).

Also see related / similar question: Set default date when inserting document with time.Time field

Upvotes: 2

Related Questions