Suppose you have a workflow that consists of multiple embedded nodes of different types. Since nodes are of different types, I thought of using Golang interfaces here and came up with following:
type Workflow struct {
CreatedAt time.Time
StartedAt time.Time
CreatedBy string
Nodes []Node
}
type Node interface {
Exec() (int, error)
}
type EmailNode struct {
From string
To string
Subject string
Body string
}
type TwitterNode struct {
Tweet string
Image []byte
}
func (n *EmailNode) Exec() (int, error){
//send email
return 0, nil
}
func (n *TwitterNode) Exec() (int, error) {
//send tweet
return 0, nil
}
These workflows are stored in MongoDB and I have sample seed data in it. Using mgo, when I try to find a workflow (given its ID):
w = &Workflow{}
collection.FindID(bson.ObjectIdHex(id)).One(w)
I get the error - value of type bson.M is not assignable to type Node.
It also feels a bit weird to me that how would mgo unmarshal embedded Node documents into a Go struct without any type information. May be I need to look at the problem from another point of view.
Any suggestions would be highly appreciated.
One of the core implementations of composition is the use of interfaces. An interface defines a behavior of a type. One of the most commonly used interfaces in the Go standard library is the fmt.Stringer interface:
How to create an interface? In Go language, you can create an interface using the following syntax: type interface_name interface{ // Method signatures } For Example: // Creating an interface type myinterface interface{ // Methods fun1() int fun2() float64 }
The Stringer interface has only one method, called String () that returns a string. A method is a special function that is scoped to a specific type in Go. Unlike a function, a method can only be called from the instance of the type it was defined on.
Just like defining a struct, Go uses curly braces ( {}) to surround the definition of the interface. In comparison to defining structs, we only define the interface’s behavior; that is, “what can this type do”. In the case of the Stringer interface, the only behavior is the String () method.
You cannot use an interface in a document for the reason you noted. The decoder has no information about the type to create.
One way to handle this is to define a struct to hold the type information:
type NodeWithType struct {
Node Node `bson:"-"`
Type string
}
type Workflow struct {
CreatedAt time.Time
StartedAt time.Time
CreatedBy string
Nodes []NodeWithType
}
Implement the SetBSON function on this type. This function should decode the type string, create a value of the correct type based on that string and unmarshal to that value.
func (nt *NodeWithType) SetBSON(r bson.Raw) error {
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With