implement more bug fixes and improvements for populating deeply nested refs
This commit is contained in:
parent
378b5d756a
commit
6e865b733d
@ -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())
|
||||
}
|
||||
|
120
query.go
120
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,6 +200,8 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
|
||||
}
|
||||
default:
|
||||
isNotStructOrSlice = true
|
||||
|
||||
if r.exists {
|
||||
tto := r.HydratedType
|
||||
if tto.Kind() == reflect.Pointer || tto.Kind() == reflect.Slice {
|
||||
tto = tto.Elem()
|
||||
@ -190,7 +228,6 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
|
||||
} else {
|
||||
hatred.Set(reflect.ValueOf(t).Elem())
|
||||
}
|
||||
recover()
|
||||
} else {
|
||||
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.setExists(true)
|
||||
}
|
||||
}
|
||||
|
||||
if toReturn == nil {
|
||||
sidoc, sok := rv.Interface().(IDocument)
|
||||
@ -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
|
||||
for k, v := range m.references {
|
||||
if strings.HasPrefix(field, k) {
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
if vv, ok := m.references[field]; ok {
|
||||
r[field] = vv
|
||||
}
|
||||
}
|
||||
} else if ok {
|
||||
r = m.references[field]
|
||||
break
|
||||
}
|
||||
if keptKey != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
return r, keptKey
|
||||
return r
|
||||
}
|
||||
|
||||
// Populate populates document references via reflection
|
||||
|
8
util.go
8
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++ {
|
||||
|
Loading…
x
Reference in New Issue
Block a user