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) v := reflect.ValueOf(d.self)
tt := v.Elem().Type() 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 := reflect.NewAt(tt, v.UnsafePointer())
nv.Elem().Set(reflect.ValueOf(tmp1).Elem()) nv.Elem().Set(reflect.ValueOf(tmp1).Elem())
} }

120
query.go
View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"github.com/fatih/structtag" "github.com/fatih/structtag"
"go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo"
@ -28,7 +29,10 @@ const (
OP_FIND = "find" 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) rt := reflect.TypeOf(src)
rv := reflect.ValueOf(src) rv := reflect.ValueOf(src)
srt := rt srt := rt
@ -42,6 +46,27 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
rv = reflect.New(rt) rv = reflect.New(rt)
rv.Elem().Set(reflect.ValueOf(src)) 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 var fieldsMap [3]string
type bsonWhat struct { type bsonWhat struct {
@ -59,12 +84,14 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
default: default:
w.What = "-" w.What = "-"
} }
var fld string
var next string var next string
if len(strings.Split(d, ".")) > 1 { if len(strings.Split(curDescent, ".")) > 1 {
next = strings.Join(strings.Split(d, ".")[1:], ".") next = strings.Join(strings.Split(curDescent, ".")[1:], ".")
d = strings.Split(d, ".")[0] fld = strings.Split(curDescent, ".")[0]
} else { } else {
next = d fld = curDescent
next = curDescent
} }
var toReturn interface{} var toReturn interface{}
switch w.What { switch w.What {
@ -76,11 +103,18 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
} else { } else {
rahh = rv rahh = rv
} }
if len(rawDoc.(bson.A)) > 0 {
if !isObject(rawDoc.(bson.A)[0]) {
next = curDescent
}
}
for i, el := range rawDoc.(bson.A) { for i, el := range rawDoc.(bson.A) {
it := rahh.Index(i) 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 { if pidoc, pok := popped.(IDocument); pok {
pidoc.setRaw(el) pidoc.setRaw(rel)
} }
poppedVal := reflect.ValueOf(popped) poppedVal := reflect.ValueOf(popped)
if poppedVal.Kind() == reflect.Pointer { 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 sf reflect.Value
var rsf reflect.Value var rsf reflect.Value
if rv.Kind() == reflect.Pointer { if rv.Kind() == reflect.Pointer {
sf = rv.Elem().FieldByName(d) sf = rv.Elem().FieldByName(fld)
} else { } else {
sf = rv.FieldByName(d) sf = rv.FieldByName(fld)
} }
if rv.Kind() == reflect.Pointer { if rv.Kind() == reflect.Pointer {
rsf = rv.Elem().FieldByName(d) rsf = rv.Elem().FieldByName(fld)
} else { } else {
rsf = rv.FieldByName(d) rsf = rv.FieldByName(fld)
} }
var ff reflect.StructField var ff reflect.StructField
var ok bool var ok bool
if rt.Kind() == reflect.Pointer { if rt.Kind() == reflect.Pointer {
ff, ok = rt.Elem().FieldByName(d) ff, ok = rt.Elem().FieldByName(fld)
} else { } else {
ff, ok = rt.FieldByName(d) ff, ok = rt.FieldByName(fld)
} }
if ok { 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 { if fttt.Kind() == reflect.Pointer || fttt.Kind() == reflect.Slice {
fttt = fttt.Elem() 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 iidoc, idocOk := intermediate.(IDocument); idocOk {
if (reflect.ValueOf(intermediate).CanAddr() && !reflect.ValueOf(intermediate).IsNil()) || !reflect.ValueOf(intermediate).IsZero() { if (reflect.ValueOf(intermediate).CanAddr() && !reflect.ValueOf(intermediate).IsNil()) || !reflect.ValueOf(intermediate).IsZero() {
iiid, iok := intermediate.(HasID) iiid, iok := intermediate.(HasID)
@ -164,6 +200,8 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
} }
default: default:
isNotStructOrSlice = true isNotStructOrSlice = true
if r.exists {
tto := r.HydratedType tto := r.HydratedType
if tto.Kind() == reflect.Pointer || tto.Kind() == reflect.Slice { if tto.Kind() == reflect.Pointer || tto.Kind() == reflect.Slice {
tto = tto.Elem() tto = tto.Elem()
@ -190,7 +228,6 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
} else { } else {
hatred.Set(reflect.ValueOf(t).Elem()) hatred.Set(reflect.ValueOf(t).Elem())
} }
recover()
} else { } else {
hatred.Set(reflect.ValueOf(t)) hatred.Set(reflect.ValueOf(t))
} }
@ -201,6 +238,7 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
t.SetSelf(t) t.SetSelf(t)
t.setExists(true) t.setExists(true)
} }
}
if toReturn == nil { if toReturn == nil {
sidoc, sok := rv.Interface().(IDocument) sidoc, sok := rv.Interface().(IDocument)
@ -209,6 +247,8 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
if !isNotStructOrSlice { if !isNotStructOrSlice {
sidoc.setRaw(rawDoc) sidoc.setRaw(rawDoc)
} }
} else if rv.Kind() == reflect.Pointer && rt.Kind() != reflect.Pointer {
rv = rv.Elem()
} }
return rv.Interface() return rv.Interface()
} }
@ -254,50 +294,20 @@ func (q *Query) LoadFile(fields ...string) *Query {
return q return q
} }
func readFields(field string, m *Model) (Reference, string) { func readFields(field string, m *Model) map[string]Reference {
var r Reference r := make(map[string]Reference)
var keptKey string
if m == nil { if m == nil {
return r, keptKey return r
} }
for k2, v := range m.references { for k, v := range m.references {
if strings.HasPrefix(k2, field) { if strings.HasPrefix(field, k) {
r = v r[k] = 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
} }
} }
if vv, ok := m.references[field]; ok {
r[field] = vv
} }
} return r
} else if ok {
r = m.references[field]
break
}
if keptKey != "" {
break
}
}
return r, keptKey
} }
// Populate populates document references via reflection // Populate populates document references via reflection

View File

@ -164,6 +164,14 @@ func incrementInterface(t interface{}) interface{} {
return t 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 { func pull(s reflect.Value, idx int, typ reflect.Type) reflect.Value {
retI := reflect.New(reflect.SliceOf(typ)) retI := reflect.New(reflect.SliceOf(typ))
for i := 0; i < s.Len(); i++ { for i := 0; i < s.Len(); i++ {