在go中使用预构造的字符串作为bson.M进行mgo查询

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']

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.