Para responder a "Minha pergunta real:Como posso personalizar o comportamento do mgo antes do upsert? " - você pode personalizar o marshalling bson definindo bson Getter ao modelo.
Para ilustrar como funciona, vamos simplificar o modelo para evitar documentos aninhados:
type Game struct {
ID int `bson:"_id"`
Name string
Stats [] float64
}
Com newGame da seguinte forma:
newGame := Game{
ID: 1,
Name: "foo",
Stats: []{5.0}
}
A atualização
col.UpsertId(newGame.ID, newGame)
por padrão, os marechais newGame
em JSON, produzindo consulta mongo como:update({_id:1}, {name: "foo", stats: [5]}, {upsert: true});
Para usar
$set
, $push
etc, você pode definir um getter bson personalizado. Por exemplo. func (g Game) GetBSON() (interface{}, error) {
return bson.M{
"$set": bson.M{"name": g.Name},
"$push": bson.M{"stats": bson.M{"$each": g.Stats}},
}, nil
}
Portanto, a atualização
col.UpsertId(newGame.ID, newGame)
irá produzir uma consulta mongodb update({_id:1}, {$set: {name: "foo"}, $push: {stats: {$each: [5]}}}, {upsert: true});
Para deixar bem claro - o empacotador personalizado será usado em todas as consultas mgo, então você provavelmente não deseja defini-lo diretamente no modelo, mas em seu derivado para usar apenas em operações de upsert:
type UpdatedGame struct {
Game
}
func (g UpdatedGame) GetBSON() (interface{}, error) {
return bson.M{....}
}
.....
newGame := Game{
ID: 1,
Name: "foo",
Stats: []{5.0}
}
col.UpsertId(newGame.ID, UpdatedGame{newGame})