2014-09-25 4 views
4

Предположим, что у вас есть рабочий процесс, состоящий из нескольких внедренных узлов разных типов. Поскольку узлы различных типов, я думал об использовании интерфейсов Golang здесь и придумал следующее:Как использовать тип интерфейса в качестве модели в mgo (Go)?

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 и у меня есть образец данных семян в нем. Использование оксида магния, когда я пытаюсь найти рабочий процесс (учитывая его ID):

w = &Workflow{} 
collection.FindID(bson.ObjectIdHex(id)).One(w) 

Я получаю ошибку - значение типа bson.M не может быть назначен на тип узла.

Это также немного странно для меня, что как бы mgo не подключил встроенные документы Node в структуру Go без какой-либо информации о типе. Может быть, мне нужно взглянуть на проблему с другой точки зрения.

Любые предложения будут высоко оценены.

ответ

5

Вы не можете использовать интерфейс в документе по той причине, что вы указали. Декодер не имеет информации о типе, который нужно создать.

Один из способов справиться с этим для определения структуры для хранения информации типа:

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 { 
} 
+0

Отлично! Это работает. Ухоженная. – shardnit

+0

Можете ли вы привести пример того, как будет реализована функция setBSON? Я не знаю, как декодировать строку типа. – e3matheus

1

После ответа Саймона Фокса относительно осуществления 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 
} 

То, что вы хотите сделать сейчас: как только вы распаковать объект BSON из Монго, вы хотите, чтобы иметь возможность знать, если каждый узел является либо EmailNode или TwitterNode.

Поскольку вы храните узлы в качестве интерфейса Node, у mgo нет способа узнать, какую конструкцию реализовать, поэтому вы должны явно указать это. Вот SetBSON.

В вашем примере проблема возникает из-за этого Workflow.Nodes, который представляет собой фрагмент интерфейса Node. Как это просто кусок, лучшее, что вы создаете пользовательский тип, который ГГО будет иметь возможность звонить когда демаршаллизации в BSON:

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 
} 
Смежные вопросы