diff --git a/document_internals.go b/document_internals.go index 7dc6500..4248a5c 100644 --- a/document_internals.go +++ b/document_internals.go @@ -199,6 +199,9 @@ func doSave(c *mongo.Collection, isNew bool, opts *SaveOptions, arg interface{}) } } else { //_, err = c.ReplaceOne(context.TO_DO(), bson.D{{Key: "_id", Value: d.(HasID).Id()}}, d.serializeToStore()) + _, im, _ := ModelRegistry.HasByName(asModel.getModel().getTypeName()) + _ = gridFsSave(asHasId, *im) + _, err = c.UpdateOne(context.TODO(), bson.D{{Key: "_id", Value: d.(HasID).Id()}}, bson.M{ "$set": d.serializeToStore(), }) diff --git a/gridfs.go b/gridfs.go index 93f2d65..e82d3c1 100644 --- a/gridfs.go +++ b/gridfs.go @@ -21,13 +21,12 @@ type GridFSFile struct { Length int `bson:"length"` } -func parseFmt(format string, value any) string { +func parseFmt(format string, value any) (string, error) { tmpl, err := template.New("filename").Parse(format) panik(err) w := new(strings.Builder) err = tmpl.Execute(w, value) - panik(err) - return w.String() + return w.String(), err } func bucket(gfsRef gridFSReference) *mongo.GridFSBucket { @@ -35,6 +34,32 @@ func bucket(gfsRef gridFSReference) *mongo.GridFSBucket { return b } +func hasTag(rtype reflect.Type, imodel Model) bool { + if rtype.Kind() == reflect.Pointer { + rtype = rtype.Elem() + } + if rtype.Kind() == reflect.Slice { + return hasTag(rtype.Elem(), imodel) + } else if rtype.Kind() == reflect.Struct { + for i := 0; i < rtype.NumField(); i++ { + f := rtype.Field(i) + tags, err := structtag.Parse(string(f.Tag)) + if err != nil { + return false + } + if _, err = tags.Get("gridfs"); err == nil { + return true + } + for kk := range imodel.gridFSReferences { + if strings.HasPrefix(kk, f.Name) { + return true + } + } + } + } + return false +} + func gridFsLoad(val any, g gridFSReference, field string) any { doc := reflect.ValueOf(val) rdoc := reflect.ValueOf(val) @@ -81,7 +106,11 @@ func gridFsLoad(val any, g gridFSReference, field string) any { default: b := bucket(g) var found GridFSFile - cursor, err := b.Find(context.TODO(), bson.M{"filename": parseFmt(g.FilenameFmt, val)}) + fname, err := parseFmt(g.FilenameFmt, val) + if err != nil { + return nil + } + cursor, err := b.Find(context.TODO(), bson.M{"filename": fname}) if err != nil { return nil } @@ -105,7 +134,7 @@ func gridFsLoad(val any, g gridFSReference, field string) any { return doc.Interface() } -func gridFsDel(val any, imodel Model) error { +func gridFsGen(val any, imodel Model, isSaving bool) error { var rerr error v := reflect.ValueOf(val) el := v @@ -130,17 +159,39 @@ func gridFsDel(val any, imodel Model) error { } } var inner = func(b *mongo.GridFSBucket, it reflect.Value) error { - filename := parseFmt(gfsRef.FilenameFmt, it.Interface()) + filename, err2 := parseFmt(gfsRef.FilenameFmt, it.Interface()) + if err2 != nil { + return err2 + } contents := GridFSFile{} curs, err2 := b.Find(context.TODO(), bson.M{"filename": filename}) if !errors.Is(err2, mongo.ErrNoDocuments) { - _ = curs.Decode(&contents) - if !reflect.ValueOf(contents).IsZero() { - return b.Delete(context.TODO(), contents.ID) + for { + if !curs.Next(context.TODO()) { + break + } + _ = curs.Decode(&contents) + if !reflect.ValueOf(contents).IsZero() { + b.Delete(context.TODO(), contents.ID) + } } + } - return nil + if isSaving { + c := it.Field(gfsRef.Idx) + var rdr io.Reader + + if c.Type().AssignableTo(reflect.TypeOf([]byte{})) { + rdr = bytes.NewReader(c.Interface().([]byte)) + } else if c.Type().AssignableTo(reflect.TypeOf("")) { + rdr = strings.NewReader(c.Interface().(string)) + } else { + return fmt.Errorf("gridfs loader type '%s' not supported", c.Type().String()) + } + _, err = b.UploadFromStream(context.TODO(), filename, rdr) + } + return err } if gfsRef != nil { b := bucket(*gfsRef) @@ -148,29 +199,31 @@ func gridFsDel(val any, imodel Model) error { for j := 0; j < fv.Len(); j++ { lerr := inner(b, fv.Index(j)) if lerr != nil { - return lerr + continue } } } else if fv.Kind() == reflect.Struct { lerr := inner(b, fv) if lerr != nil { - return lerr + continue } } else { lerr := inner(b, el) if lerr != nil { - return lerr + continue } } } - err = gridFsDel(fv.Interface(), imodel) - if err != nil { - return err + if hasTag(ft.Type, imodel) { + err = gridFsGen(fv.Interface(), imodel, isSaving) + if err != nil { + return err + } } } case reflect.Slice: for i := 0; i < el.Len(); i++ { - rerr = gridFsDel(el.Index(i).Interface(), imodel) + rerr = gridFsGen(el.Index(i).Interface(), imodel, isSaving) if rerr != nil { return rerr } @@ -181,91 +234,14 @@ func gridFsDel(val any, imodel Model) error { return rerr } +func gridFsDel(val any, imodel Model) error { + return gridFsGen(val, imodel, false) +} + func gridFsSave(val any, imodel Model) error { - var rerr error - v := reflect.ValueOf(val) - el := v - if v.Kind() == reflect.Pointer { - el = el.Elem() + t := reflect.TypeOf(val) + if hasTag(t, imodel) { + return gridFsGen(val, imodel, true) } - - switch el.Kind() { - case reflect.Struct: - for i := 0; i < el.NumField(); i++ { - ft := el.Type().Field(i) - fv := el.Field(i) - if !ft.IsExported() { - continue - } - _, err := structtag.Parse(string(ft.Tag)) - panik(err) - var gfsRef *gridFSReference - for kk, vv := range imodel.gridFSReferences { - if strings.HasPrefix(kk, ft.Name) { - gfsRef = &vv - break - } - } - var inner = func(b *mongo.GridFSBucket, it reflect.Value) error { - filename := parseFmt(gfsRef.FilenameFmt, it.Interface()) - contents := GridFSFile{} - curs, err2 := b.Find(context.TODO(), bson.M{"filename": filename}) - - if !errors.Is(err2, mongo.ErrNoDocuments) { - _ = curs.Decode(&contents) - if !reflect.ValueOf(contents).IsZero() { - _ = b.Delete(context.TODO(), contents.ID) - } - } - c := it.Field(gfsRef.Idx) - var rdr io.Reader - - if c.Type().AssignableTo(reflect.TypeOf([]byte{})) { - rdr = bytes.NewReader(c.Interface().([]byte)) - } else if c.Type().AssignableTo(reflect.TypeOf("")) { - rdr = strings.NewReader(c.Interface().(string)) - } else { - return fmt.Errorf("gridfs loader type '%s' not supported", c.Type().String()) - } - _, err = b.UploadFromStream(context.TODO(), filename, rdr) - return err - } - - if gfsRef != nil { - b := bucket(*gfsRef) - if fv.Kind() == reflect.Slice { - for j := 0; j < fv.Len(); j++ { - lerr := inner(b, fv.Index(j)) - if lerr != nil { - return lerr - } - } - } else if fv.Kind() == reflect.Struct { - lerr := inner(b, fv) - if lerr != nil { - return lerr - } - } else { - lerr := inner(b, el) - if lerr != nil { - return lerr - } - } - } - err = gridFsSave(fv.Interface(), imodel) - if err != nil { - return err - } - } - case reflect.Slice: - for i := 0; i < el.Len(); i++ { - rerr = gridFsSave(el.Index(i).Interface(), imodel) - if rerr != nil { - return rerr - } - } - default: - break - } - return rerr + return nil }