我正在开发一个Go应用程序,该应用程序利用gqlgen
(GraphQL代码生成器)与客户端进行通信,并使用MongoDB作为后端数据库。对于我们的GraphQL模式,我们有几种类型的游戏(GameA
,GameB
,GameC
等),都实现了基本的Game
类型。客户端请求游戏大厅(具有Game
属性),并作为整体从查询中获得当前正在玩的任何游戏作为回报。 gqlgen
将此模式转换为具有单个必需功能的Game
接口:IsGame()
,用于将各种类型的游戏与单个接口相关联。
//models.go
/* ... */
// Define the Game interface
type Game interface {
IsGame()
}
// Define GameA
type GameA struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Score []int `json:"score"`
Players []Player `json:"players"`
}
// GameA implements the Game interface
func (g GameA) IsGame() {}
/* ... */
以上代码在GraphQL服务器和客户端通信之前一直有效。 尝试向Mongo读写Game
时出现问题,因为我们无法提前知道游戏的类型:
//db.go
/* ... */
// SetGame takes a game model, game ID, and an insert switch to update a game in the db.
// If insert is true and a game with a matching ID does not already exist, a new
// game will be created.
func (c *Client) SetGame(game *models.Game, id primitive.ObjectID, insert bool) error {
d, err := bson.Marshal(game)
if err != nil {
return err
}
res, err := c.GameCollection.ReplaceOne(
context.TODO(), bson.M{"_id": id}, d,
options.Replace().SetUpsert(insert),
)
if err != nil {
return err
}
if res.UpsertedCount == 0 && res.MatchedCount == 0 {
return fmt.Errorf("no games with ID %s found.", id.Hex())
}
return nil
}
// GetGame accepts an ID (primitive.ObjectID) and
// returns the game it discovers.
func (c *Client) GetGame(id primitive.ObjectID) (*models.Game, error) {
var result *models.Game // This doesn't work obviously, since we don't know what type of game it is
err := c.GameCollection.FindOne(
context.TODO(), bson.M{"_id": id},
).Decode(&result)
return result, err
}
/* ... */
从理论上讲,SetGame(...)
应该可以按预期工作,但是GetGame(...)
不能这样做,因为.Decode()
需要一种将结果清空的结构。如果不提前知道游戏类型,就无法正确解码FindOne(...)
函数的结果。这把我引到我的问题:
为每种类型的游戏制作多个GetGameX(...)
函数(此函数会随着时间的推移而增加,因此尝试减少编码开销)是很短的,有没有办法使这些函数支持多态还是没有GraphQL和Mongo的属性?