Reputation: 1177
The larger goal here is to have an update query in go set only those fields that are sent to it in the request.
For e.g., I have a document to update where I am allowing the user to update a variable number of fields by simply specifying them in the request like so -
{
"field1": valueOfField1,
"field2": valueOfField2,
"field3": valueOfField3,
...
}
The problem here is that when I decode this input using json.Decode
to a custom struct type which has most fields optional, the values which do not exist in the input are left nil
.
My struct looks like this -
type Fields struct {
Field1 string `bson:"field1" json:"field1,omitempty"`
Field2 string `bson:"field2" json:"field2"`
Field3 time.Time `bson:"field3,omitempty" json:"field3,omitempty"`
Field4 bool `bson:"field4,omitempty" json:"field4,omitempty"`
...
}
Now in my update query, I say,
bson.M{"$set": bson.M{"field1": body.Field1, "field2": body.Field2, "field3": body.Field3, "field4": body.Field4, ...}}
The problem is that if one of these fields does not exist in the input, it still over-writes existing values in the database and makes it null.
To avoid this, I would ideally want that this {"field1": body.Field1, "field2": body.Field2, "field3": body.Field3, "field4": body.Field4, ...}
part is dynamically constructed depending on the fields coming in.
To do that, I did a json.Marshal
of the input, like so -
finalbody, err := json.Marshal(body)
And then I am trying to use this inside the $set field as -
bson.M{"$set": string(finalbody)}
Of course this gives me an error saying - "Modifiers operate on fields but we found a string instead".
The string is perfectly just like the bson.M would've been except that it is not a bson.M i.e. {"field1": valueOfField1, "field2": valueOfField2, "field3": valueOfField1, ...}
Where am I going wrong? [... represents 'and so on']
Upvotes: 2
Views: 2512
Reputation: 1177
I found the solution by Unmarshalling finalbody into a map[string]interface{} and then using that as the update map.
So,
finalbody, err := json.Marshal(body)
var finalbodymap map[string]interface{}
json.Unmarshal(finalbody, &finalbodymap)
Of course, you need to add some error handling so the final code looks like this -
finalbody, err := json.Marshal(body)
if err != nil {
log.Println(err)
return
}
var finalbodymap map[string]interface{}
if err = json.Unmarshal(finalbody, &finalbodymap); err != nil{
log.Println(err)
}
And then in the update query, I can simply write -
bson.M{"$set": finalbodymap}
One issue here though was that while Marshalling, it would set any time.Time type values to nil i.e. "0001-01-01T00:00:00Z". I suspect such behavior could be observed with certain other types as well.
Upvotes: 3