mgo关系维护v2
程序员文章站
2022-04-22 08:18:19
...
package main
import (
"dog/util/conman"
"dog/util/log"
"dog/util/v"
"dog/util/val"
"errors"
"fmt"
"reflect"
"strings"
mgo "gopkg.in/mgo.v2"
)
// 暂不支持跨数据库
/*B,C 都有外键指向A*/
// 多对一
// 有对象的外键指向A,A被删除时抛出异常
const (
A_CName = "a"
B_CName = "b"
C_CName = "c"
D_CName = "d"
)
type A struct {
AId int64 `json:"a_id" bson:"_id,omitempty"`
AName string `json:"a_name" bson:"a_name"`
AAge string `json:"a_age" bson:"a_age"`
BS []B `bson:"-" rel:"o2m:b"` //一对多,B对象的外键:a_id(在B对象中遍历到即可,也可指定fk:a_id),关联到本端的_id键(在B对象中遍历到即可,也可指定ref:_id)
// C C `bson:"-" rel:"o2o:c"` //关系为一对多改为一对一,可作为一对一查询,此时不能构建反向一对一查询结构体,会成为无效的递归类型
}
type B struct {
BId int64 `json:"b_id" bson:"_id,omitempty"`
BName string `json:"b_name" bson:"b_name"`
AId int64 `json:"a_id" bson:"a_id,omitempty" fk:"a._id"` //外键指向a表的_id字段
CId int64 `json:"c_id" bson:"c_id" fk:"c._id"` //外键指向a表的_id字段
// DS []D `bson:"-" rel:"o2m:d"`
C C `bson:"-" fkv:"c"` //a->外键值对应的表
// A A `bson:"-" fkv:"a"` //a->外键值对应的表
}
type C struct {
CId int64 `json:"c_id" bson:"_id,omitempty"` //一对一关系,主键作为外键
CName string `json:"c_name" bson:"c_name"`
DS []D `bson:"-" rel:"o2m:d"` //a->外键值对应的表
}
type D struct {
Id int64 `json:"d_id" bson:"_id,omitempty"`
DName string
CId int64 `json:"c_id" bson:"c_id" fk:"c._id"`
}
// 多对多
// E或F被删除时,关系表自动被删除
// 默认关系表e_f,且不可设置
// 主关系表名在关系表开头
const (
E_CName = "e"
F_CName = "f"
)
type E struct {
Eid int64 `json:"e_id" bson:"_id"`
EName string `json:"e_name" bson:"e_name"`
DS []D `bson:"-" rel:"m2m:_id.d.2"` //_id本端键,对应关系表的键为e_id(表名+键名),f->对多个f表对象,.1主关系标识->可用来扩展很多功能
FS []F `bson:"-" rel:"m2m:_id.f.1"` //_id本端键,对应关系表的键为e_id(表名+键名),f->对多个f表对象,.1主关系标识->可用来扩展很多功能
}
type F struct {
Fid int64 `json:"_id" bson:"_id"`
FName string `json:"f_name" bson:"f_name"`
CS []C `bson:"-" rel:"m2m:_id.c.2"` //_id本端键,对应关系表的键为f_id(表名+键名),e->对多个e表对象,.2从关系标识->可用来扩展很多功能
ES []E `bson:"-" rel:"m2m:_id.e.2"` //_id本端键,对应关系表的键为f_id(表名+键名),e->对多个e表对象,.2从关系标识->可用来扩展很多功能
}
func main() {
icm := conman.NewConMan()
defer icm.Free()
// insB(icm)
// insA(icm)
// setB(icm)
// DelA(icm)
// updateB(icm)
// insD(icm)
// insC(icm)
// a := new(A)
// if err := FindOne(icm.MgoConn().DB("test").C(A_CName), v.M{"_id": 3}, a); err != nil {
// log.Error.Fatal(err)
// }
// log.Info.Println(val.JsonPretty(a))
// insE(icm)
// insF(icm)
// e := new(E)
// if err := FindId(icm.MgoConn().DB("test").C(E_CName), 1, e); err != nil {
// log.Error.Fatal(err)
// }
// log.Info.Println(val.JsonPretty(e))
as := &[]A{}
if err := Find(icm.MgoConn().DB("test").C(A_CName), nil, as, 1, 5); err != nil {
log.Error.Fatal(err)
}
log.Info.Println(val.JsonPretty(as))
// if err := UpdateRel(icm.MgoConn().DB("test").C(E_CName), &E{Eid: 1, FS: []F{{Fid: 1}, {Fid: 2}}}); err != nil {
// log.Error.Fatal(err)
// }
// if err := DelRel(icm.MgoConn().DB("test").C(E_CName), &E{Eid: 1},
// &F{Fid: 1}); err != nil {
// log.Error.Fatal(err)
// }
}
func updateB(icm conman.IConMan) {
b := &B{BName: "bname000001", AId: 5, BId: 1}
if err := UpdateId(icm.MgoConn().DB("test").C(B_CName), b); err != nil {
log.Error.Fatal(err)
}
}
func setB(icm conman.IConMan) {
// if err := Set(icm.MgoConn().DB("test").C(B_CName), new(B), v.M{"_id": 1}, v.M{"a_id": 3}); err != nil {
// log.Error.Fatal(err)
// }
if err := SetById(icm.MgoConn().DB("test").C(B_CName), new(B), 3, v.M{"a_id": 3}); err != nil {
log.Error.Fatal(err)
}
}
func insA(icm conman.IConMan) {
a := &A{AName: "aname1"}
if err := Insert(icm.MgoConn().DB("test").C(A_CName), a); err != nil {
log.Error.Fatal(err)
}
}
func insE(icm conman.IConMan) {
a := &E{EName: "ename3"}
if err := Insert(icm.MgoConn().DB("test").C(E_CName), a); err != nil {
log.Error.Fatal(err)
}
}
func insF(icm conman.IConMan) {
a := &F{FName: "fname3"}
if err := Insert(icm.MgoConn().DB("test").C(F_CName), a); err != nil {
log.Error.Fatal(err)
}
}
func insD(icm conman.IConMan) {
d := &D{DName: "dname1", CId: 1}
if err := Insert(icm.MgoConn().DB("test").C(D_CName), d); err != nil {
log.Error.Fatal(err)
}
}
func insC(icm conman.IConMan) {
c := &C{CName: "cname1"}
if err := Insert(icm.MgoConn().DB("test").C(C_CName), c); err != nil {
log.Error.Fatal(err)
}
}
func DelA(icm conman.IConMan) {
if err := Del(icm.MgoConn().DB("test").C(A_CName), new(A), nil); err != nil {
log.Error.Fatal(err)
}
}
func insB(icm conman.IConMan) {
b := &B{BName: "bname1", AId: 0, CId: 1}
if err := Insert(icm.MgoConn().DB("test").C(B_CName), b); err != nil {
log.Error.Fatal(err)
}
}
/*=================================================================================*/
func Skip(page, item int) int {
if page == 0 {
return 0
}
return (page - 1) * Limit(page, item)
}
func Limit(page, item int) int {
if item == 0 && page != 0 {
return 10
} else if item != 0 && page != 0 {
return item
}
return 0
}
// 查询
// 一个外键
func Find(c *mgo.Collection, query, ptr interface{}, page, item int) error {
log.Info.Println("find")
log.Info.Println(query)
if err := c.Find(query).Sort("-_id").Skip(Skip(page, item)).Limit(Limit(page, item)).All(ptr); err != nil {
return err
}
// log.Info.Println(c.Name, val.JsonPretty(query), val.JsonPretty(ptr))
fvs := reflect.ValueOf(ptr).Elem()
for i := 0; i < fvs.Len(); i++ { //遍历每一个元素
setRelVal(c, fvs.Index(i).Addr().Interface())
}
return nil
}
// 设置关联值
func setRelVal(c *mgo.Collection, ptr interface{}) error {
fv := reflect.ValueOf(ptr).Elem()
ft := fv.Type()
for i := 0; i < ft.NumField(); i++ { //找关系
j := i
// 外键值查找(注意,避免死循环)
fkvCName := ft.Field(i).Tag.Get("fkv") //外键值对应的表
if fkvCName != "" {
for i := 0; i < ft.NumField(); i++ {
fk := strings.Split(ft.Field(i).Tag.Get("fk"), ".")
if fk[0] != fkvCName {
if i == ft.NumField()-1 {
return fmt.Errorf("外键:%s 值,对应的外键没有找到", ft.Field(i).Name)
}
continue
}
fviPtr := fv.Field(j).Addr().Interface()
if err := FindOne(c.Database.C(fkvCName), v.M{fk[1]: fv.Field(i).Interface()}, fviPtr); err != nil {
return err
}
break //找到
}
}
// 关系对象查找
relTag := ft.Field(i).Tag.Get("rel")
if relTag == "" {
continue
}
rel := strings.Split(relTag, ":")
switch rel[0] {
case "o2m":
log.Info.Println("o2m")
if fv.Field(i).Kind() != reflect.Slice {
return errors.New("关系列类型错误,需为对象值的切片")
}
vi := fv.Field(i)
vivElemt := vi.Type().Elem()
FEPtr := reflect.New(vivElemt)
fit := reflect.TypeOf(FEPtr.Interface()).Elem()
for i := 0; i < fit.NumField(); i++ { //找外键
fkTag := fit.Field(i).Tag.Get("fk")
if fkTag == "" {
if i == fit.NumField()-1 {
return fmt.Errorf("关系对象:%s没有设置外键", fit.Name())
}
continue
}
fk := strings.Split(fkTag, ".")
if len(fk) < 2 {
return errors.New("外键格式错误")
}
relBsonTag := strings.Split(fit.Field(i).Tag.Get("bson"), ",")[0]
if relBsonTag == "" {
return errors.New("设置外键所在的列没有设置bson标签")
}
var bsonTag string
var fvRefVal interface{}
for i := 0; i < ft.NumField(); i++ {
bsonTag = strings.Split(ft.Field(i).Tag.Get("bson"), ",")[0]
if bsonTag != fk[1] {
if i == ft.NumField()-1 {
return errors.New("外键所指向的列不存在")
}
continue
}
fvRefVal = fv.Field(i).Interface()
break //找到
}
if err := Find(c.Database.C(rel[1]), v.M{relBsonTag: fvRefVal}, fv.Field(j).Addr().Interface(), 0, 0); err != nil {
return err
}
log.Info.Println(fv.Field(i))
break //只支持一个外键
}
case "m2m": //暂只支持主从查询不支持反向查询
log.Info.Println("m2m")
relSets := strings.Split(rel[1], ".")
if relSets[2] != "1" {
continue
}
relCName := GetRelCName(c, relSets)
// 关系键
relKey1 := c.Name + "_id"
relKey2 := relSets[1] + "_id"
log.Info.Println(relKey1, relKey2)
// 本端键对应的值
idVal1, err := GetFieldValByBsonTagId(fv)
if err != nil {
return err
}
// 对端关系值
relValMaps := []v.M{}
if err := c.Database.C(relCName).Find(v.M{relKey1: idVal1}).Select(v.M{"_id": 0, relKey2: 1}).All(&relValMaps); err != nil {
return err
}
relVals := []interface{}{}
for _, relValMap := range relValMaps {
relVals = append(relVals, relValMap[relKey2])
}
log.Info.Println(relVals)
// 查询关系值
if err := Find(
c.Database.C(relSets[1]),
v.M{"_id": v.M{"$in": relVals}},
fv.Field(i).Addr().Interface(), 0, 0,
); err != nil {
return err
}
default:
return fmt.Errorf("关系标签格式错误,列:%s,%s", "rel:\"o2m:b\"", "rel:\"m2m:_id.f.1\"")
}
}
return nil
}
// 从多对多关系设置中获取关系表名
// 本端关系_id.f.1
// 对端关系_id.e.2
func getCNameByM2MSet(set1, set2 string) (string, error) {
set1s, set2s := strings.Split(set1, "."), strings.Split(set2, ".")
if len(set1s) < 3 || len(set2s) < 3 {
return "", errors.New("getCNameByM2MSet:关系配置错误")
}
relCName := ""
if set1s[2] == "1" {
relCName = set2s[1] + "_"
if set2s[2] == "2" {
relCName += set1s[1]
} else {
return "", errors.New("从端关系设置错误")
}
}
if set2s[2] == "1" {
relCName = set1s[1] + "_"
if set1s[2] == "2" {
relCName += set2s[1]
} else {
return "", errors.New("从端关系设置错误")
}
}
if relCName == "" {
return "", errors.New("主从端关系设置错误")
}
return relCName, nil
}
// 获取多对多关系的主的关系键名
func getKey1ByM2MSet(set1, set2 string) (string, error) {
set1s, set2s := strings.Split(set1, "."), strings.Split(set2, ".")
if len(set1s) < 3 || len(set2s) < 3 {
return "", errors.New("getKey1ByM2MSet:关系配置错误")
}
key1 := ""
if set1s[2] == "1" {
key1 = set1s[0] + "_" + set2s[1]
} else {
key1 = set2s[0] + "_" + set1s[1]
}
return key1, nil
}
// 获取多对多关系的从的关系键名
func getKey2ByM2MSet(set1, set2 string) (string, error) {
set1s, set2s := strings.Split(set1, "."), strings.Split(set2, ".")
if len(set1s) < 3 || len(set2s) < 3 {
return "", errors.New("getKey1ByM2MSet:关系配置错误")
}
key1 := ""
if set1s[2] == "2" {
key1 = set1s[0] + "_" + set2s[1]
} else {
key1 = set2s[0] + "_" + set1s[1]
}
return key1, nil
}
func FindOne(c *mgo.Collection, query, ptr interface{}) error {
if err := c.Find(query).One(ptr); err != nil {
return err
}
return setRelVal(c, ptr)
}
func FindId(c *mgo.Collection, id int64, ptr interface{}) error {
return FindOne(c, v.M{"_id": id}, ptr)
}
// 更新
func Update(c *mgo.Collection, query, ptr interface{}) error {
// 判断添加的对象是否有外键
if err := CheckFK(c, ptr); err != nil {
return err
}
err := c.Update(query, ptr)
return err
}
func UpdateId(c *mgo.Collection, ptr interface{}) error {
id, err := GetIdVal(ptr)
if err != nil {
return err
}
return Update(c, v.M{"_id": id}, ptr)
}
// 修改
// 全部外键
func Set(c *mgo.Collection, ptr interface{}, query, setFields v.M) error {
// 判断修改的文档是否有外键
ft := reflect.TypeOf(ptr).Elem()
for i := 0; i < ft.NumField(); i++ {
fk := ft.Field(i).Tag.Get("fk")
if fk == "" {
continue
}
fkvs := strings.Split(fk, ".")
if len(fkvs) < 2 {
return errors.New("外键配置错误,正确格式:fk:\"a,_id\",a->表名,_id->指向的列")
}
// 检查外键是否被修改fk:"a._id"
bsonTag := strings.Split(ft.Field(i).Tag.Get("bson"), ",")[0]
if val, ok := setFields[bsonTag]; ok {
if count, err := c.Database.C(fkvs[0]).Find(v.M{fkvs[1]: val}).Count(); err != nil {
return err
} else if count == 0 {
return fmt.Errorf("外键:%s,指向值:%d,不存在", ft.Field(i).Name, val)
}
}
}
_, err := c.UpdateAll(query, v.M{"$set": setFields})
return err
}
func SetById(c *mgo.Collection, ptr interface{}, id int64, setFields v.M) error {
return Set(c, ptr, v.M{"_id": id}, setFields)
}
/*目前不做内嵌结构*/
// 并发没有加锁
// 添加对象,要检查关系
func Insert(c *mgo.Collection, ptr interface{}) error {
// 判断添加的对象是否有外键
if err := CheckFK(c, ptr); err != nil {
return err
}
// 配置_id
if err := SetId(c, ptr); err != nil {
return err
}
HERE:
if err := c.Insert(ptr); err != nil {
if strings.Contains(err.Error(), "index: _id_ dup key") { //_id重复
maxIdM := map[string]int64{}
if err := c.Pipe([]v.M{v.M{"$group": v.M{"_id": nil, "max_id": v.M{"$max": "$_id"}}}, v.M{"$project": v.M{"_id": 0}}}).One(&maxIdM); err != nil {
return err
}
if err := SetIntId4Max(c, maxIdM["max_id"]+1); err != nil {
return err
}
ft := reflect.TypeOf(ptr).Elem()
fv := reflect.ValueOf(ptr).Elem()
for i := 0; i < ft.NumField(); i++ {
bfv := strings.Split(ft.Field(i).Tag.Get("bson"), ",")[0]
if bfv != "_id" {
if i == ft.NumField()-1 {
return errors.New("插入的对象没有指定主键bson:\"_id\"")
}
continue
}
if _, ok := fv.Field(i).Interface().(int64); !ok {
return errors.New("主键只支持int64类型")
}
fv.Field(i).SetInt(maxIdM["max_id"] + 1)
break
}
goto HERE
}
return err
}
// 更新多对多关系
return UpdateRel(c, ptr)
}
// 多对多关系创建
// 只能从主端开始创建
// 没检查外键表关系是否存在
// 删除对象删除关系键TODO
func UpdateRel(c *mgo.Collection, ptr interface{}) error {
fv := reflect.ValueOf(ptr).Elem()
ft := reflect.TypeOf(ptr).Elem()
j := 0
for i := 0; i < fv.NumField(); i++ {
relSets, ok, err := getM2MRelSetByRelTag(ft.Field(i))
if err != nil {
return err
// } else if !ok {
} else if !ok || relSets[2] != "1" { //过滤非主端
continue
}
log.Info.Println(relSets)
relCName := GetRelCName(c, relSets)
log.Info.Println(relCName)
// 获取本端键值map
idVal1, err := GetFieldValByBsonTagId(fv)
if err != nil {
return err
}
log.Info.Println(idVal1)
relKey1 := c.Name + "_id"
relkey2 := relSets[1] + "_id"
j = i
relC := c.Database.C(relCName)
// 删除之前关系
_, err = relC.RemoveAll(v.M{relKey1: idVal1})
if err != nil {
return err
}
// 遍历对端对象
for i := 0; i < fv.Field(j).Len(); i++ {
IdVal2, err := GetFieldValByBsonTagId(fv.Field(j).Index(i))
if err != nil {
return err
}
// 添加关系
if err := relC.EnsureIndex(mgo.Index{Key: []string{relKey1, relkey2}, Unique: true}); err != nil {
return err
}
relId, err := GenIntId(relC)
if err != nil {
return err
}
rel := v.M{"_id": relId, relKey1: idVal1, relkey2: IdVal2}
if err := relC.Insert(rel); err != nil {
if strings.Contains(err.Error(), relKey1) {
continue
}
return err
}
}
}
return nil
}
// 获取_id标签对应的值
func GetFieldValByBsonTagId(fv reflect.Value) (int64, error) {
for i := 0; i < fv.NumField(); i++ {
if strings.Split(fv.Type().Field(i).Tag.Get("bson"), ",")[0] == "_id" {
return fv.Field(i).Int(), nil
}
}
return 0, errors.New("没有找到_id标签对应的值")
}
// 获取关系集合名
func GetRelCName(c *mgo.Collection, relSets []string) string {
if relSets[2] == "1" {
return c.Name + "_" + relSets[1]
}
return relSets[1] + "_" + c.Name
}
// 获取多对多关系配置
func getM2MRelSetByRelTag(sf reflect.StructField) ([]string, bool, error) {
rel1Tag := sf.Tag.Get("rel")
if rel1Tag == "" {
return nil, false, nil
}
if len(strings.Split(rel1Tag, ":")) < 2 {
return nil, false, errors.New("关系配置错误")
}
rel1Sets := strings.Split(strings.Split(rel1Tag, ":")[1], ".") //找到关系配置
if len(rel1Sets) < 3 {
return nil, false, errors.New("关系内容配置错误")
}
return rel1Sets, true, nil
}
// ptr只检查关系,不作为查询条件
// 只支持一个外键
func Del(c *mgo.Collection, ptr interface{}, query v.M) error {
ft := reflect.TypeOf(ptr).Elem()
fv := reflect.ValueOf(ptr).Elem()
for i := 0; i < ft.NumField(); i++ {
// 关系标签
relTag := ft.Field(i).Tag.Get("rel")
if relTag == "" {
continue
}
rel := strings.Split(relTag, ":")
if len(rel) < 2 {
return fmt.Errorf("关系标签格式错误,列:%s", "rel:\"o2m:b\"")
}
// 获取外键配置
switch fv.Field(i).Kind() {
case reflect.Slice:
vi := fv.Field(i)
vivElemt := vi.Type().Elem()
FEPtr := reflect.New(vivElemt)
fit := reflect.TypeOf(FEPtr.Interface()).Elem()
for i := 0; i < fit.NumField(); i++ {
fkTag := fit.Field(i).Tag.Get("fk")
if fkTag == "" {
if i == fit.NumField()-1 {
return fmt.Errorf("关系对象:%s没有设置外键", fit.Name())
}
continue
}
fk := strings.Split(fkTag, ".")
if len(fk) < 2 {
return errors.New("外键格式错误")
}
relBsonTag := strings.Split(fit.Field(i).Tag.Get("bson"), ",")[0]
if relBsonTag == "" {
return errors.New("设置外键所在的列没有设置bson标签")
}
var bsonTag string
for i := 0; i < ft.NumField(); i++ {
bsonTag = strings.Split(ft.Field(i).Tag.Get("bson"), ",")[0]
if bsonTag != fk[1] {
if i == ft.NumField()-1 {
return errors.New("外键所指向的列不存在")
}
continue
}
break //找到
}
result := []v.M{}
if err := c.Find(query).Select(v.M{bsonTag: 1}).All(&result); err != nil {
return err
}
relV := []interface{}{}
for _, m := range result {
relV = append(relV, m[bsonTag])
}
count, err := c.Database.C(rel[1]).Find(v.M{relBsonTag: v.M{"$in": relV}}).Count()
if err != nil {
return err
} else if count != 0 {
return fmt.Errorf("表:%s,有外键:%s,指向要删除的数据,列:%s,值:%#v", rel[1], relBsonTag, fk[1], relV)
}
break //只支持一个外键
}
default:
log.Error.Panicln("暂不支持的类型:", fv.Field(i).Kind())
}
}
err := c.Remove(query)
return err
}
// 插入对象前,为对象配置_id
func SetId(c *mgo.Collection, ptr interface{}) error {
ft := reflect.TypeOf(ptr).Elem()
fv := reflect.ValueOf(ptr).Elem()
for i := 0; i < ft.NumField(); i++ {
bfv := strings.Split(ft.Field(i).Tag.Get("bson"), ",")[0]
if bfv != "_id" {
if i == ft.NumField()-1 {
return errors.New("插入的对象没有指定主键bson:\"_id\"")
}
continue
}
if _, ok := fv.Field(i).Interface().(int64); !ok {
return errors.New("主键只支持int64类型")
}
if fv.Field(i).Int() == 0 {
id, err := GenIntId(c)
if err != nil {
return err
}
fv.Field(i).SetInt(id)
}
break
}
return nil
}
func GenIntId(c *mgo.Collection) (int64, error) {
cName := c.Name
IDInt64 := struct {
Value int64 `bson:"max_id"`
}{Value: 1}
_, err := c.Database.C("gen_id").Find(v.M{"_id": cName}).Apply(mgo.Change{Update: v.M{"$inc": IDInt64}, Upsert: true, ReturnNew: true}, &IDInt64)
return IDInt64.Value, err
}
// _id生成容错
func SetIntId4Max(c *mgo.Collection, maxId int64) error {
cName := c.Name
return c.Database.C("gen_id").Update(v.M{"_id": cName}, v.M{"max_id": maxId})
}
// 检查全部外键
func CheckFK(c *mgo.Collection, ptr interface{}) error {
ft := reflect.TypeOf(ptr).Elem()
fv := reflect.ValueOf(ptr).Elem()
for i := 0; i < ft.NumField(); i++ {
fk := ft.Field(i).Tag.Get("fk")
if fk == "" {
continue
}
fkvs := strings.Split(fk, ".")
if len(fkvs) < 2 {
return errors.New("外键配置错误,正确格式:fk:\"a,_id\",a->表名,_id->指向的列")
}
// 外键为空不检查
switch x := fv.Field(i).Interface().(type) {
case int64:
if x == 0 {
continue
}
case string:
if x == "" {
continue
}
default:
return errors.New("暂不支持的外键值类型")
}
// 检查外键是否存在
if count, err := c.Database.C(fkvs[0]).Find(v.M{fkvs[1]: fv.Field(i).Interface()}).Count(); err != nil {
return err
} else if count == 0 {
return fmt.Errorf("外键:%s,指向值:%d,不存在", ft.Field(i).Name, fv.Field(i).Int())
}
}
return nil
}
func GetIdVal(ptr interface{}) (int64, error) {
ft := reflect.TypeOf(ptr).Elem()
fv := reflect.ValueOf(ptr).Elem()
for i := 0; i < ft.NumField(); i++ {
bfv := strings.Split(ft.Field(i).Tag.Get("bson"), ",")[0]
if bfv != "_id" {
if i == ft.NumField()-1 {
goto THERE
}
continue
}
if _, ok := fv.Field(i).Interface().(int64); !ok {
return 0, errors.New("主键只支持int64类型")
}
return fv.Field(i).Int(), nil
}
THERE:
return 0, errors.New("插入的对象没有指定主键bson:\"_id\"")
}