From 6e865b733d681d4fea84c002259933fe02ba3660 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: Fri, 11 Apr 2025 23:05:14 -0400 Subject: [PATCH] implement more bug fixes and improvements for populating deeply nested refs --- document.go | 3 +- query.go | 186 +++++++++++++++++++++++++++------------------------- util.go | 8 +++ 3 files changed, 108 insertions(+), 89 deletions(-) diff --git a/document.go b/document.go index 9994363..1fb604d 100644 --- a/document.go +++ b/document.go @@ -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()) } diff --git a/query.go b/query.go index 0d6cab5..771da38 100644 --- a/query.go +++ b/query.go @@ -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 diff --git a/util.go b/util.go index bbef80c..5b5cf57 100644 --- a/util.go +++ b/util.go @@ -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++ {