overhaul models to be more sensible by separating "instance" logic into a separate Document type ✨🪷
This commit is contained in:
		
							parent
							
								
									bcf3985360
								
							
						
					
					
						commit
						fcd7cb2013
					
				
							
								
								
									
										223
									
								
								document.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								document.go
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  | } | ||||||
| @ -125,25 +125,25 @@ func serializeIDs(input interface{}) interface{} { | |||||||
| } | } | ||||||
| func doSave(c *mongo.Collection, isNew bool, arg interface{}) error { | func doSave(c *mongo.Collection, isNew bool, arg interface{}) error { | ||||||
| 	var err error | 	var err error | ||||||
| 	m, ok := arg.(IModel) | 	d, ok := arg.(IDocument) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return fmt.Errorf(errFmtNotAModel, nameOf(arg)) | 		return fmt.Errorf(errFmtNotAModel, nameOf(arg)) | ||||||
| 	} | 	} | ||||||
| 	m.setSelf(m) | 	d.setSelf(d) | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| 	selfo := reflect.ValueOf(m) | 	selfo := reflect.ValueOf(d) | ||||||
| 	vp := selfo | 	vp := selfo | ||||||
| 	if vp.Kind() != reflect.Ptr { | 	if vp.Kind() != reflect.Ptr { | ||||||
| 		vp = reflect.New(selfo.Type()) | 		vp = reflect.New(selfo.Type()) | ||||||
| 		vp.Elem().Set(selfo) | 		vp.Elem().Set(selfo) | ||||||
| 	} | 	} | ||||||
| 	var asHasId = vp.Interface().(HasID) | 	var asHasId = vp.Interface().(HasID) | ||||||
| 	var asModel = vp.Interface().(IModel) | 	var asModel = vp.Interface().(IDocument) | ||||||
| 	if isNew { | 	if isNew { | ||||||
| 		m.setCreated(now) | 		d.setCreated(now) | ||||||
| 	} | 	} | ||||||
| 	m.setModified(now) | 	d.setModified(now) | ||||||
| 	idxs := m.getIdxs() | 	idxs := d.getModel().getIdxs() | ||||||
| 	for _, i := range idxs { | 	for _, i := range idxs { | ||||||
| 		_, err = c.Indexes().CreateOne(context.TODO(), *i) | 		_, err = c.Indexes().CreateOne(context.TODO(), *i) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @ -157,28 +157,28 @@ func doSave(c *mongo.Collection, isNew bool, arg interface{}) error { | |||||||
| 			(asHasId).SetId(pnid) | 			(asHasId).SetId(pnid) | ||||||
| 		} | 		} | ||||||
| 		incrementAll(asHasId) | 		incrementAll(asHasId) | ||||||
| 		_, im, _ := ModelRegistry.HasByName(asModel.getTypeName()) | 		_, im, _ := ModelRegistry.HasByName(asModel.getModel().getTypeName()) | ||||||
| 		_ = gridFsSave(asHasId, *im) | 		_ = gridFsSave(asHasId, *im) | ||||||
| 
 | 
 | ||||||
| 		_, err = c.InsertOne(context.TODO(), m.serializeToStore()) | 		_, err = c.InsertOne(context.TODO(), d.serializeToStore()) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			m.setExists(true) | 			d.setExists(true) | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} 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 | 	return err | ||||||
| } | } | ||||||
| func doDelete(m *Model, arg interface{}) error { | func doDelete(d *Document, arg interface{}) error { | ||||||
| 	self, ok := arg.(HasID) | 	self, ok := arg.(HasID) | ||||||
| 
 | 
 | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return fmt.Errorf(errFmtNotHasID, nameOf(arg)) | 		return fmt.Errorf(errFmtNotHasID, nameOf(arg)) | ||||||
| 	} | 	} | ||||||
| 	c := m.getColl() | 	c := d.model.getColl() | ||||||
| 	_, err := c.DeleteOne(context.TODO(), bson.M{"_id": self.Id()}) | 	_, err := c.DeleteOne(context.TODO(), bson.M{"_id": self.Id()}) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		m.exists = false | 		d.exists = false | ||||||
| 	} | 	} | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
							
								
								
									
										47
									
								
								document_slice.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								document_slice.go
									
									
									
									
									
										Normal file
									
								
							| @ -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() | ||||||
