What is the fastest way to decode a nested MongoDB document array into a struct slice in Go?

Issue

Here’s my problem. I’m using gqlgen library to run a GraphQL server. In my database I have a MongoDB document that looks like this:

{
  "_id": ObjectID(...),
  "somefield": "data",
  "anArrayOfObjects": [
    {
      "field1": "value1",
      "field2": "value2
    },
    ...
  ]
}

What I want is to be able to only decode the field anArrayOfObjects into a slice (may look like []MyObj, where MyObj is a struct).

(For a little context, I only want this field because the array is quite large and it has it’s own GraphQL resolver).

Here’s currently what I have tried:

ObjectID, err := primitive.ObjectIDFromHex("someid")
// do some error check

opts := options.FindOne().SetProjection(
    bson.D{{Key: "anArrayOfObjects", Value: 1}, {Key: "_id", Value: 0}},
)
myslice = []MyObj{}
err := coll.FindOne(ctx, bson.M{"_id": ObjectID}, opts).Decode(&myslice)
// err is "cannot decode document into []MyObj"

If instead I run

result = primitive.D{} // or primitive.M{}
err := coll.FindOne(ctx, bson.M{"_id": ObjectID}, opts).Decode(&result)
// err is nil and result contains everything I want but not in the "ideal" data structure

Doing the latter, I reckon I can iterate through result and then map everything to every struct field (similar to this answer). But my intuition is that probably there is a better way to do this than that.

Thanks in advance!

Solution

The result of your query will be a document holding the array under the anArrayOfObjects field, and not the array value of that field alone. And you can’t decode a document into a Go slice, that’s what the error message tells you.

So unmarshal into a struct having that field:

var result struct{
    MySlice []MyObj `bson:"anArrayOfObjects"`
}
err := coll.FindOne(ctx, bson.M{"_id": ObjectID}, opts).Decode(&result)

The array from MongoDB will be result.MySlice.

Answered By – icza

Answer Checked By – Senaida (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.