Home > other >  How to retrieve a nested array of objects in mongodb with golang?
How to retrieve a nested array of objects in mongodb with golang?

Time:07-21

I am using Golang/Fiber Mongo driver.

I have simple struct for blog post:

type Post struct {
    ID          primitive.ObjectID `json:"_id" bson:"_id,omitempty"`
    Title       *string            `json:"title" bson:"title"`
    Slug        *string            `json:"slug" bson:"slug"`
    Content     []interface{}      `json:"content" bson:"content,omitempty"` // same as any[]
    CreatedAt   time.Time          `json:"created_at" bson:"created_at"`
    UpdatedAt   time.Time          `json:"updated_at" bson:"updated_at"`
    PublishedAt *time.Time         `json:"published_at" bson:"published_at"`
}

In content I put array of objects:

[
    {
        data: 'blah blah'
    },
    {
        data: 'blah blah'
    }
]

Method itself is pretty straightforward:

func GetPostBySlug(slug string) (Post, error) {
    post := Post{}

    filter := bson.M{
        "slug": slug,
    }

    database, error := Database.GetMongoDatabase()

    if error != nil {
        println(error)
    }

    collection := database.Collection(model_name)

    err := collection.FindOne(ctx, filter).Decode(&post)

    if err != nil {
        return post, err
    }

    return post, nil
}

And this is what I get:

"_id": "000000000000000000000000",
"title": "Test",
"slug": "test",
"content": [ // array of arrays? what??
    [
        {
            "Key": "data",
            "Value": "blah blah"
        },
        {
            "Key": "data",
            "Value": "blah blah"
        },
    ]
],
"created_at": "2022-06-07T21:08:04.261Z",
"updated_at": "2022-07-20T21:42:36.717Z",
"published_at": null

In mongodb the content field is saved exactly as I passed it, but when I am trying to get the document, the content transforms to this weird array of arrays with key-value pairs.

If I save something like:

content: [
    {
        data: {
            test: 'test',
            what: 'what'
        }
    }
]

It will transform to this:

content: [
    [
        Key: "data",
        Value: [
            {
                Key: "test",
                Value: "test" 
            },
            {
                Key: "what",
                Value: "what" 
            },
        ]
    ]
]

I understand the reasons behind this (this is just golang's way to handle JSON?) and assume there should be an extra step somewhere in the middle, but I have no idea what exactly I need to do

CodePudding user response:

Modify type of Content to interface{} or just string.

CodePudding user response:

The reason for what you see is because you use interface{} for the element type of Content:

Content []interface{}

If you use interface{}, it basically carries no type information what type the driver should use when umarshaling the array elements, so the driver will choose / use bson.D to represent the documents of the content field. bson.D is a slice that holds the ordered list of fields of the documents, that's why you see an "array of arrays". Each bson.D is a slice, representing a document.

type D []E

type E struct {
    Key   string
    Value interface{}
}

If you can model the array elements with a struct, use that, e.g.:

type Foo struct {
    Bar string
    Baz int
}

Content []Foo `json:"content" bson:"content,omitempty"`
// Or a pointer to Foo:
Content []*Foo `json:"content" bson:"content,omitempty"`

If you don't have a fixed model for the array elements, alternatively you may use bson.M which is a map (but the fields / properties will be unordered which may or may not be a problem):

type M map[string]interface{}

Using it:

Content []bson.M `json:"content" bson:"content,omitempty"`
  • Related