假设您的工作流由多个不同类型的嵌入式节点组成。由于节点的类型不同,我想在这里使用Golang接口并提出以下内容:
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
}
这些工作流存储在MongoDB中,我有样本种子数据。使用mgo,当我尝试找到工作流程(给定其ID)时:
w = &Workflow{}
collection.FindID(bson.ObjectIdHex(id)).One(w)
我收到错误 - 类型为bson.M的值不能分配给Node。
对我来说,mgo如何在没有任何类型信息的情况下将嵌入式Node文档解组为Go结构,这也让我觉得有点奇怪。我可能需要从另一个角度来看问题。
任何建议都将受到高度赞赏。
答案 0 :(得分:7)
出于上述原因,您无法在文档中使用界面。解码器没有关于要创建的类型的信息。
处理此问题的一种方法是定义一个结构来保存类型信息:
type NodeWithType struct {
Node Node `bson:"-"`
Type string
}
type Workflow struct {
CreatedAt time.Time
StartedAt time.Time
CreatedBy string
Nodes []NodeWithType
}
在此类型上实现SetBSON功能。此函数应解码类型字符串,根据该字符串创建正确类型的值并解组该值。
func (nt *NodeWithType) SetBSON(r bson.Raw) error {
}
答案 1 :(得分:3)
根据Simon Fox关于SetBSON
实施的回答,这是一个更准确的答案。
我们来看一段原始代码:
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
}
您现在要做的是:一旦您从Mongo解组BSON对象,您就希望能够知道每个节点是EmailNode
还是TwitterNode
。
当您将节点存储为Node
接口时,mgo无法知道要实现的结构,因此您必须明确告知它。这是SetBSON
。
在您的示例中,问题来自此Workflow.Nodes
,它是Node
接口的一部分。因为它是一个简单的切片,最好的是你创建一个自定义类型,当解组BSON时mgo将能够调用:
type NodesList []Node
// The updated Workflow struct:
type Workflow struct {
CreatedAt time.Time
StartedAt time.Time
CreatedBy string
Nodes NodesList
}
现在,您可以在此SetBSON
上实施NodesList
并说明其工作原理。请注意,当您使用指针时,您可以定义变量中包含的内容:
// Note that you must use a pointer to the slice
func (list *NodesList) SetBSON(raw raw.BSON) error {
// Now you need to create the objects according to your
// own domain logic and what's contained inside "raw":
if raw.something {
*list = append(*list, &TwitterNode{})
} else {
*list = append(*list, &EmailNode{})
}
return nil
}
答案 2 :(得分:0)
我有类似的问题, 我有一个像Workflow这样的结构, 我新建了一个工作流程实例。
var wk Workflow
//do something to instantiate wk
然后我想
collection.InsertOne(context.Background(), wk)
但是我犯了一个错误
collection err: cannot transform type *WorkFlow to a *bsonx.Document