用Go的范型实现Mongodb的CRUD操作

MongoDB是一个非常强大的数据库,它提供了一个非常简单的接口,可以让我们在Go中使用它。
前几天用gin+gorm实现一个小功能,根据操作的方法,发现很容易实现通用的CRUD操作。
比如批量查询数据时,可以定义一个interface或具体结构体去绑定结果集:

# 返回值也可以定义为interface{}
func FindAllAd(filter interface{}, sort interface{}) ([]model.Ad, error) {
	cursor, err := model.Mongo.Collection((&model.Ad{}).CollectionName()).Find(context.Background(), filter, options.Find().SetSort(sort))
	if err != nil {
		return nil, err
	}
	defer cursor.Close(context.Background())
	var results []model.Ad
	for cursor.Next(context.Background()) {
		# 根据返回值的类型,可以定义为interface{}
		var result model.Ad
		err := cursor.Decode(&result)
		if err != nil {
			return nil, err
		}
		results = append(results, result)
	}
	return results, nil
}

上述的代码里,加入返回值是interface{},那么返回结果的格式是这样的:

[[{Key:_id Value:3644d977-d2ff-430e-a8ca-b19c643fcb91} {Key:title Value:重版来袭} {Key:url Value:} {Key:type Value:0} {Key:sort Value:0} {Key:create_at Value:1659175157} {Key:update_at Value:1659175157}]]

这样的格式不方便前端处理。

如果是绑定具体的结构体,那么返回结果的格式是这样的:

[{ID:3644d977-d2ff-430e-a8ca-b19c643fcb91 Title:重版来袭 Url: Type:0 Sort:0 CreateAt:1659175157 UpdateAt:1659175157}]

显然下面这种方式更加方便处理。
如果不做封装的情况下,需要在每个模型结构体去定义查询方法,那么就需要额外增加代码,通用型不强。
于是我就尝试用范型去封装通用查询方法:

package model

import (
	"context"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type IMongo interface {
	CollectionName() string
}

type Dao[T IMongo] []T

func (d *Dao[T]) CollectionName() string {
	var m T
	return m.CollectionName()
}

// InsertOne 新增单条记录
func (d *Dao[T]) InsertOne(document T) (*mongo.InsertOneResult, error) {
	return Mongo.Collection(d.CollectionName()).InsertOne(context.Background(), document)
}

// FindAll 获取所有记录
func (d *Dao[T]) FindAll(filter interface{}, sort interface{}) ([]T, error) {
	cursor, err := Mongo.Collection(d.CollectionName()).Find(context.Background(), filter, options.Find().SetSort(sort))
	if err != nil {
		return nil, err
	}
	defer cursor.Close(context.Background())
	var results []T
	for cursor.Next(context.Background()) {
		var result T
		err := cursor.Decode(&result)
		if err != nil {
			return nil, err
		}
		results = append(results, result)
	}
	return results, nil
}

至于要额外封装IMongo的接口,主要考虑两个原因:

  • 每个模型结构体要额外定义一个返回文档名称的方法,作为操作目标;
  • 目前interface只能约束基本类型,但结构体不支持,比如:
    # 如果是结构体不支持约束
    type Number interface {
      int | int32 | int64 | float32 | float64
    }
    
    即使后面能支持约束,也不方便维护,因为每增加一个模型就要去修改这个约束接口。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技你太美

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值