Search code examples
gogoogle-appsgoogle-cloud-datastore

How to ignore zero value in struct in Google Datastore for Golang?


I am trying to use Google Datastore to store data by Go. Since the EndDate is optional field, and don't want to store zero value in that field. If I make a pointer for time field, Google Datastore will send an error message - datastore: unsupported struct field type: *time.Time

How can I ignore zero value field in struct?

type Event struct {
    StartDate time.Time `datastore:"start_date,noindex" json:"startDate"`
    EndDate   time.Time `datastore:"end_date,noindex" json:"endDate"`
}

Solution

  • The default saving mechanism does not handle optional fields. A field is either saved all the time, or never. There is no such thing as "only save if it's value does not equal to something".

    The "optionally saved property" is considered a custom behavior, a custom saving mechanism, and as such, it has to be implemented manually. Go's way to do this is to implement the PropertyLoadSaver interface on your struct. Here I present 2 different methods to achieve that:

    Manually saving fields

    Here is an example how to do it by manually saving the fields (and excluding EndDate if it is the zero value):

    type Event struct {
        StartDate time.Time `datastore:"start_date,noindex" json:"startDate"`
        EndDate   time.Time `datastore:"end_date,noindex" json:"endDate"`
    }
    
    func (e *Event) Save(c chan<- datastore.Property) error {
        defer close(c)
        // Always save StartDate:
        c <- datastore.Property{Name:"start_date", Value:e.StartDate, NoIndex: true}
    
        // Only save EndDate if not zero value:
        if !e.EndDate.IsZero() {
            c <- datastore.Property{Name:"end_date", Value:e.EndDate, NoIndex: true}
        }
        return nil
    }
    
    func (e *Event) Load(c chan<- datastore.Property) error {
        // No change required in loading, call default implementation:
        return datastore.LoadStruct(e, c)
    }
    

    With another struct

    Here's another way using another struct. The Load() implementation is always the same, only Save() differs:

    func (e *Event) Save(c chan<- datastore.Property) error {
        if !e.EndDate.IsZero() {
            // If EndDate is not zero, save as usual:
            return datastore.SaveStruct(e, c)
        }
    
        // Else we need a struct without the EndDate field:
        s := struct{ StartDate time.Time `datastore:"start_date,noindex"` }{e.StartDate}
        // Which now can be saved using the default saving mechanism:
        return datastore.SaveStruct(&s, c)
    }