restructuring

- use pointers where possible when using reflection
- add a `CreateSlice` func, for storing slices of Models created by Find and FindPaged queries
- modify tests to use `CreateSlice`
This commit is contained in:
parent 14ac93b327
commit 4297071b3b
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
4 changed files with 70 additions and 25 deletions

@ -59,12 +59,12 @@ type IModel interface {
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)
getColl() *mongo.Collection
getIdxs() []*mongo.IndexModel
getParsedIdxs() map[string][]InternalIndex
Pull(field string, a ...any) error Pull(field string, a ...any) error
Remove() error Remove() error
Save() error Save() error
getColl() *mongo.Collection
getIdxs() []*mongo.IndexModel
getParsedIdxs() map[string][]InternalIndex
serializeToStore() any serializeToStore() any
setTypeName(str string) setTypeName(str string)
getExists() bool getExists() bool
@ -131,8 +131,9 @@ func (m *Model) FindRaw(query interface{}, opts ...*options.FindOptions) (*mongo
// returns a pointer to a Query for further chaining. // returns a pointer to a Query for further chaining.
func (m *Model) Find(query interface{}, opts ...*options.FindOptions) (*Query, error) { func (m *Model) Find(query interface{}, opts ...*options.FindOptions) (*Query, error) {
qqn := ModelRegistry.new_(m.typeName) qqn := ModelRegistry.new_(m.typeName)
qqv := reflect.New(reflect.SliceOf(reflect.TypeOf(qqn).Elem())) qqt := reflect.SliceOf(reflect.TypeOf(qqn))
qqv.Elem().Set(reflect.Zero(qqv.Elem().Type())) qqv := reflect.New(qqt)
qqv.Elem().Set(reflect.MakeSlice(qqt, 0, 0))
qq := &Query{ qq := &Query{
Model: *m, Model: *m,
Collection: m.getColl(), Collection: m.getColl(),
@ -190,10 +191,13 @@ func (m *Model) FindOne(query interface{}, options ...*options.FindOneOptions) (
err := rip.Decode(&raw) err := rip.Decode(&raw)
panik(err) panik(err)
m.exists = true m.exists = true
qqn := ModelRegistry.new_(m.typeName)
v := reflect.New(reflect.TypeOf(qqn))
v.Elem().Set(reflect.ValueOf(qqn))
qq := &Query{ qq := &Query{
Collection: m.getColl(), Collection: m.getColl(),
rawDoc: raw, rawDoc: raw,
doc: ModelRegistry.new_(m.typeName), doc: v.Elem().Interface(),
Op: OP_FIND_ONE, Op: OP_FIND_ONE,
Model: *m, Model: *m,
} }
@ -360,9 +364,7 @@ func (m *Model) serializeToStore() any {
return serializeIDs((m).self) return serializeIDs((m).self)
} }
// Create creates a new instance of a given model func createBase(d any) (reflect.Value, int, string) {
// and returns a pointer to it.
func Create(d any) any {
var n string var n string
var ri *InternalModel var ri *InternalModel
var ok bool var ok bool
@ -381,13 +383,21 @@ func Create(d any) any {
r.Elem().Set(v) r.Elem().Set(v)
df := r.Elem().Field(i)
dm := df.Interface().(Model)
if reflect.ValueOf(d).Kind() == reflect.Pointer { if reflect.ValueOf(d).Kind() == reflect.Pointer {
r.Elem().Set(reflect.ValueOf(d).Elem()) r.Elem().Set(reflect.ValueOf(d).Elem())
} else { } else {
r.Elem().Set(reflect.ValueOf(d)) r.Elem().Set(reflect.ValueOf(d))
} }
return r, i, n
}
// Create creates a new instance of a given model
// 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 dm.typeName = n
what := r.Interface() what := r.Interface()
dm.self = what dm.self = what
@ -395,6 +405,15 @@ func Create(d any) any {
return what return what
} }
func CreateSlice[T any](d T) []*T {
r, _, _ := createBase(d)
rtype := r.Type()
rslice := reflect.SliceOf(rtype)
newItem := reflect.New(rslice)
newItem.Elem().Set(reflect.MakeSlice(rslice, 0, 0))
return newItem.Elem().Interface().([]*T)
}
func (m *Model) PrintMe() { func (m *Model) PrintMe() {
fmt.Printf("My name is %s !\n", nameOf(m)) fmt.Printf("My name is %s !\n", nameOf(m))
} }

@ -83,7 +83,7 @@ func TestModel_FindAll(t *testing.T) {
smodel := Create(story{}).(*story) smodel := Create(story{}).(*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 := make([]story, 0) final := CreateSlice(story{})
query.Exec(&final) query.Exec(&final)
assert.Greater(t, len(final), 0) assert.Greater(t, len(final), 0)
} }
@ -92,11 +92,14 @@ func TestModel_PopulateMulti(t *testing.T) {
initTest() initTest()
bandDoc := Create(iti_single.Chapters[0].Bands[0]).(*band) bandDoc := Create(iti_single.Chapters[0].Bands[0]).(*band)
saveDoc(t, bandDoc) saveDoc(t, bandDoc)
mauthor := Create(author).(*user)
saveDoc(t, mauthor)
iti_multi.Author = mauthor
createAndSave(t, &iti_multi) createAndSave(t, &iti_multi)
smodel := Create(story{}).(*story) smodel := Create(story{}).(*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 := make([]story, 0) final := CreateSlice(story{})
query.Populate("Author", "Chapters.Bands").Exec(&final) query.Populate("Author", "Chapters.Bands").Exec(&final)
assert.Greater(t, len(final), 0) assert.Greater(t, len(final), 0)
for _, s := range final { for _, s := range final {
@ -151,6 +154,7 @@ func TestModel_Swap(t *testing.T) {
initTest() initTest()
iti_single.Author = &author iti_single.Author = &author
storyDoc := Create(iti_single).(*story) storyDoc := Create(iti_single).(*story)
saveDoc(t, storyDoc)
storyDoc.Chapters[0].Bands = append(storyDoc.Chapters[0].Bands, metallica) storyDoc.Chapters[0].Bands = append(storyDoc.Chapters[0].Bands, metallica)
assert.Equal(t, 2, len(storyDoc.Chapters[0].Bands)) assert.Equal(t, 2, len(storyDoc.Chapters[0].Bands))
err := storyDoc.Swap("Chapters[0].Bands", 0, 1) err := storyDoc.Swap("Chapters[0].Bands", 0, 1)

@ -226,7 +226,7 @@ func (q *Query) Populate(fields ...string) *Query {
_, refColl, _ := ModelRegistry.HasByName(tto.Name()) _, refColl, _ := ModelRegistry.HasByName(tto.Name())
var tmp1 interface{} var tmp1 interface{}
if arr, ok := rawDoc.(bson.A); ok { if arr, ok := rawDoc.(bson.A); ok {
typ := cm.Type typ := reflect.PointerTo(cm.Type)
slic := reflect.New(reflect.SliceOf(typ)) slic := reflect.New(reflect.SliceOf(typ))
for i, val2 := range arr { for i, val2 := range arr {
ref := reflect.ValueOf(q.doc) ref := reflect.ValueOf(q.doc)
@ -236,7 +236,7 @@ func (q *Query) Populate(fields ...string) *Query {
src := ref.Index(i).Interface() src := ref.Index(i).Interface()
inter := populate(r, refColl.Collection, val2, field, src) inter := populate(r, refColl.Collection, val2, field, src)
if reflect.ValueOf(inter).Kind() == reflect.Pointer { if reflect.ValueOf(inter).Kind() == reflect.Pointer {
slic.Elem().Set(reflect.Append(slic.Elem(), reflect.ValueOf(inter).Elem())) slic.Elem().Set(reflect.Append(slic.Elem(), reflect.ValueOf(inter)))
} else { } else {
slic.Elem().Set(reflect.Append(slic, reflect.ValueOf(inter))) slic.Elem().Set(reflect.Append(slic, reflect.ValueOf(inter)))
} }
@ -255,35 +255,46 @@ func (q *Query) reOrganize() {
var trvo reflect.Value var trvo reflect.Value
if arr, ok := q.rawDoc.(bson.A); ok { if arr, ok := q.rawDoc.(bson.A); ok {
typ := ModelRegistry[q.Model.typeName].Type typ := ModelRegistry[q.Model.typeName].Type
if typ.Kind() != reflect.Pointer {
typ = reflect.PointerTo(typ)
}
slic := reflect.New(reflect.SliceOf(typ)) slic := reflect.New(reflect.SliceOf(typ))
for _, v2 := range arr { for _, v2 := range arr {
inter := reflect.ValueOf(rerere(v2, typ)) inter := reflect.ValueOf(rerere(v2, typ))
if inter.Kind() == reflect.Pointer { /*if inter.Kind() == reflect.Pointer {
inter = inter.Elem() inter = inter.Elem()
} }*/
slic.Elem().Set(reflect.Append(slic.Elem(), inter)) slic.Elem().Set(reflect.Append(slic.Elem(), inter))
} }
trvo = slic.Elem() trvo = slic.Elem()
} else { } else {
trvo = reflect.ValueOf(rerere(q.rawDoc, reflect.TypeOf(q.doc))) trvo = reflect.ValueOf(rerere(q.rawDoc, reflect.TypeOf(q.doc)))
for { /*for {
if trvo.Kind() == reflect.Pointer { if trvo.Kind() == reflect.Pointer {
trvo = trvo.Elem() trvo = trvo.Elem()
} else { } else {
break break
} }
} }*/
} }
resV := reflect.ValueOf(q.doc) resV := reflect.ValueOf(q.doc)
for { for {
if resV.Kind() == reflect.Pointer { if resV.Kind() == reflect.Pointer {
resV = resV.Elem() if resV.Elem().Kind() == reflect.Slice {
resV = resV.Elem()
} else {
break
}
} else { } else {
break break
} }
} }
resV.Set(trvo) if resV.CanSet() {
resV.Set(trvo)
} else {
resV.Elem().Set(trvo.Elem())
}
} }
func rerere(input interface{}, resType reflect.Type) interface{} { func rerere(input interface{}, resType reflect.Type) interface{} {
@ -303,7 +314,7 @@ func rerere(input interface{}, resType reflect.Type) interface{} {
resType = resType.Elem() resType = resType.Elem()
} }
resV := reflect.New(resType) resV := reflect.New(resType)
var rve reflect.Value = resV var rve = resV
if rve.Kind() == reflect.Pointer { if rve.Kind() == reflect.Pointer {
rve = resV.Elem() rve = resV.Elem()
} }
@ -472,6 +483,17 @@ func (q *Query) Exec(result interface{}) {
if q.done { if q.done {
panic("Exec() has already been called!") panic("Exec() has already been called!")
} }
doc := reflect.ValueOf(q.doc)
if doc.Elem().Kind() == reflect.Slice {
for i := 0; i < doc.Elem().Len(); i++ {
cur := doc.Elem().Index(i)
imodel, ok := cur.Interface().(IModel)
if ok {
imodel.setExists(true)
doc.Elem().Index(i).Set(reflect.ValueOf(imodel))
}
}
}
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.Model.self = q.doc
q.done = true q.done = true

@ -72,7 +72,7 @@ func getRawTypeFromTag(tagOpt string, slice bool) reflect.Type {
var v uint = 0 var v uint = 0
t = reflect.TypeOf(v) t = reflect.TypeOf(v)
case "string": case "string":
var v string = "0" var v = "0"
t = reflect.TypeOf(v) t = reflect.TypeOf(v)
} }
@ -207,11 +207,11 @@ func (r TModelRegistry) Index(n string) int {
} }
func (t TModelRegistry) new_(n string) interface{} { func (t TModelRegistry) new_(n string) interface{} {
if n, 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) d := df.Interface().(Model)
d.typeName = n d.typeName = name
df.Set(reflect.ValueOf(d)) df.Set(reflect.ValueOf(d))
return v.Interface() return v.Interface()
} }