From fcd7cb20134b6536d505fde06101ac592fd00b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=98=99=E2=97=A6=20The=20Tablet=20=E2=9D=80=20GamerGirla?= =?UTF-8?q?ndCo=20=E2=97=A6=E2=9D=A7?= Date: Sat, 14 Sep 2024 02:09:38 -0400 Subject: [PATCH] =?UTF-8?q?overhaul=20models=20to=20be=20more=20sensible?= =?UTF-8?q?=20by=20separating=20"instance"=20logic=20into=20a=20separate?= =?UTF-8?q?=20Document=20type=20=E2=9C=A8=F0=9F=AA=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- document.go | 223 +++++++++++++++++ model_internals.go => document_internals.go | 28 +-- document_slice.go | 47 ++++ gridfs.go | 2 +- model.go | 250 +++----------------- model_test.go | 33 +-- query.go | 5 +- registry.go | 40 ++-- testing.go | 16 +- util.go | 44 ++++ 10 files changed, 403 insertions(+), 285 deletions(-) create mode 100644 document.go rename model_internals.go => document_internals.go (92%) create mode 100644 document_slice.go diff --git a/document.go b/document.go new file mode 100644 index 0000000..3c0e6e9 --- /dev/null +++ b/document.go @@ -0,0 +1,223 @@ +package orm + +import ( + "reflect" + "time" +) + +type Document struct { + // Created time. updated/added automatically. + Created time.Time `bson:"createdAt" json:"createdAt"` + // Modified time. updated/added automatically. + Modified time.Time `bson:"updatedAt" json:"updatedAt"` + model *Model + exists bool `bson:"-"` + self any `bson:"-"` +} +type IDocument interface { + Append(field string, a ...interface{}) error + Pull(field string, a ...any) error + Swap(field string, i, j int) error + Delete() error + Remove() error + Save() error + setSelf(arg interface{}) + getExists() bool + setExists(n bool) + setModified(Modified time.Time) + setCreated(Modified time.Time) + getModified() time.Time + getCreated() time.Time + serializeToStore() any + getModel() *Model + setModel(m Model) +} + +func (d *Document) getCreated() time.Time { + return d.Created +} + +func (d *Document) setCreated(Created time.Time) { + d.Created = Created +} + +func (d *Document) getModified() time.Time { + return d.Modified +} + +func (d *Document) setModified(Modified time.Time) { + d.Modified = Modified +} +func (d *Document) setSelf(arg interface{}) { + d.self = arg +} + +func (d *Document) getModel() *Model { + return d.model +} + +func (d *Document) setModel(m Model) { + d.model = &m +} + +func (d *Document) getExists() bool { + return d.exists +} +func (d *Document) setExists(n bool) { + d.exists = n +} + +// Delete - deletes a model instance from the database +func (d *Document) Delete() error { + var err error + val := valueOf(d.self) + if val.Kind() == reflect.Slice { + for i := 0; i < val.Len(); i++ { + cur := val.Index(i) + if err = doDelete(d, cur.Interface()); err != nil { + return err + } + } + return nil + } else { + return doDelete(d, d.self) + } +} + +// Remove - alias for Delete +func (d *Document) Remove() error { + return d.Delete() +} + +// Save - updates this Model in the database, +// or inserts it if it doesn't exist +func (d *Document) Save() error { + val := valueOf(d.self) + if val.Kind() == reflect.Slice { + for i := 0; i < val.Len(); i++ { + cur := val.Index(i) + if err := doSave(d.model.getColl(), !d.exists, cur.Interface()); err != nil { + return err + } + } + return nil + } else { + return doSave(d.model.getColl(), !d.exists, d.self) + } +} + +func (d *Document) serializeToStore() any { + return serializeIDs((d).self) +} + +// Append appends one or more items to `field`. +// will error if this Model contains a reference +// to multiple documents, or if `field` is not a +// slice. +func (d *Document) Append(field string, a ...interface{}) error { + var d0 IDocument = d + d0.getCreated() + rv := reflect.ValueOf(d.self) + selfRef := rv + rt := reflect.TypeOf(d.self) + if selfRef.Kind() == reflect.Pointer { + selfRef = selfRef.Elem() + rt = rt.Elem() + } + if err := checkStruct(selfRef); err != nil { + return err + } + _, origV, err := getNested(field, selfRef) + if err != nil { + return err + } + + origRef := makeSettable(*origV, (*origV).Interface()) + fv := origRef + if fv.Kind() == reflect.Pointer { + fv = fv.Elem() + } + if fv.Kind() != reflect.Slice { + return ErrNotASlice + } + for _, b := range a { + val := reflect.ValueOf(incrementTagged(b)) + fv.Set(reflect.Append(fv, val)) + } + return nil +} + +// Pull - removes elements from the subdocument slice stored in `field`. +func (d *Document) Pull(field string, a ...any) error { + rv := reflect.ValueOf(d.self) + selfRef := rv + rt := reflect.TypeOf(d.self) + if selfRef.Kind() == reflect.Pointer { + selfRef = selfRef.Elem() + rt = rt.Elem() + } + if err := checkStruct(selfRef); err != nil { + return err + } + _, origV, err := getNested(field, selfRef) + if err != nil { + return err + } + + origRef := makeSettable(*origV, (*origV).Interface()) + fv := origRef + if fv.Kind() == reflect.Pointer { + fv = fv.Elem() + } + if fv.Kind() != reflect.Slice { + return ErrNotASlice + } +outer: + for _, b := range a { + for i := 0; i < fv.Len(); i++ { + if reflect.DeepEqual(b, fv.Index(i).Interface()) { + fv.Set(pull(fv, i, fv.Index(i).Type())) + break outer + } + } + } + return nil +} + +// Swap - swaps the elements at indexes `i` and `j` in the +// slice stored at `field` +func (d *Document) Swap(field string, i, j int) error { + rv := reflect.ValueOf(d.self) + selfRef := rv + rt := reflect.TypeOf(d.self) + if selfRef.Kind() == reflect.Pointer { + selfRef = selfRef.Elem() + rt = rt.Elem() + } + if err := checkStruct(selfRef); err != nil { + return err + } + _, origV, err := getNested(field, selfRef) + if err != nil { + return err + } + + origRef := makeSettable(*origV, (*origV).Interface()) + fv := origRef + if fv.Kind() == reflect.Pointer { + fv = fv.Elem() + } + if err = checkSlice(fv); err != nil { + return err + } + if i >= fv.Len() || j >= fv.Len() { + return ErrOutOfBounds + } + oi := fv.Index(i).Interface() + oj := fv.Index(j).Interface() + + fv.Index(i).Set(reflect.ValueOf(oj)) + fv.Index(j).Set(reflect.ValueOf(oi)) + + return nil +} diff --git a/model_internals.go b/document_internals.go similarity index 92% rename from model_internals.go rename to document_internals.go index 2cca2f5..70568ec 100644 --- a/model_internals.go +++ b/document_internals.go @@ -125,25 +125,25 @@ func serializeIDs(input interface{}) interface{} { } func doSave(c *mongo.Collection, isNew bool, arg interface{}) error { var err error - m, ok := arg.(IModel) + d, ok := arg.(IDocument) if !ok { return fmt.Errorf(errFmtNotAModel, nameOf(arg)) } - m.setSelf(m) + d.setSelf(d) now := time.Now() - selfo := reflect.ValueOf(m) + 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().(IModel) + var asModel = vp.Interface().(IDocument) if isNew { - m.setCreated(now) + d.setCreated(now) } - m.setModified(now) - idxs := m.getIdxs() + d.setModified(now) + idxs := d.getModel().getIdxs() for _, i := range idxs { _, err = c.Indexes().CreateOne(context.TODO(), *i) if err != nil { @@ -157,28 +157,28 @@ func doSave(c *mongo.Collection, isNew bool, arg interface{}) error { (asHasId).SetId(pnid) } incrementAll(asHasId) - _, im, _ := ModelRegistry.HasByName(asModel.getTypeName()) + _, im, _ := ModelRegistry.HasByName(asModel.getModel().getTypeName()) _ = gridFsSave(asHasId, *im) - _, err = c.InsertOne(context.TODO(), m.serializeToStore()) + _, err = c.InsertOne(context.TODO(), d.serializeToStore()) if err == nil { - m.setExists(true) + d.setExists(true) } } else { - _, err = c.ReplaceOne(context.TODO(), bson.D{{Key: "_id", Value: m.(HasID).Id()}}, m.serializeToStore()) + _, err = c.ReplaceOne(context.TODO(), bson.D{{Key: "_id", Value: d.(HasID).Id()}}, d.serializeToStore()) } return err } -func doDelete(m *Model, arg interface{}) error { +func doDelete(d *Document, arg interface{}) error { self, ok := arg.(HasID) if !ok { return fmt.Errorf(errFmtNotHasID, nameOf(arg)) } - c := m.getColl() + c := d.model.getColl() _, err := c.DeleteOne(context.TODO(), bson.M{"_id": self.Id()}) if err == nil { - m.exists = false + d.exists = false } return err } diff --git a/document_slice.go b/document_slice.go new file mode 100644 index 0000000..3065949 --- /dev/null +++ b/document_slice.go @@ -0,0 +1,47 @@ +package orm + +type IDocumentSlice interface { + Delete() error + Remove() error + Save() error + setExists(n bool) + getModel() *Model +} + +type DocumentSlice []IDocument + +func (d *DocumentSlice) Delete() error { + var err error + for _, doc := range *d { + err = doc.Delete() + if err != nil { + return err + } + } + return err +} + +func (d *DocumentSlice) Remove() error { + return d.Delete() +} + +func (d *DocumentSlice) Save() error { + var err error + for _, doc := range *d { + err = doc.Save() + if err != nil { + return err + } + } + return err +} + +func (d *DocumentSlice) setExists(b bool) { + for _, s := range *d { + s.setExists(b) + } +} + +func (d *DocumentSlice) getModel() *Model { + return (*d)[0].getModel() +} diff --git a/gridfs.go b/gridfs.go index ee41ecf..df14010 100644 --- a/gridfs.go +++ b/gridfs.go @@ -107,7 +107,7 @@ func gridFsLoad(val any, g GridFSReference, field string) any { return doc.Interface() } -func gridFsSave(val any, imodel InternalModel) error { +func gridFsSave(val any, imodel Model) error { var rerr error v := reflect.ValueOf(val) el := v diff --git a/model.go b/model.go index e8e4a0a..0616b45 100644 --- a/model.go +++ b/model.go @@ -3,39 +3,22 @@ package orm import ( "context" "fmt" - "reflect" - "time" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + "reflect" + "unsafe" ) // Model - "base" struct for all queryable models type Model struct { - // Created time. updated/added automatically. - Created time.Time `bson:"createdAt" json:"createdAt"` - // Modified time. updated/added automatically. - Modified time.Time `bson:"updatedAt" json:"updatedAt"` - typeName string `bson:"-"` - self any `bson:"-"` - exists bool `bson:"-"` -} - -func (m *Model) getCreated() time.Time { - return m.Created -} - -func (m *Model) setCreated(Created time.Time) { - m.Created = Created -} - -func (m *Model) getModified() time.Time { - return m.Modified -} - -func (m *Model) setModified(Modified time.Time) { - m.Modified = Modified + typeName string `bson:"-"` + Idx int + Type reflect.Type + Collection string + References map[string]Reference + Indexes map[string][]InternalIndex + GridFSReferences map[string]GridFSReference } // HasID is a simple interface that you must implement @@ -52,29 +35,17 @@ type HasID interface { type HasIDSlice []HasID type IModel interface { - Append(field string, a ...interface{}) error - Delete() error FindRaw(query interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) Find(query interface{}, opts ...*options.FindOptions) (*Query, error) FindByID(id interface{}) (*Query, error) FindOne(query interface{}, options ...*options.FindOneOptions) (*Query, error) FindPaged(query interface{}, page int64, perPage int64, options ...*options.FindOptions) (*Query, error) - Pull(field string, a ...any) error - Remove() error - Save() error + getColl() *mongo.Collection getIdxs() []*mongo.IndexModel getParsedIdxs() map[string][]InternalIndex - serializeToStore() any getTypeName() string setTypeName(str string) - getExists() bool - setExists(n bool) - setModified(Modified time.Time) - setCreated(Modified time.Time) - getModified() time.Time - getCreated() time.Time - setSelf(arg interface{}) } func (m *Model) getTypeName() string { @@ -85,17 +56,6 @@ func (m *Model) setTypeName(str string) { m.typeName = str } -func (m *Model) setSelf(arg interface{}) { - m.self = arg -} - -func (m *Model) getExists() bool { - return m.exists -} -func (m *Model) setExists(n bool) { - m.exists = n -} - func (m *Model) getColl() *mongo.Collection { _, ri, ok := ModelRegistry.HasByName(m.typeName) if !ok { @@ -146,12 +106,12 @@ func (m *Model) Find(query interface{}, opts ...*options.FindOptions) (*Query, e op: OP_FIND_ALL, } q, err := m.FindRaw(query, opts...) - + idoc := (*DocumentSlice)(unsafe.Pointer(qqv.Elem().UnsafeAddr())) if err == nil { rawRes := bson.A{} err = q.All(context.TODO(), &rawRes) if err == nil { - m.exists = true + idoc.setExists(true) } qq.rawDoc = rawRes err = q.All(context.TODO(), &qq.doc) @@ -195,14 +155,16 @@ func (m *Model) FindOne(query interface{}, options ...*options.FindOneOptions) ( raw := bson.M{} err := rip.Decode(&raw) panik(err) - m.exists = true qqn := ModelRegistry.new_(m.typeName) - v := reflect.New(reflect.TypeOf(qqn)) - v.Elem().Set(reflect.ValueOf(qqn)) + idoc, ok := qqn.(IDocument) + if ok { + idoc.setExists(true) + } + qq := &Query{ collection: m.getColl(), rawDoc: raw, - doc: v.Elem().Interface(), + doc: idoc, op: OP_FIND_ONE, model: m, } @@ -212,166 +174,13 @@ func (m *Model) FindOne(query interface{}, options ...*options.FindOneOptions) ( qq.reOrganize() err = nil } - m.self = qq.doc + idoc.setSelf(idoc) return qq, err } -// Delete - deletes a model instance from the database -func (m *Model) Delete() error { - var err error - val := valueOf(m.self) - if val.Kind() == reflect.Slice { - for i := 0; i < val.Len(); i++ { - cur := val.Index(i) - if err = doDelete(m, cur.Interface()); err != nil { - return err - } - } - return nil - } else { - return doDelete(m, m.self) - } -} - -// Remove - alias for Delete -func (m *Model) Remove() error { - return m.Delete() -} - -// Append appends one or more items to `field`. -// will error if this Model contains a reference -// to multiple documents, or if `field` is not a -// slice. -func (m *Model) Append(field string, a ...interface{}) error { - rv := reflect.ValueOf(m.self) - selfRef := rv - rt := reflect.TypeOf(m.self) - if selfRef.Kind() == reflect.Pointer { - selfRef = selfRef.Elem() - rt = rt.Elem() - } - if err := checkStruct(selfRef); err != nil { - return err - } - _, origV, err := getNested(field, selfRef) - if err != nil { - return err - } - - origRef := makeSettable(*origV, (*origV).Interface()) - fv := origRef - if fv.Kind() == reflect.Pointer { - fv = fv.Elem() - } - if fv.Kind() != reflect.Slice { - return ErrNotASlice - } - for _, b := range a { - val := reflect.ValueOf(incrementTagged(b)) - fv.Set(reflect.Append(fv, val)) - } - return nil -} - -// Pull - removes elements from the subdocument slice stored in `field`. -func (m *Model) Pull(field string, a ...any) error { - rv := reflect.ValueOf(m.self) - selfRef := rv - rt := reflect.TypeOf(m.self) - if selfRef.Kind() == reflect.Pointer { - selfRef = selfRef.Elem() - rt = rt.Elem() - } - if err := checkStruct(selfRef); err != nil { - return err - } - _, origV, err := getNested(field, selfRef) - if err != nil { - return err - } - - origRef := makeSettable(*origV, (*origV).Interface()) - fv := origRef - if fv.Kind() == reflect.Pointer { - fv = fv.Elem() - } - if fv.Kind() != reflect.Slice { - return ErrNotASlice - } -outer: - for _, b := range a { - for i := 0; i < fv.Len(); i++ { - if reflect.DeepEqual(b, fv.Index(i).Interface()) { - fv.Set(pull(fv, i, fv.Index(i).Type())) - break outer - } - } - } - return nil -} - -// Swap - swaps the elements at indexes `i` and `j` in the -// slice stored at `field` -func (m *Model) Swap(field string, i, j int) error { - rv := reflect.ValueOf(m.self) - selfRef := rv - rt := reflect.TypeOf(m.self) - if selfRef.Kind() == reflect.Pointer { - selfRef = selfRef.Elem() - rt = rt.Elem() - } - if err := checkStruct(selfRef); err != nil { - return err - } - _, origV, err := getNested(field, selfRef) - if err != nil { - return err - } - - origRef := makeSettable(*origV, (*origV).Interface()) - fv := origRef - if fv.Kind() == reflect.Pointer { - fv = fv.Elem() - } - if err = checkSlice(fv); err != nil { - return err - } - if i >= fv.Len() || j >= fv.Len() { - return ErrOutOfBounds - } - oi := fv.Index(i).Interface() - oj := fv.Index(j).Interface() - - fv.Index(i).Set(reflect.ValueOf(oj)) - fv.Index(j).Set(reflect.ValueOf(oi)) - - return nil -} - -// Save - updates this Model in the database, -// or inserts it if it doesn't exist -func (m *Model) Save() error { - val := valueOf(m.self) - if val.Kind() == reflect.Slice { - for i := 0; i < val.Len(); i++ { - cur := val.Index(i) - if err := doSave(m.getColl(), !m.exists, cur.Interface()); err != nil { - return err - } - } - return nil - } else { - return doSave(m.getColl(), !m.exists, m.self) - } -} - -func (m *Model) serializeToStore() any { - return serializeIDs((m).self) -} - func createBase(d any) (reflect.Value, int, string) { var n string - var ri *InternalModel + var ri *Model var ok bool n, ri, ok = ModelRegistry.HasByName(nameOf(d)) @@ -393,20 +202,23 @@ func createBase(d any) (reflect.Value, int, string) { } else { r.Elem().Set(reflect.ValueOf(d)) } + ri.setTypeName(n) + r.Interface().(IDocument).setModel(*ri) return r, i, n } -// Create creates a new instance of a given model -// and returns a pointer to it. +// Create creates a new instance of a given Document +// type and returns a pointer to it. func Create(d any) any { - r, i, n := createBase(d) - df := r.Elem().Field(i) - dm := df.Interface().(Model) - dm.typeName = n + r, _, n := createBase(d) + //df := r.Elem().Field(i) + dm := r.Interface().(IDocument) + dm.getModel().setTypeName(n) what := r.Interface() - dm.self = what - df.Set(reflect.ValueOf(dm)) + + dm.setSelf(what) + //df.Set(reflect.ValueOf(dm)) return what } diff --git a/model_test.go b/model_test.go index c5d179d..7dfa05c 100644 --- a/model_test.go +++ b/model_test.go @@ -11,9 +11,10 @@ import ( func TestNew(t *testing.T) { initTest() - doc := Create(iti_single()).(*story) - assert.Equal(t, iti_single().Title, doc.Title) - assert.Equal(t, iti_single().Chapters[0].Summary, doc.Chapters[0].Summary) + is := iti_single() + doc := Create(is).(*story) + assert.Equal(t, is.Title, doc.Title) + assert.Equal(t, is.Chapters[0].Summary, doc.Chapters[0].Summary) } func TestSave(t *testing.T) { @@ -46,7 +47,7 @@ func TestPopulate(t *testing.T) { saveDoc(t, storyDoc) assert.Greater(t, storyDoc.ID, int64(0)) - smodel := Create(story{}).(*story) + smodel := ModelRegistry["story"] q, err := smodel.FindByID(storyDoc.ID) assert.Equal(t, nil, err) assert.NotPanics(t, func() { @@ -67,7 +68,7 @@ func TestUpdate(t *testing.T) { nb.Locked = true saveDoc(t, nb) - foundM := Create(band{}).(*band) + foundM := ModelRegistry["band"] q, err := foundM.FindByID(int64(1)) assert.Equal(t, nil, err) found := &band{} @@ -81,7 +82,7 @@ func TestModel_FindAll(t *testing.T) { initTest() im := iti_multi() createAndSave(t, &im) - smodel := Create(story{}).(*story) + smodel := ModelRegistry["story"] query, err := smodel.Find(bson.M{}, options.Find()) assert.Equal(t, nil, err) final := CreateSlice(story{}) @@ -98,7 +99,7 @@ func TestModel_PopulateMulti(t *testing.T) { im := iti_multi() im.Author = mauthor createAndSave(t, &im) - smodel := Create(story{}).(*story) + smodel := ModelRegistry["story"] query, err := smodel.Find(bson.M{}, options.Find()) assert.Equal(t, nil, err) final := CreateSlice(story{}) @@ -118,7 +119,7 @@ func TestModel_PopulateChained_Multi(t *testing.T) { saveDoc(t, mauthor) im.Author = mauthor createAndSave(t, &im) - smodel := Create(story{}).(*story) + smodel := ModelRegistry["story"] query, err := smodel.Find(bson.M{}, options.Find()) assert.Equal(t, nil, err) final := CreateSlice(story{}) @@ -141,7 +142,7 @@ func TestPopulate_Chained(t *testing.T) { saveDoc(t, storyDoc) assert.Greater(t, storyDoc.ID, int64(0)) - smodel := Create(story{}).(*story) + smodel := ModelRegistry["story"] q, err := smodel.FindByID(storyDoc.ID) assert.Equal(t, nil, err) assert.NotPanics(t, func() { @@ -159,15 +160,15 @@ func TestModel_Append(t *testing.T) { initTest() bandDoc := Create(metallica).(*band) saveDoc(t, bandDoc) - bmodel := Create(band{}).(*band) + bmodel := ModelRegistry["band"] query, err := bmodel.FindByID(int64(1)) assert.Equal(t, nil, err) fin := &band{} query.Exec(fin) assert.Greater(t, fin.ID, int64(0)) - err = bmodel.Append("Characters", "Robert Trujillo") + err = fin.Append("Characters", "Robert Trujillo") assert.Equal(t, nil, err) - saveDoc(t, bmodel) + saveDoc(t, fin) fin = &band{} query, _ = bmodel.FindByID(int64(1)) query.Exec(fin) @@ -185,7 +186,7 @@ func TestModel_Delete(t *testing.T) { func TestModel_Pull(t *testing.T) { initTest() storyDoc := Create(iti_multi()).(*story) - smodel := Create(story{}).(*story) + smodel := ModelRegistry["story"] saveDoc(t, storyDoc) err := storyDoc.Pull("Chapters", storyDoc.Chapters[4]) assert.Nil(t, err) @@ -217,7 +218,7 @@ func TestModel_Swap(t *testing.T) { func TestModel_GridFSLoad(t *testing.T) { initTest() ModelRegistry.Model(somethingWithNestedChapters{}) - model := Create(somethingWithNestedChapters{}).(*somethingWithNestedChapters) + model := ModelRegistry["somethingWithNestedChapters"] thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters) found := &somethingWithNestedChapters{} @@ -236,7 +237,7 @@ func TestModel_GridFSLoad(t *testing.T) { func TestModel_GridFSLoad_Chained(t *testing.T) { initTest() ModelRegistry.Model(somethingWithNestedChapters{}) - model := Create(somethingWithNestedChapters{}).(*somethingWithNestedChapters) + model := ModelRegistry["somethingWithNestedChapters"] thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters) found := &somethingWithNestedChapters{} @@ -254,7 +255,7 @@ func TestModel_GridFSLoad_Chained(t *testing.T) { func TestModel_GridFSLoad_Complex(t *testing.T) { initTest() - model := Create(story{}).(*story) + model := ModelRegistry["story"] bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band) thingDoc := Create(iti_multi()).(*story) mauthor := Create(author).(*user) diff --git a/query.go b/query.go index 052f6b1..7772da4 100644 --- a/query.go +++ b/query.go @@ -156,7 +156,7 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter } rawt := ModelRegistry.new_(tto.Name()) - t := rawt.(IModel) + t := rawt.(IDocument) q := bson.M{"_id": rawDoc} reso := DB.Collection(rcoll).FindOne(context.TODO(), q) reso.Decode(t) @@ -516,7 +516,7 @@ func (q *Query) Exec(result interface{}) { if doc.Elem().Kind() == reflect.Slice { for i := 0; i < doc.Elem().Len(); i++ { cur := doc.Elem().Index(i) - imodel, ok := cur.Interface().(IModel) + imodel, ok := cur.Interface().(IDocument) if ok { imodel.setExists(true) doc.Elem().Index(i).Set(reflect.ValueOf(imodel)) @@ -524,6 +524,5 @@ func (q *Query) Exec(result interface{}) { } } reflect.ValueOf(result).Elem().Set(reflect.ValueOf(q.doc).Elem()) - q.model.self = q.doc q.done = true } diff --git a/registry.go b/registry.go index 56edb18..73bdf47 100644 --- a/registry.go +++ b/registry.go @@ -16,17 +16,6 @@ import ( "golang.org/x/net/context" ) -// InternalModel, as the name suggests, is used -// internally by the model registry -type InternalModel struct { - Idx int - Type reflect.Type - Collection string - References map[string]Reference - Indexes map[string][]InternalIndex - GridFSReferences map[string]GridFSReference -} - // Reference stores a typed document reference type Reference struct { // owning model name @@ -51,10 +40,10 @@ type GridFSReference struct { Idx int } -type TModelRegistry map[string]*InternalModel +type TModelRegistry map[string]*Model // ModelRegistry - the ModelRegistry stores a map containing -// pointers to InternalModel instances, keyed by an associated +// pointers to Model instances, keyed by an associated // model name var ModelRegistry = make(TModelRegistry, 0) @@ -182,7 +171,7 @@ func parseTags(t reflect.Type, v reflect.Value) (map[string][]InternalIndex, map ft = ft.Elem() fallthrough case reflect.Struct: - if ft.ConvertibleTo(reflect.TypeOf(Model{})) { + if ft.ConvertibleTo(reflect.TypeOf(Document{})) { collTag, err := tags.Get("coll") panik(err) coll = collTag.Name @@ -223,10 +212,10 @@ func parseTags(t reflect.Type, v reflect.Value) (map[string][]InternalIndex, map return idcs, refs, gfsRefs, coll } -// Has returns the model typename and InternalModel instance corresponding +// Has returns the model typename and Model instance corresponding // to the argument passed, as well as a boolean indicating whether it // was found. otherwise returns `"", nil, false` -func (r TModelRegistry) Has(i interface{}) (string, *InternalModel, bool) { +func (r TModelRegistry) Has(i interface{}) (string, *Model, bool) { t := reflect.TypeOf(i) if t.Kind() == reflect.Ptr { t = t.Elem() @@ -240,14 +229,14 @@ func (r TModelRegistry) Has(i interface{}) (string, *InternalModel, bool) { // HasByName functions almost identically to Has, // except that it takes a string as its argument. -func (r TModelRegistry) HasByName(n string) (string, *InternalModel, bool) { +func (r TModelRegistry) HasByName(n string) (string, *Model, bool) { if t, ok := ModelRegistry[n]; ok { return n, t, true } return "", nil, false } -// Index returns the index at which the Model struct is embedded +// Index returns the index at which the Document struct is embedded func (r TModelRegistry) Index(n string) int { if v, ok := ModelRegistry[n]; ok { return v.Idx @@ -259,9 +248,12 @@ func (r TModelRegistry) new_(n string) interface{} { if name, m, ok := ModelRegistry.HasByName(n); ok { v := reflect.New(m.Type) df := v.Elem().Field(m.Idx) - d := df.Interface().(Model) - d.typeName = name - df.Set(reflect.ValueOf(d)) + do := reflect.New(df.Type()) + d := do.Interface().(IDocument) + //d := df.Interface().(IDocument) + d.setModel(*m) + d.getModel().typeName = name + df.Set(reflect.ValueOf(d).Elem()) return v.Interface() } return nil @@ -303,19 +295,19 @@ func (r TModelRegistry) Model(mdl ...any) { idx := -1 for i := 0; i < v.NumField(); i++ { ft := t.Field(i) - if (ft.Type.ConvertibleTo(reflect.TypeOf(Model{}))) { + if (ft.Type.ConvertibleTo(reflect.TypeOf(Document{}))) { idx = i break } } if idx < 0 { - panic("A model must embed the Model struct!") + panic("A model must embed the Document struct!") } inds, refs, gfs, coll := parseTags(t, v) if coll == "" { panic(fmt.Sprintf("a model needs to be given a collection name! (passed type: %s)", n)) } - ModelRegistry[n] = &InternalModel{ + ModelRegistry[n] = &Model{ Idx: idx, Type: t, Collection: coll, diff --git a/testing.go b/testing.go index 2569e5a..e294110 100644 --- a/testing.go +++ b/testing.go @@ -37,19 +37,19 @@ type chapter struct { type band struct { ID int64 `bson:"_id" json:"_id"` - Model `bson:",inline" json:",inline" coll:"bands"` + Document `bson:",inline" json:",inline" coll:"bands"` Name string `bson:"name" json:"name" form:"name"` Locked bool `bson:"locked" json:"locked" form:"locked"` Characters []string `bson:"characters" json:"characters" form:"characters"` } type user struct { ID int64 `bson:"_id" json:"_id"` - Model `bson:",inline" json:",inline" coll:"users"` + Document `bson:",inline" json:",inline" coll:"users"` Username string `bson:"username" json:"username"` } type story struct { ID int64 `bson:"_id" json:"_id"` - Model `bson:",inline" json:",inline" coll:"stories"` + Document `bson:",inline" json:",inline" coll:"stories"` Title string `bson:"title" json:"title" form:"title"` Author *user `bson:"author" json:"author" ref:"user"` CoAuthor *user `bson:"coAuthor" json:"coAuthor" ref:"user"` @@ -62,7 +62,7 @@ type story struct { } type somethingWithNestedChapters struct { ID int64 `bson:"_id" json:"_id"` - Model `bson:",inline" json:",inline" coll:"nested_stuff"` + Document `bson:",inline" json:",inline" coll:"nested_stuff"` Chapters []chapter `bson:"chapters" json:"chapters"` NestedText string `json:"text" bson:"-" gridfs:"nested_text,/nested/{{.ID}}.txt"` } @@ -88,7 +88,7 @@ func (s *user) Id() any { func (s *story) SetId(id any) { s.ID = id.(int64) - //var t IModel = s + //var t IDocument =s } func (s *band) SetId(id any) { @@ -245,12 +245,12 @@ var bodom = band{ }, } -func saveDoc(t *testing.T, doc IModel) { +func saveDoc(t *testing.T, doc IDocument) { err := doc.Save() assert.Nil(t, err) } -func createAndSave(t *testing.T, doc IModel) { - mdl := Create(doc).(IModel) +func createAndSave(t *testing.T, doc IDocument) { + mdl := Create(doc).(IDocument) saveDoc(t, mdl) } diff --git a/util.go b/util.go index 552585e..b715b5f 100644 --- a/util.go +++ b/util.go @@ -167,3 +167,47 @@ func checkSlice(ref reflect.Value) error { } return nil } + +func convertSlice[In, Out any](in []In) []Out { + out := make([]Out, 0) + for _, i := range in { + ii, ok := any(i).(Out) + if ok { + out = append(out, ii) + } + } + return out +} + +func normalizeSliceToDocumentSlice(in any) *DocumentSlice { + ret := make(DocumentSlice, 0) + val := reflect.ValueOf(in) + if val.Kind() == reflect.Pointer { + val = val.Elem() + } + if val.Kind() == reflect.Slice { + for i := 0; i < val.Len(); i++ { + if idoc, ok := val.Index(i).Interface().(IDocument); ok { + ret = append(ret, idoc) + } + } + } + return &ret +} + +/*func normalizeDocSlice(iput reflect.Value) reflect.Value { + idslice, idsliceOk := iput.Interface().(IDocumentSlice) + dslice, dsliceOk := resV.Interface().(DocumentSlice) + switch { + case idsliceOk: + + case dsliceOk: + default: + return iput + } + //if { + // resV.Set(trvo.Elem()) + //} else { + // + //} +}*/