|  | } | ||||||
| @ -107,7 +107,7 @@ func gridFsLoad(val any, g GridFSReference, field string) any { | |||||||
| 	return doc.Interface() | 	return doc.Interface() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func gridFsSave(val any, imodel InternalModel) error { | func gridFsSave(val any, imodel Model) error { | ||||||
| 	var rerr error | 	var rerr error | ||||||
| 	v := reflect.ValueOf(val) | 	v := reflect.ValueOf(val) | ||||||
| 	el := v | 	el := v | ||||||
|  | |||||||
							
								
								
									
										250
									
								
								model.go
									
									
									
									
									
								
							
							
						
						
									
										250
									
								
								model.go
									
									
									
									
									
								
							| @ -3,39 +3,22 @@ package orm | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"reflect" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"go.mongodb.org/mongo-driver/bson" | 	"go.mongodb.org/mongo-driver/bson" | ||||||
| 	"go.mongodb.org/mongo-driver/mongo" | 	"go.mongodb.org/mongo-driver/mongo" | ||||||
| 	"go.mongodb.org/mongo-driver/mongo/options" | 	"go.mongodb.org/mongo-driver/mongo/options" | ||||||
|  | 	"reflect" | ||||||
|  | 	"unsafe" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Model - "base" struct for all queryable models
 | // Model - "base" struct for all queryable models
 | ||||||
| type Model struct { | type Model struct { | ||||||
| 	// Created time. updated/added automatically.
 | 	typeName         string `bson:"-"` | ||||||
| 	Created time.Time `bson:"createdAt" json:"createdAt"` | 	Idx              int | ||||||
| 	// Modified time. updated/added automatically.
 | 	Type             reflect.Type | ||||||
| 	Modified time.Time `bson:"updatedAt" json:"updatedAt"` | 	Collection       string | ||||||
| 	typeName string    `bson:"-"` | 	References       map[string]Reference | ||||||
| 	self     any       `bson:"-"` | 	Indexes          map[string][]InternalIndex | ||||||
| 	exists   bool      `bson:"-"` | 	GridFSReferences map[string]GridFSReference | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // HasID is a simple interface that you must implement
 | // HasID is a simple interface that you must implement
 | ||||||
| @ -52,29 +35,17 @@ type HasID interface { | |||||||
| type HasIDSlice []HasID | type HasIDSlice []HasID | ||||||
| 
 | 
 | ||||||
| type IModel interface { | type IModel interface { | ||||||
| 	Append(field string, a ...interface{}) error |  | ||||||
| 	Delete() error |  | ||||||
| 	FindRaw(query interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) | 	FindRaw(query interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) | ||||||
| 	Find(query interface{}, opts ...*options.FindOptions) (*Query, error) | 	Find(query interface{}, opts ...*options.FindOptions) (*Query, error) | ||||||
| 	FindByID(id interface{}) (*Query, error) | 	FindByID(id interface{}) (*Query, error) | ||||||
| 	FindOne(query interface{}, options ...*options.FindOneOptions) (*Query, error) | 	FindOne(query interface{}, options ...*options.FindOneOptions) (*Query, error) | ||||||
| 	FindPaged(query interface{}, page int64, perPage int64, options ...*options.FindOptions) (*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 | 	getColl() *mongo.Collection | ||||||
| 	getIdxs() []*mongo.IndexModel | 	getIdxs() []*mongo.IndexModel | ||||||
| 	getParsedIdxs() map[string][]InternalIndex | 	getParsedIdxs() map[string][]InternalIndex | ||||||
| 	serializeToStore() any |  | ||||||
| 	getTypeName() string | 	getTypeName() string | ||||||
| 	setTypeName(str 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 { | func (m *Model) getTypeName() string { | ||||||
| @ -85,17 +56,6 @@ func (m *Model) setTypeName(str string) { | |||||||
| 	m.typeName = str | 	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 { | func (m *Model) getColl() *mongo.Collection { | ||||||
| 	_, ri, ok := ModelRegistry.HasByName(m.typeName) | 	_, ri, ok := ModelRegistry.HasByName(m.typeName) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| @ -146,12 +106,12 @@ func (m *Model) Find(query interface{}, opts ...*options.FindOptions) (*Query, e | |||||||
| 		op:         OP_FIND_ALL, | 		op:         OP_FIND_ALL, | ||||||
| 	} | 	} | ||||||
| 	q, err := m.FindRaw(query, opts...) | 	q, err := m.FindRaw(query, opts...) | ||||||
| 
 | 	idoc := (*DocumentSlice)(unsafe.Pointer(qqv.Elem().UnsafeAddr())) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		rawRes := bson.A{} | 		rawRes := bson.A{} | ||||||
| 		err = q.All(context.TODO(), &rawRes) | 		err = q.All(context.TODO(), &rawRes) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			m.exists = true | 			idoc.setExists(true) | ||||||
| 		} | 		} | ||||||
| 		qq.rawDoc = rawRes | 		qq.rawDoc = rawRes | ||||||
| 		err = q.All(context.TODO(), &qq.doc) | 		err = q.All(context.TODO(), &qq.doc) | ||||||
| @ -195,14 +155,16 @@ func (m *Model) FindOne(query interface{}, options ...*options.FindOneOptions) ( | |||||||
| 	raw := bson.M{} | 	raw := bson.M{} | ||||||
| 	err := rip.Decode(&raw) | 	err := rip.Decode(&raw) | ||||||
| 	panik(err) | 	panik(err) | ||||||
| 	m.exists = true |  | ||||||
| 	qqn := ModelRegistry.new_(m.typeName) | 	qqn := ModelRegistry.new_(m.typeName) | ||||||
| 	v := reflect.New(reflect.TypeOf(qqn)) | 	idoc, ok := qqn.(IDocument) | ||||||
| 	v.Elem().Set(reflect.ValueOf(qqn)) | 	if ok { | ||||||
|  | 		idoc.setExists(true) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	qq := &Query{ | 	qq := &Query{ | ||||||
| 		collection: m.getColl(), | 		collection: m.getColl(), | ||||||
| 		rawDoc:     raw, | 		rawDoc:     raw, | ||||||
| 		doc:        v.Elem().Interface(), | 		doc:        idoc, | ||||||
| 		op:         OP_FIND_ONE, | 		op:         OP_FIND_ONE, | ||||||
| 		model:      m, | 		model:      m, | ||||||
| 	} | 	} | ||||||
| @ -212,166 +174,13 @@ func (m *Model) FindOne(query interface{}, options ...*options.FindOneOptions) ( | |||||||
| 		qq.reOrganize() | 		qq.reOrganize() | ||||||
| 		err = nil | 		err = nil | ||||||
| 	} | 	} | ||||||
| 	m.self = qq.doc | 	idoc.setSelf(idoc) | ||||||
| 	return qq, err | 	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) { | func createBase(d any) (reflect.Value, int, string) { | ||||||
| 	var n string | 	var n string | ||||||
| 	var ri *InternalModel | 	var ri *Model | ||||||
| 	var ok bool | 	var ok bool | ||||||
| 
 | 
 | ||||||
| 	n, ri, ok = ModelRegistry.HasByName(nameOf(d)) | 	n, ri, ok = ModelRegistry.HasByName(nameOf(d)) | ||||||
| @ -393,20 +202,23 @@ func createBase(d any) (reflect.Value, int, string) { | |||||||
| 	} else { | 	} else { | ||||||
| 		r.Elem().Set(reflect.ValueOf(d)) | 		r.Elem().Set(reflect.ValueOf(d)) | ||||||
| 	} | 	} | ||||||
|  | 	ri.setTypeName(n) | ||||||
|  | 	r.Interface().(IDocument).setModel(*ri) | ||||||
| 
 | 
 | ||||||
| 	return r, i, n | 	return r, i, n | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Create creates a new instance of a given model
 | // Create creates a new instance of a given Document
 | ||||||
| // and returns a pointer to it.
 | // type and returns a pointer to it.
 | ||||||
| func Create(d any) any { | func Create(d any) any { | ||||||
| 	r, i, n := createBase(d) | 	r, _, n := createBase(d) | ||||||
| 	df := r.Elem().Field(i) | 	//df := r.Elem().Field(i)
 | ||||||
| 	dm := df.Interface().(Model) | 	dm := r.Interface().(IDocument) | ||||||
| 	dm.typeName = n | 	dm.getModel().setTypeName(n) | ||||||
| 	what := r.Interface() | 	what := r.Interface() | ||||||
| 	dm.self = what | 
 | ||||||
| 	df.Set(reflect.ValueOf(dm)) | 	dm.setSelf(what) | ||||||
|  | 	//df.Set(reflect.ValueOf(dm))
 | ||||||
| 	return what | 	return what | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -11,9 +11,10 @@ import ( | |||||||
| 
 | 
 | ||||||
| func TestNew(t *testing.T) { | func TestNew(t *testing.T) { | ||||||
| 	initTest() | 	initTest() | ||||||
| 	doc := Create(iti_single()).(*story) | 	is := iti_single() | ||||||
| 	assert.Equal(t, iti_single().Title, doc.Title) | 	doc := Create(is).(*story) | ||||||
| 	assert.Equal(t, iti_single().Chapters[0].Summary, doc.Chapters[0].Summary) | 	assert.Equal(t, is.Title, doc.Title) | ||||||
|  | 	assert.Equal(t, is.Chapters[0].Summary, doc.Chapters[0].Summary) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestSave(t *testing.T) { | func TestSave(t *testing.T) { | ||||||
| @ -46,7 +47,7 @@ func TestPopulate(t *testing.T) { | |||||||
| 	saveDoc(t, storyDoc) | 	saveDoc(t, storyDoc) | ||||||
| 	assert.Greater(t, storyDoc.ID, int64(0)) | 	assert.Greater(t, storyDoc.ID, int64(0)) | ||||||
| 
 | 
 | ||||||
| 	smodel := Create(story{}).(*story) | 	smodel := ModelRegistry["story"] | ||||||
| 	q, err := smodel.FindByID(storyDoc.ID) | 	q, err := smodel.FindByID(storyDoc.ID) | ||||||
| 	assert.Equal(t, nil, err) | 	assert.Equal(t, nil, err) | ||||||
| 	assert.NotPanics(t, func() { | 	assert.NotPanics(t, func() { | ||||||
| @ -67,7 +68,7 @@ func TestUpdate(t *testing.T) { | |||||||
| 	nb.Locked = true | 	nb.Locked = true | ||||||
| 	saveDoc(t, nb) | 	saveDoc(t, nb) | ||||||
| 
 | 
 | ||||||
| 	foundM := Create(band{}).(*band) | 	foundM := ModelRegistry["band"] | ||||||
| 	q, err := foundM.FindByID(int64(1)) | 	q, err := foundM.FindByID(int64(1)) | ||||||
| 	assert.Equal(t, nil, err) | 	assert.Equal(t, nil, err) | ||||||
| 	found := &band{} | 	found := &band{} | ||||||
| @ -81,7 +82,7 @@ func TestModel_FindAll(t *testing.T) { | |||||||
| 	initTest() | 	initTest() | ||||||
| 	im := iti_multi() | 	im := iti_multi() | ||||||
| 	createAndSave(t, &im) | 	createAndSave(t, &im) | ||||||
| 	smodel := Create(story{}).(*story) | 	smodel := ModelRegistry["story"] | ||||||
| 	query, err := smodel.Find(bson.M{}, options.Find()) | 	query, err := smodel.Find(bson.M{}, options.Find()) | ||||||
| 	assert.Equal(t, nil, err) | 	assert.Equal(t, nil, err) | ||||||
| 	final := CreateSlice(story{}) | 	final := CreateSlice(story{}) | ||||||
| @ -98,7 +99,7 @@ func TestModel_PopulateMulti(t *testing.T) { | |||||||
| 	im := iti_multi() | 	im := iti_multi() | ||||||
| 	im.Author = mauthor | 	im.Author = mauthor | ||||||
| 	createAndSave(t, &im) | 	createAndSave(t, &im) | ||||||
| 	smodel := Create(story{}).(*story) | 	smodel := ModelRegistry["story"] | ||||||
| 	query, err := smodel.Find(bson.M{}, options.Find()) | 	query, err := smodel.Find(bson.M{}, options.Find()) | ||||||
| 	assert.Equal(t, nil, err) | 	assert.Equal(t, nil, err) | ||||||
| 	final := CreateSlice(story{}) | 	final := CreateSlice(story{}) | ||||||
| @ -118,7 +119,7 @@ func TestModel_PopulateChained_Multi(t *testing.T) { | |||||||
| 	saveDoc(t, mauthor) | 	saveDoc(t, mauthor) | ||||||
| 	im.Author = mauthor | 	im.Author = mauthor | ||||||
| 	createAndSave(t, &im) | 	createAndSave(t, &im) | ||||||
| 	smodel := Create(story{}).(*story) | 	smodel := ModelRegistry["story"] | ||||||
| 	query, err := smodel.Find(bson.M{}, options.Find()) | 	query, err := smodel.Find(bson.M{}, options.Find()) | ||||||
| 	assert.Equal(t, nil, err) | 	assert.Equal(t, nil, err) | ||||||
| 	final := CreateSlice(story{}) | 	final := CreateSlice(story{}) | ||||||
| @ -141,7 +142,7 @@ func TestPopulate_Chained(t *testing.T) { | |||||||
| 	saveDoc(t, storyDoc) | 	saveDoc(t, storyDoc) | ||||||
| 	assert.Greater(t, storyDoc.ID, int64(0)) | 	assert.Greater(t, storyDoc.ID, int64(0)) | ||||||
| 
 | 
 | ||||||
| 	smodel := Create(story{}).(*story) | 	smodel := ModelRegistry["story"] | ||||||
| 	q, err := smodel.FindByID(storyDoc.ID) | 	q, err := smodel.FindByID(storyDoc.ID) | ||||||
| 	assert.Equal(t, nil, err) | 	assert.Equal(t, nil, err) | ||||||
| 	assert.NotPanics(t, func() { | 	assert.NotPanics(t, func() { | ||||||
| @ -159,15 +160,15 @@ func TestModel_Append(t *testing.T) { | |||||||
| 	initTest() | 	initTest() | ||||||
| 	bandDoc := Create(metallica).(*band) | 	bandDoc := Create(metallica).(*band) | ||||||
| 	saveDoc(t, bandDoc) | 	saveDoc(t, bandDoc) | ||||||
| 	bmodel := Create(band{}).(*band) | 	bmodel := ModelRegistry["band"] | ||||||
| 	query, err := bmodel.FindByID(int64(1)) | 	query, err := bmodel.FindByID(int64(1)) | ||||||
| 	assert.Equal(t, nil, err) | 	assert.Equal(t, nil, err) | ||||||
| 	fin := &band{} | 	fin := &band{} | ||||||
| 	query.Exec(fin) | 	query.Exec(fin) | ||||||
| 	assert.Greater(t, fin.ID, int64(0)) | 	assert.Greater(t, fin.ID, int64(0)) | ||||||
| 	err = bmodel.Append("Characters", "Robert Trujillo") | 	err = fin.Append("Characters", "Robert Trujillo") | ||||||
| 	assert.Equal(t, nil, err) | 	assert.Equal(t, nil, err) | ||||||
| 	saveDoc(t, bmodel) | 	saveDoc(t, fin) | ||||||
| 	fin = &band{} | 	fin = &band{} | ||||||
| 	query, _ = bmodel.FindByID(int64(1)) | 	query, _ = bmodel.FindByID(int64(1)) | ||||||
| 	query.Exec(fin) | 	query.Exec(fin) | ||||||
| @ -185,7 +186,7 @@ func TestModel_Delete(t *testing.T) { | |||||||
| func TestModel_Pull(t *testing.T) { | func TestModel_Pull(t *testing.T) { | ||||||
| 	initTest() | 	initTest() | ||||||
| 	storyDoc := Create(iti_multi()).(*story) | 	storyDoc := Create(iti_multi()).(*story) | ||||||
| 	smodel := Create(story{}).(*story) | 	smodel := ModelRegistry["story"] | ||||||
| 	saveDoc(t, storyDoc) | 	saveDoc(t, storyDoc) | ||||||
| 	err := storyDoc.Pull("Chapters", storyDoc.Chapters[4]) | 	err := storyDoc.Pull("Chapters", storyDoc.Chapters[4]) | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| @ -217,7 +218,7 @@ func TestModel_Swap(t *testing.T) { | |||||||
| func TestModel_GridFSLoad(t *testing.T) { | func TestModel_GridFSLoad(t *testing.T) { | ||||||
| 	initTest() | 	initTest() | ||||||
| 	ModelRegistry.Model(somethingWithNestedChapters{}) | 	ModelRegistry.Model(somethingWithNestedChapters{}) | ||||||
| 	model := Create(somethingWithNestedChapters{}).(*somethingWithNestedChapters) | 	model := ModelRegistry["somethingWithNestedChapters"] | ||||||
| 	thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters) | 	thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters) | ||||||
| 	found := &somethingWithNestedChapters{} | 	found := &somethingWithNestedChapters{} | ||||||
| 
 | 
 | ||||||
| @ -236,7 +237,7 @@ func TestModel_GridFSLoad(t *testing.T) { | |||||||
| func TestModel_GridFSLoad_Chained(t *testing.T) { | func TestModel_GridFSLoad_Chained(t *testing.T) { | ||||||
| 	initTest() | 	initTest() | ||||||
| 	ModelRegistry.Model(somethingWithNestedChapters{}) | 	ModelRegistry.Model(somethingWithNestedChapters{}) | ||||||
| 	model := Create(somethingWithNestedChapters{}).(*somethingWithNestedChapters) | 	model := ModelRegistry["somethingWithNestedChapters"] | ||||||
| 	thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters) | 	thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters) | ||||||
| 	found := &somethingWithNestedChapters{} | 	found := &somethingWithNestedChapters{} | ||||||
| 
 | 
 | ||||||
| @ -254,7 +255,7 @@ func TestModel_GridFSLoad_Chained(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestModel_GridFSLoad_Complex(t *testing.T) { | func TestModel_GridFSLoad_Complex(t *testing.T) { | ||||||
| 	initTest() | 	initTest() | ||||||
| 	model := Create(story{}).(*story) | 	model := ModelRegistry["story"] | ||||||
| 	bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band) | 	bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band) | ||||||
| 	thingDoc := Create(iti_multi()).(*story) | 	thingDoc := Create(iti_multi()).(*story) | ||||||
| 	mauthor := Create(author).(*user) | 	mauthor := Create(author).(*user) | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								query.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								query.go
									
									
									
									
									
								
							| @ -156,7 +156,7 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		rawt := ModelRegistry.new_(tto.Name()) | 		rawt := ModelRegistry.new_(tto.Name()) | ||||||
| 		t := rawt.(IModel) | 		t := rawt.(IDocument) | ||||||
| 		q := bson.M{"_id": rawDoc} | 		q := bson.M{"_id": rawDoc} | ||||||
| 		reso := DB.Collection(rcoll).FindOne(context.TODO(), q) | 		reso := DB.Collection(rcoll).FindOne(context.TODO(), q) | ||||||
| 		reso.Decode(t) | 		reso.Decode(t) | ||||||
| @ -516,7 +516,7 @@ func (q *Query) Exec(result interface{}) { | |||||||
| 	if doc.Elem().Kind() == reflect.Slice { | 	if doc.Elem().Kind() == reflect.Slice { | ||||||
| 		for i := 0; i < doc.Elem().Len(); i++ { | 		for i := 0; i < doc.Elem().Len(); i++ { | ||||||
| 			cur := doc.Elem().Index(i) | 			cur := doc.Elem().Index(i) | ||||||
| 			imodel, ok := cur.Interface().(IModel) | 			imodel, ok := cur.Interface().(IDocument) | ||||||
| 			if ok { | 			if ok { | ||||||
| 				imodel.setExists(true) | 				imodel.setExists(true) | ||||||
| 				doc.Elem().Index(i).Set(reflect.ValueOf(imodel)) | 				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()) | 	reflect.ValueOf(result).Elem().Set(reflect.ValueOf(q.doc).Elem()) | ||||||
| 	q.model.self = q.doc |  | ||||||
| 	q.done = true | 	q.done = true | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										40
									
								
								registry.go
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								registry.go
									
									
									
									
									
								
							| @ -16,17 +16,6 @@ import ( | |||||||
| 	"golang.org/x/net/context" | 	"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
 | // Reference stores a typed document reference
 | ||||||
| type Reference struct { | type Reference struct { | ||||||
| 	// owning model name
 | 	// owning model name
 | ||||||
| @ -51,10 +40,10 @@ type GridFSReference struct { | |||||||
| 	Idx         int | 	Idx         int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type TModelRegistry map[string]*InternalModel | type TModelRegistry map[string]*Model | ||||||
| 
 | 
 | ||||||
| // ModelRegistry - the ModelRegistry stores a map containing
 | // 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
 | // model name
 | ||||||
| var ModelRegistry = make(TModelRegistry, 0) | var ModelRegistry = make(TModelRegistry, 0) | ||||||
| 
 | 
 | ||||||
| @ -182,7 +171,7 @@ func parseTags(t reflect.Type, v reflect.Value) (map[string][]InternalIndex, map | |||||||
| 				ft = ft.Elem() | 				ft = ft.Elem() | ||||||
| 				fallthrough | 				fallthrough | ||||||
| 			case reflect.Struct: | 			case reflect.Struct: | ||||||
| 				if ft.ConvertibleTo(reflect.TypeOf(Model{})) { | 				if ft.ConvertibleTo(reflect.TypeOf(Document{})) { | ||||||
| 					collTag, err := tags.Get("coll") | 					collTag, err := tags.Get("coll") | ||||||
| 					panik(err) | 					panik(err) | ||||||
| 					coll = collTag.Name | 					coll = collTag.Name | ||||||
| @ -223,10 +212,10 @@ func parseTags(t reflect.Type, v reflect.Value) (map[string][]InternalIndex, map | |||||||
| 	return idcs, refs, gfsRefs, coll | 	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
 | // to the argument passed, as well as a boolean indicating whether it
 | ||||||
| // was found. otherwise returns `"", nil, false`
 | // 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) | 	t := reflect.TypeOf(i) | ||||||
| 	if t.Kind() == reflect.Ptr { | 	if t.Kind() == reflect.Ptr { | ||||||
| 		t = t.Elem() | 		t = t.Elem() | ||||||
| @ -240,14 +229,14 @@ func (r TModelRegistry) Has(i interface{}) (string, *InternalModel, bool) { | |||||||
| 
 | 
 | ||||||
| // HasByName functions almost identically to Has,
 | // HasByName functions almost identically to Has,
 | ||||||
| // except that it takes a string as its argument.
 | // 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 { | 	if t, ok := ModelRegistry[n]; ok { | ||||||
| 		return n, t, true | 		return n, t, true | ||||||
| 	} | 	} | ||||||
| 	return "", nil, false | 	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 { | func (r TModelRegistry) Index(n string) int { | ||||||
| 	if v, ok := ModelRegistry[n]; ok { | 	if v, ok := ModelRegistry[n]; ok { | ||||||
| 		return v.Idx | 		return v.Idx | ||||||
| @ -259,9 +248,12 @@ func (r TModelRegistry) new_(n string) interface{} { | |||||||
| 	if name, m, ok := ModelRegistry.HasByName(n); ok { | 	if name, m, ok := ModelRegistry.HasByName(n); ok { | ||||||
| 		v := reflect.New(m.Type) | 		v := reflect.New(m.Type) | ||||||
| 		df := v.Elem().Field(m.Idx) | 		df := v.Elem().Field(m.Idx) | ||||||
| 		d := df.Interface().(Model) | 		do := reflect.New(df.Type()) | ||||||
| 		d.typeName = name | 		d := do.Interface().(IDocument) | ||||||
| 		df.Set(reflect.ValueOf(d)) | 		//d := df.Interface().(IDocument)
 | ||||||
|  | 		d.setModel(*m) | ||||||
|  | 		d.getModel().typeName = name | ||||||
|  | 		df.Set(reflect.ValueOf(d).Elem()) | ||||||
| 		return v.Interface() | 		return v.Interface() | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| @ -303,19 +295,19 @@ func (r TModelRegistry) Model(mdl ...any) { | |||||||
| 		idx := -1 | 		idx := -1 | ||||||
| 		for i := 0; i < v.NumField(); i++ { | 		for i := 0; i < v.NumField(); i++ { | ||||||
| 			ft := t.Field(i) | 			ft := t.Field(i) | ||||||
| 			if (ft.Type.ConvertibleTo(reflect.TypeOf(Model{}))) { | 			if (ft.Type.ConvertibleTo(reflect.TypeOf(Document{}))) { | ||||||
| 				idx = i | 				idx = i | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if idx < 0 { | 		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) | 		inds, refs, gfs, coll := parseTags(t, v) | ||||||
| 		if coll == "" { | 		if coll == "" { | ||||||
| 			panic(fmt.Sprintf("a model needs to be given a collection name! (passed type: %s)", n)) | 			panic(fmt.Sprintf("a model needs to be given a collection name! (passed type: %s)", n)) | ||||||
| 		} | 		} | ||||||
| 		ModelRegistry[n] = &InternalModel{ | 		ModelRegistry[n] = &Model{ | ||||||
| 			Idx:              idx, | 			Idx:              idx, | ||||||
| 			Type:             t, | 			Type:             t, | ||||||
| 			Collection:       coll, | 			Collection:       coll, | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								testing.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								testing.go
									
									
									
									
									
								
							| @ -37,19 +37,19 @@ type chapter struct { | |||||||
| 
 | 
 | ||||||
| type band struct { | type band struct { | ||||||
| 	ID         int64 `bson:"_id" json:"_id"` | 	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"` | 	Name       string   `bson:"name" json:"name" form:"name"` | ||||||
| 	Locked     bool     `bson:"locked" json:"locked" form:"locked"` | 	Locked     bool     `bson:"locked" json:"locked" form:"locked"` | ||||||
| 	Characters []string `bson:"characters" json:"characters" form:"characters"` | 	Characters []string `bson:"characters" json:"characters" form:"characters"` | ||||||
| } | } | ||||||
| type user struct { | type user struct { | ||||||
| 	ID       int64 `bson:"_id" json:"_id"` | 	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"` | 	Username string `bson:"username" json:"username"` | ||||||
| } | } | ||||||
| type story struct { | type story struct { | ||||||
| 	ID        int64 `bson:"_id" json:"_id"` | 	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"` | 	Title     string    `bson:"title" json:"title" form:"title"` | ||||||
| 	Author    *user     `bson:"author" json:"author" ref:"user"` | 	Author    *user     `bson:"author" json:"author" ref:"user"` | ||||||
| 	CoAuthor  *user     `bson:"coAuthor" json:"coAuthor" ref:"user"` | 	CoAuthor  *user     `bson:"coAuthor" json:"coAuthor" ref:"user"` | ||||||
| @ -62,7 +62,7 @@ type story struct { | |||||||
| } | } | ||||||
| type somethingWithNestedChapters struct { | type somethingWithNestedChapters struct { | ||||||
| 	ID         int64 `bson:"_id" json:"_id"` | 	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"` | 	Chapters   []chapter `bson:"chapters" json:"chapters"` | ||||||
| 	NestedText string    `json:"text" bson:"-" gridfs:"nested_text,/nested/{{.ID}}.txt"` | 	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) { | func (s *story) SetId(id any) { | ||||||
| 	s.ID = id.(int64) | 	s.ID = id.(int64) | ||||||
| 	//var t IModel = s
 | 	//var t IDocument =s
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *band) SetId(id any) { | 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() | 	err := doc.Save() | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func createAndSave(t *testing.T, doc IModel) { | func createAndSave(t *testing.T, doc IDocument) { | ||||||
| 	mdl := Create(doc).(IModel) | 	mdl := Create(doc).(IDocument) | ||||||
| 	saveDoc(t, mdl) | 	saveDoc(t, mdl) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										44
									
								
								util.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								util.go
									
									
									
									
									
								
							| @ -167,3 +167,47 @@ func checkSlice(ref reflect.Value) error { | |||||||
| 	} | 	} | ||||||
| 	return nil | 	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 {
 | ||||||
|  | 	//
 | ||||||
|  | 	//}
 | ||||||
|  | }*/ | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user