Problem with any type in Golang

Context

I’m using MongoDB to store data where one of the fields can be of any type, ranging from primitive types (such as int, string, etc.) to more complex structures (like map, slice, or even combinations of these). As a result, my Go struct contains a field with the type any to accommodate this flexibility.

Problem

When I retrieve this data from MongoDB, the Go MongoDB driver converts the field into a primitive.D type, which is essentially a slice of primitive.E structs (each containing Key and Value properties). This transformation causes an issue when the data is marshaled to JSON: the result ends up being formatted like this (where bars[i].value is the field in question):

{
  "id": "123",
  "bars": [
    {
      "name": "map",
      "value": [
        {
          "Key": "im",
          "Value": "a map"
        }
      ]
    }
  ]
}

Example

I’ve created a minimal example to reproduce the issue: Link to Gist

Questions

I have a few questions regarding this behavior:

  1. Why does this happen?
    Why does the MongoDB driver convert this field into a primitive.D type instead of a more straightforward representation like map[string]interface{}?

  2. What was the reasoning behind this decision?
    Why did the MongoDB Go driver choose to represent this data as primitive.D instead of something more directly usable in Go, like a map or slice?

  3. Is there a way to control the type conversion?
    I know I can manually convert the primitive.D into a map when marshalling to JSON, but is there a way to instruct the driver to convert it into a map (or another suitable structure) directly during the retrieval process?

Answers:

  1. Why does this happen?
    MongoDB BSON documents preserve the order of fields, where Go map values do not. While it’s possible to unmarshal into a map[string]any, it would not preserve the original document order. There is no native Go type that can represent any MongoDB BSON document and preserve the order, so the Go Driver uses bson.D as the default BSON document representation when unmarshaling.
  2. What was the reasoning behind this decision?
    See the answer to #1.
  3. Is there a way to control the type conversion?
    You can set BSONOptions.DefaultDocumentM to true, which will cause the Go Driver to always unmarshal BSON documents into a bson.M value instead of a bson.D when there’s no other type information. For example:
opts := options.Client().SetBSONOptions(&options.BSONOptions{
	DefaultDocumentM: true,
})
client, err := mongo.Connect(context.TODO(), opts)
// ...

P.S. If you’re unmarshaling BSON directly, you can call Decoder.DefaultDocumentM to cause a bson.Decoder to unmarshal BSON documents into bson.M instead of bson.D .

2 Likes

Thanks for the answer!

I was using an older version of the driver and hadn’t found anything related. When reproducing the example, I didn’t pay attention to the version :confused:

But the solution worked perfectly!

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.