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)
|
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
120
query.go
@ -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
|
||||||
|
8
util.go
8
util.go
@ -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++ {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user