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()) | ||||
| 			} | ||||
|  | ||||
							
								
								
									
										186
									
								
								query.go
									
									
									
									
									
								
							
							
						
						
									
										186
									
								
								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
 | ||||
|  | ||||
							
								
								
									
										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