246 lines
5.7 KiB
Go
246 lines
5.7 KiB
Go
package orm
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/fatih/structtag"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
func serializeIDs(input interface{}) interface{} {
|
|
|
|
vp := reflect.ValueOf(input)
|
|
mt := reflect.TypeOf(input)
|
|
var ret interface{}
|
|
if vp.Kind() != reflect.Ptr {
|
|
if vp.CanAddr() {
|
|
vp = vp.Addr()
|
|
} else {
|
|
vp = makeSettable(vp, input)
|
|
}
|
|
}
|
|
|
|
if mt.Kind() == reflect.Pointer {
|
|
mt = mt.Elem()
|
|
}
|
|
getID := func(bbb interface{}) interface{} {
|
|
mptr := reflect.ValueOf(bbb)
|
|
if mptr.Kind() != reflect.Pointer {
|
|
mptr = makeSettable(mptr, bbb)
|
|
}
|
|
ifc, ok := mptr.Interface().(HasID)
|
|
if ok {
|
|
return ifc.Id()
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
switch vp.Elem().Kind() {
|
|
case reflect.Struct:
|
|
ret0 := bson.M{}
|
|
for i := 0; i < vp.Elem().NumField(); i++ {
|
|
fv := vp.Elem().Field(i)
|
|
ft := mt.Field(i)
|
|
tag, err := structtag.Parse(string(ft.Tag))
|
|
panik(err)
|
|
bbson, err := tag.Get("bson")
|
|
if err != nil || bbson.Name == "-" {
|
|
continue
|
|
}
|
|
if bbson.Name == "" {
|
|
marsh, _ := bson.Marshal(fv.Interface())
|
|
unmarsh := bson.M{}
|
|
bson.Unmarshal(marsh, &unmarsh)
|
|
for k, v := range unmarsh {
|
|
ret0[k] = v
|
|
}
|
|
} else {
|
|
_, terr := tag.Get("ref")
|
|
if reflect.ValueOf(fv.Interface()).Type().Kind() != reflect.Pointer {
|
|
vp1 := reflect.New(fv.Type())
|
|
vp1.Elem().Set(reflect.ValueOf(fv.Interface()))
|
|
fv.Set(vp1.Elem())
|
|
}
|
|
if terr == nil {
|
|
ifc, ok := fv.Interface().(HasID)
|
|
if fv.Kind() == reflect.Slice {
|
|
rarr := bson.A{}
|
|
for j := 0; j < fv.Len(); j++ {
|
|
rarr = append(rarr, getID(fv.Index(j).Interface()))
|
|
}
|
|
ret0[bbson.Name] = rarr
|
|
} else if !ok {
|
|
panic(fmt.Sprintf("referenced model slice at '%s.%s' does not implement HasID", nameOf(input), ft.Name))
|
|
} else {
|
|
if reflect.ValueOf(ifc).IsNil() {
|
|
ret0[bbson.Name] = nil
|
|
} else {
|
|
ret0[bbson.Name] = ifc.Id()
|
|
}
|
|
}
|
|
|
|
} else {
|
|
ret0[bbson.Name] = serializeIDs(fv.Interface())
|
|
}
|
|
}
|
|
|
|
ret = ret0
|
|
}
|
|
case reflect.Slice:
|
|
ret0 := bson.A{}
|
|
for i := 0; i < vp.Elem().Len(); i++ {
|
|
|
|
ret0 = append(ret0, serializeIDs(vp.Elem().Index(i).Addr().Interface()))
|
|
}
|
|
ret = ret0
|
|
default:
|
|
ret = vp.Elem().Interface()
|
|
}
|
|
return ret
|
|
}
|
|
func doSave(c *mongo.Collection, isNew bool, arg interface{}) error {
|
|
var err error
|
|
d, ok := arg.(IDocument)
|
|
if !ok {
|
|
return fmt.Errorf(errFmtNotAModel, nameOf(arg))
|
|
}
|
|
d.setSelf(d)
|
|
now := time.Now()
|
|
selfo := reflect.ValueOf(d)
|
|
vp := selfo
|
|
if vp.Kind() != reflect.Ptr {
|
|
vp = reflect.New(selfo.Type())
|
|
vp.Elem().Set(selfo)
|
|
}
|
|
var asHasId = vp.Interface().(HasID)
|
|
var asModel = vp.Interface().(IDocument)
|
|
if isNew {
|
|
d.setCreated(now)
|
|
}
|
|
d.setModified(now)
|
|
idxs := d.getModel().getIdxs()
|
|
for _, i := range idxs {
|
|
_, err = c.Indexes().CreateOne(context.TODO(), *i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if isNew {
|
|
nid := getLastInColl(c.Name(), asHasId.Id())
|
|
pnid := incrementInterface(nid)
|
|
if reflect.ValueOf(asHasId.Id()).IsZero() {
|
|
(asHasId).SetId(pnid)
|
|
}
|
|
incrementAll(asHasId)
|
|
_, im, _ := ModelRegistry.HasByName(asModel.getModel().getTypeName())
|
|
_ = gridFsSave(asHasId, *im)
|
|
|
|
_, err = c.InsertOne(context.TODO(), d.serializeToStore())
|
|
if err == nil {
|
|
d.setExists(true)
|
|
}
|
|
} else {
|
|
_, err = c.ReplaceOne(context.TODO(), bson.D{{Key: "_id", Value: d.(HasID).Id()}}, d.serializeToStore())
|
|
}
|
|
return err
|
|
}
|
|
func doDelete(d *Document, arg interface{}) error {
|
|
self, ok := arg.(HasID)
|
|
|
|
if !ok {
|
|
return fmt.Errorf(errFmtNotHasID, nameOf(arg))
|
|
}
|
|
c := d.model.getColl()
|
|
_, err := c.DeleteOne(context.TODO(), bson.M{"_id": self.Id()})
|
|
if err == nil {
|
|
d.exists = false
|
|
}
|
|
return err
|
|
}
|
|
func incrementTagged(item interface{}) interface{} {
|
|
rv := reflect.ValueOf(item)
|
|
rt := reflect.TypeOf(item)
|
|
if rv.Kind() != reflect.Pointer {
|
|
rv = makeSettable(rv, item)
|
|
}
|
|
if rt.Kind() == reflect.Pointer {
|
|
rt = rt.Elem()
|
|
}
|
|
if rt.Kind() != reflect.Struct {
|
|
if rt.Kind() == reflect.Slice {
|
|
for i := 0; i < rv.Elem().Len(); i++ {
|
|
incrementTagged(rv.Elem().Index(i).Addr().Interface())
|
|
}
|
|
} else {
|
|
return item
|
|
}
|
|
}
|
|
for i := 0; i < rt.NumField(); i++ {
|
|
structField := rt.Field(i)
|
|
cur := rv.Elem().Field(i)
|
|
tags, err := structtag.Parse(string(structField.Tag))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
incTag, err := tags.Get("autoinc")
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
nid := getLastInColl(incTag.Name, cur.Interface())
|
|
if cur.IsZero() {
|
|
coerced := coerceInt(reflect.ValueOf(incrementInterface(nid)), cur)
|
|
if coerced != nil {
|
|
cur.Set(reflect.ValueOf(coerced))
|
|
} else {
|
|
cur.Set(reflect.ValueOf(incrementInterface(nid)))
|
|
}
|
|
}
|
|
counterColl := DB.Collection(COUNTER_COL)
|
|
counterColl.UpdateOne(context.TODO(), bson.M{"collection": incTag.Name}, bson.M{"$set": bson.M{"collection": incTag.Name, "current": cur.Interface()}}, options.Update().SetUpsert(true))
|
|
|
|
}
|
|
return rv.Elem().Interface()
|
|
}
|
|
|
|
func incrementAll(item interface{}) {
|
|
if item == nil {
|
|
return
|
|
}
|
|
vp := reflect.ValueOf(item)
|
|
el := vp
|
|
if vp.Kind() == reflect.Pointer {
|
|
el = vp.Elem()
|
|
}
|
|
if vp.Kind() == reflect.Pointer && vp.IsNil() {
|
|
return
|
|
}
|
|
vt := el.Type()
|
|
switch el.Kind() {
|
|
case reflect.Struct:
|
|
incrementTagged(item)
|
|
for i := 0; i < el.NumField(); i++ {
|
|
fv := el.Field(i)
|
|
fst := vt.Field(i)
|
|
if !fst.IsExported() {
|
|
continue
|
|
}
|
|
incrementAll(fv.Interface())
|
|
}
|
|
case reflect.Slice:
|
|
for i := 0; i < el.Len(); i++ {
|
|
incd := incrementTagged(el.Index(i).Addr().Interface())
|
|
if reflect.ValueOf(incd).Kind() == reflect.Pointer {
|
|
el.Index(i).Set(reflect.ValueOf(incd).Elem())
|
|
} else {
|
|
el.Index(i).Set(reflect.ValueOf(incd))
|
|
}
|
|
}
|
|
default:
|
|
}
|
|
}
|