Search code examples
mongodbgocrudbsonmongo-go

Decode in BSON, an interface which is implemented by a private structure


I am trying to do a basic TODO list application in Go. I am creating the CRUD operations on my cluster from mongodb atlas. But I have a problem decoding BSON objects. For my model I use a struct which is unimported but it implements a interface which is used in the repo. When trying to read from database I get this error:

panic: no decoder found for interfaces.IToDoItem

I know I should somehow implement a decoder for my interface but can not realize how to do it, without exporting my main struct from model. That would also mean I won't have a privacy in the model and the items in the model can be accessed in any mode all around the program, a thing which I think is wrong.

Here is my code:

model.go

type toDoItem struct{
    ItemId      int             `bson:"itemId,omitempty"`
    Title       string          `bson:"title,omitempty"`
    Description string          `bson:"description,omitempty"`
}

func New(itemId int,title string,description string) interfaces.IToDoItem {
    return toDoItem{
        ItemId:      itemId,
        Title:       title,
        Description: description,
    }
}

func (item toDoItem)GetItemId()int{
    return item.ItemId
}

func (item toDoItem)GetTitle()string{
    return item.Title
}

func (item toDoItem)GetDescription()string{
    return item.Description
}

Interface

type IToDoItem interface {
    GetItemId() int
    GetTitle() string
    GetDescription() string
}

repo function

func (r *Repository)GetAll() []interfaces.IToDoItem{
    cursor, err := r.collection.Find(context.TODO(), bson.D{})
    if err != nil{
        panic(err)
    }
    defer cursor.Close(context.Background())

    var allItems []interfaces.IToDoItem

    for cursor.Next(context.Background()){
        var result interfaces.IToDoItem
        err := cursor.Decode(&result)
        if err!= nil{
            panic(err)
        }
        allItems = append(allItems[:],result)
    }
    fmt.Println(allItems)
    return []interfaces.IToDoItem{}
}

For now it does not return anything because I want to resolve the issues at the repo level.


Solution

  • Interfaces are not concrete types, there may be multiple (any number of) types implementing it. The driver rightfully doesn't know how to instantiate it.

    Fields of toDoItem are exported, there is no reason to make toDoItem itself unexported. Just export it and use a slice of []ToDoItem (or []*ToDoItem), and all your problems go away.

    Also note that there is a Cursor.All() method to get all results, so you don't have to iterate over the documents and call Cursor.Decode() for each.

    If you'd ever need to register custom decoders, check out this answer how to do it:

    How to ignore nulls while unmarshalling a MongoDB document?