4

How to update the document with non zero values only. As example I didn't received any value for status and Struct has only two values to be updated. So it should only update those 2 values and skip zero/null values. But as given below it's updating it to zero/null/""

type Product struct {
    ID          primitive.ObjectID `json:"id" bson:"_id"`
    Status      int                `json:"status" bson:"status"`
    DisplayName string             `json:"displayName" bson:"display_name"`
    Text        string             `json:"text" bson:"text"`
}

I have tried the following up it's overriding the status value to 0 if no value is passed for it.

    opts := options.Update().SetUpsert(false)

    filter := bson.D{primitive.E{Key: "_id", Value: product.ID}}
    update := bson.D{{"$set", bson.D{{"status", product.Status}, bson.D{{"text",product.Text}, {"display_name", product.DisplayName}}}}
    _, err := db.Collection("product").UpdateOne(context.TODO(), filter, update, opts)

How to achieve this cleanly without ifs. For any struct in Service.

Zayn Korai
  • 493
  • 1
  • 6
  • 24

1 Answers1

3

First, your update document should not contain an embedded document if Product is the Go struct that models your documents. It should be:

update := bson.D{
    {"$set", bson.D{
        {"status", product.Status},
        {"text", product.Text},
        {"display_name", product.DisplayName},
    }},
}

Now on to your issue. You explicitly tell in the update document to set them to their zero value, so that's what MongoDB does.

If you don't want to set zero values, don't add them to the update document. Build your update document like this:

setDoc := bson.D{}
if product.Status != 0 {
    setDoc = append(setDoc, bson.E{"status", product.Status})
}
if product.Text != "" {
    setDoc = append(setDoc, bson.E{"text", product.Text})
}
if product.DisplayName != "" {
    setDoc = append(setDoc, bson.E{"display_name", product.DisplayName})
}

update := bson.D{{"$set", setDoc}}

Note that you can achieve the same if you use the ,omitempty BSON tag option and use / pass a Product struct value for the setDoc:

type Product struct {
    ID          primitive.ObjectID `json:"id" bson:"_id"`
    Status      int                `json:"status" bson:"status,omitempty"`
    DisplayName string             `json:"displayName" bson:"display_name,omitempty"`
    Text        string             `json:"text" bson:"text,omitempty"`
}

And then simply:

update := bson.D{{"$set", product}}
icza
  • 389,944
  • 63
  • 907
  • 827
  • So there no clean way to do it, I mean without ifs? Because Product is not only struct/document in service. So is there is way to do it generically? – Zayn Korai Dec 15 '22 at 13:38
  • 1
    @ZaynKorai You can do it in a "clean" way if you use `,omitempty` and a `Product` value for the `setDoc`. See edited answer. But be sure to use consistent `bson` tag names, ones that match fields in your MongoDB (the ones you posted in the question seems off). – icza Dec 15 '22 at 13:42
  • Updated the question, I wrote Product struct just for the question so made a mistake their. It's fixed now. – Zayn Korai Dec 15 '22 at 13:48
  • 1
    @ZaynKorai OK, then using the struct with `,omitempty` is the simplest solution. – icza Dec 15 '22 at 13:57