I've got a Go app which uses MongoDB data storage, but still want it to be easy to switch out drivers (to PostgresSQL, who knows) and to test.
So I'm passing around a Storage interface which describes what a storage should do.
type Storage interface {
InsertOne(ctx context.Context, collection string, document interface{}) error
FindOne(ctx context.Context, collection string, filters map[string]string) (map[string]interface{}, error)
}
Then I've got a MongoDB implementation of that:
type Mongo struct {
Client *mongo.Client
Database string
}
func (m *Mongo) InsertOne(ctx context.Context, collection string, document interface{}) error {
_, err := m.Client.Database(m.Database).Collection(collection).InsertOne(ctx, document)
if err != nil {
return err
}
return nil
}
func (m *Mongo) FindOne(ctx context.Context, collection string, filters map[string]string) (map[string]interface{}, error) {
var result bson.M
var filter bson.D
for k, v := range filters {
filter = append(filter, bson.E{
Key: k,
Value: v,
})
}
err := m.Client.Database(m.Database).Collection(collection).FindOne(ctx, filter).Decode(&result)
if err != nil {
return nil, err
}
resultMap := make(map[string]interface{}, len(result))
for k, v := range result {
resultMap[k] = v
}
return resultMap, nil
}
I'm using bson.D to filter Mongo, so passing in filters map[string]string and converting it to bson.D.
This works fine when the filter is something simple like bson.D{{"title", "The Room"}}, simply pass it to the function like map[string]string{"title": "The Room"}.
But it's not so simple when it's a more complex filter like bson.D{{"population", bson.D{{"$lte", 500}}}}
This approach is not looking that good at the moment, feels like I'm very limited in what I can do.
So I'm looking for advice on a better way to do this.