implement more bug fixes and improvements for populating deeply nested refs

This commit is contained in:
☙◦ The Tablet ❀ GamerGirlandCo ◦❧ 2025-04-11 23:05:14 -04:00
parent 378b5d756a
commit 6e865b733d
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
3 changed files with 108 additions and 89 deletions

View File

@ -331,7 +331,8 @@ func (d *Document) Populate(fields ...string) {
v := reflect.ValueOf(d.self)
tt := v.Elem().Type()
tmp1 = populate(r, refColl.collection, rawDoc, field, d.self)
tmp1 = populate(r, d.populatedFields, refColl.collection, rawDoc, field, d.self)
nv := reflect.NewAt(tt, v.UnsafePointer())
nv.Elem().Set(reflect.ValueOf(tmp1).Elem())
}

186
query.go
View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/fatih/structtag"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
@ -28,7 +29,10 @@ const (
OP_FIND = "find"
)
func populate(r Reference, rcoll string, rawDoc interface{}, d string, src interface{}) any {
func populate(r Reference,
alreadyPopulated map[string]bool,
rcoll string, rawDoc interface{},
curDescent string, src interface{}) any {
rt := reflect.TypeOf(src)
rv := reflect.ValueOf(src)
srt := rt
@ -42,6 +46,27 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
rv = reflect.New(rt)
rv.Elem().Set(reflect.ValueOf(src))
}
if srt.Kind() == reflect.Struct && !isObject(rawDoc) {
nrd := ModelRegistry.Get(srt.Name())
if nrd != nil && nrd.collection != rcoll {
q, err := nrd.FindByID(rawDoc)
if err == nil {
rawDoc = q.rawDoc
toPopulate := []string{curDescent}
if asIDoc, ok := rv.Interface().(IDocument); ok {
for k, v := range asIDoc.getPopulated() {
if k != curDescent && v {
toPopulate = append(toPopulate, k)
}
}
}
q.Populate(toPopulate...)
q.Exec(rv.Interface())
src = rv.Interface()
}
}
}
var fieldsMap [3]string
type bsonWhat struct {
@ -59,12 +84,14 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
default:
w.What = "-"
}
var fld string
var next string
if len(strings.Split(d, ".")) > 1 {
next = strings.Join(strings.Split(d, ".")[1:], ".")
d = strings.Split(d, ".")[0]
if len(strings.Split(curDescent, ".")) > 1 {
next = strings.Join(strings.Split(curDescent, ".")[1:], ".")
fld = strings.Split(curDescent, ".")[0]
} else {
next = d
fld = curDescent
next = curDescent
}
var toReturn interface{}
switch w.What {
@ -76,11 +103,18 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
} else {
rahh = rv
}
if len(rawDoc.(bson.A)) > 0 {
if !isObject(rawDoc.(bson.A)[0]) {
next = curDescent
}
}
for i, el := range rawDoc.(bson.A) {
it := rahh.Index(i)
popped := populate(r, rcoll, el, next, it.Interface())
rel := el
popped := populate(r, alreadyPopulated, rcoll, rel, next, it.Interface())
if pidoc, pok := popped.(IDocument); pok {
pidoc.setRaw(el)
pidoc.setRaw(rel)
}
poppedVal := reflect.ValueOf(popped)
if poppedVal.Kind() == reflect.Pointer {
@ -110,22 +144,22 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
var sf reflect.Value
var rsf reflect.Value
if rv.Kind() == reflect.Pointer {
sf = rv.Elem().FieldByName(d)
sf = rv.Elem().FieldByName(fld)
} else {
sf = rv.FieldByName(d)
sf = rv.FieldByName(fld)
}
if rv.Kind() == reflect.Pointer {
rsf = rv.Elem().FieldByName(d)
rsf = rv.Elem().FieldByName(fld)
} else {
rsf = rv.FieldByName(d)
rsf = rv.FieldByName(fld)
}
var ff reflect.StructField
var ok bool
if rt.Kind() == reflect.Pointer {
ff, ok = rt.Elem().FieldByName(d)
ff, ok = rt.Elem().FieldByName(fld)
} else {
ff, ok = rt.FieldByName(d)
ff, ok = rt.FieldByName(fld)
}
if ok {
@ -137,11 +171,13 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
if fttt.Kind() == reflect.Pointer || fttt.Kind() == reflect.Slice {
fttt = fttt.Elem()
}
fieldsMap = [3]string{d, fttt.Name(), val.Name}
fieldsMap = [3]string{fld, fttt.Name(), val.Name}
}
}
} else {
fmt.Println("todo")
}
intermediate := populate(r, rcoll, dd[fieldsMap[2]], next, sf.Interface())
intermediate := populate(r, alreadyPopulated, rcoll, dd[fieldsMap[2]], next, sf.Interface())
/*if iidoc, idocOk := intermediate.(IDocument); idocOk {
if (reflect.ValueOf(intermediate).CanAddr() && !reflect.ValueOf(intermediate).IsNil()) || !reflect.ValueOf(intermediate).IsZero() {
iiid, iok := intermediate.(HasID)
@ -164,42 +200,44 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
}
default:
isNotStructOrSlice = true
tto := r.HydratedType
if tto.Kind() == reflect.Pointer || tto.Kind() == reflect.Slice {
tto = tto.Elem()
}
rawt := ModelRegistry.new_(tto.Name())
t := rawt.(IDocument)
q := bson.M{"_id": rawDoc}
reso := DB.Collection(rcoll).FindOne(context.TODO(), q)
if !errors.Is(reso.Err(), mongo.ErrNoDocuments) {
var anotherMap = make(bson.M)
reso.Decode(&anotherMap)
reflect.ValueOf(t).Elem().Set(reflect.ValueOf(rerere(anotherMap, tto, false)).Elem())
t.setRaw(anotherMap)
}
hatred := rv
if hatred.Kind() == reflect.Pointer {
hatred = hatred.Elem()
}
if hatred.CanSet() {
if reflect.ValueOf(t).Kind() == reflect.Pointer {
if hatred.Kind() == reflect.Pointer {
hatred.Set(reflect.ValueOf(t))
} else {
hatred.Set(reflect.ValueOf(t).Elem())
}
recover()
} else {
hatred.Set(reflect.ValueOf(t))
if r.exists {
tto := r.HydratedType
if tto.Kind() == reflect.Pointer || tto.Kind() == reflect.Slice {
tto = tto.Elem()
}
} else {
src = t
toReturn = src
rawt := ModelRegistry.new_(tto.Name())
t := rawt.(IDocument)
q := bson.M{"_id": rawDoc}
reso := DB.Collection(rcoll).FindOne(context.TODO(), q)
if !errors.Is(reso.Err(), mongo.ErrNoDocuments) {
var anotherMap = make(bson.M)
reso.Decode(&anotherMap)
reflect.ValueOf(t).Elem().Set(reflect.ValueOf(rerere(anotherMap, tto, false)).Elem())
t.setRaw(anotherMap)
}
hatred := rv
if hatred.Kind() == reflect.Pointer {
hatred = hatred.Elem()
}
if hatred.CanSet() {
if reflect.ValueOf(t).Kind() == reflect.Pointer {
if hatred.Kind() == reflect.Pointer {
hatred.Set(reflect.ValueOf(t))
} else {
hatred.Set(reflect.ValueOf(t).Elem())
}
} else {
hatred.Set(reflect.ValueOf(t))
}
} else {
src = t
toReturn = src
}
t.SetSelf(t)
t.setExists(true)
}
t.SetSelf(t)
t.setExists(true)
}
if toReturn == nil {
@ -209,6 +247,8 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
if !isNotStructOrSlice {
sidoc.setRaw(rawDoc)
}
} else if rv.Kind() == reflect.Pointer && rt.Kind() != reflect.Pointer {
rv = rv.Elem()
}
return rv.Interface()
}
@ -254,50 +294,20 @@ func (q *Query) LoadFile(fields ...string) *Query {
return q
}
func readFields(field string, m *Model) (Reference, string) {
var r Reference
var keptKey string
func readFields(field string, m *Model) map[string]Reference {
r := make(map[string]Reference)
if m == nil {
return r, keptKey
return r
}
for k2, v := range m.references {
if strings.HasPrefix(k2, field) {
r = v
keptKey = k2
break
} else if _, ok := m.references[field]; !ok {
splitSegs := strings.Split(field, ".")
for ii := 0; ii < len(splitSegs)-1; ii++ {
mr, ok2 := m.references[splitSegs[ii]]
if ok2 {
_, sec, _ := ModelRegistry.HasByName(mr.Model)
if sec != nil {
refff, ok3 := sec.references[splitSegs[ii+1]]
if ok3 {
r = refff
keptKey = k2
break
} else {
joined := strings.Join(splitSegs[ii+1:], ".")
inner1, innerKey := readFields(joined, sec)
if inner1.exists {
r = inner1
keptKey = innerKey
}
break
}
}
}
}
} else if ok {
r = m.references[field]
break
}
if keptKey != "" {
break
for k, v := range m.references {
if strings.HasPrefix(field, k) {
r[k] = v
}
}
return r, keptKey
if vv, ok := m.references[field]; ok {
r[field] = vv
}
return r
}
// Populate populates document references via reflection

View File

@ -164,6 +164,14 @@ func incrementInterface(t interface{}) interface{} {
return t
}
func isObject(t interface{}) bool {
switch t.(type) {
case bson.M, bson.D:
return true
default:
return false
}
}
func pull(s reflect.Value, idx int, typ reflect.Type) reflect.Value {
retI := reflect.New(reflect.SliceOf(typ))
for i := 0; i < s.Len(); i++ {