From 4de1c20938bff4a71ac90156eeaf1a034df496f2 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?= <i.am.the.tablet@proton.me>
Date: Sun, 6 Apr 2025 15:47:21 -0400
Subject: [PATCH] THIS DOESN'T WORK. but i need to commit

---
 document.go           | 41 ++++++++++++++++++++++++++++---
 document_internals.go | 57 ++++++++++++++++++++++++++++++++++++-------
 go.mod                |  8 +++---
 go.sum                | 28 ++++++---------------
 model.go              |  5 ++--
 query.go              | 18 +++++++++++++-
 registry.go           |  5 ++++
 util.go               | 10 ++++++++
 8 files changed, 133 insertions(+), 39 deletions(-)

diff --git a/document.go b/document.go
index 3439e2a..f188729 100644
--- a/document.go
+++ b/document.go
@@ -1,6 +1,8 @@
 package orm
 
 import (
+	"encoding/json"
+	"go.mongodb.org/mongo-driver/v2/bson"
 	"reflect"
 	"time"
 )
@@ -15,6 +17,36 @@ type Document struct {
 	self            any             `bson:"-"`
 	populatedFields map[string]bool `bson:"-"`
 }
+
+func (d *Document) UnmarshalJSON(bytes []byte) error {
+	var fiv interface{}
+	if err := json.Unmarshal(bytes, &fiv); err != nil {
+		return err
+	}
+	var err error
+	switch fiv.(type) {
+	case []interface{}:
+		tmpV := make(bson.A, 0)
+		err = json.Unmarshal(bytes, &tmpV)
+		typ := reflect.SliceOf(d.model.Type)
+		//d.SetSelf()
+		rerere(tmpV, typ)
+		break
+	case map[string]interface{}:
+		tmpV := make(bson.M)
+		err = json.Unmarshal(bytes, &tmpV)
+		typ := d.model.Type
+		rerere(tmpV, typ)
+		break
+	}
+	return err
+}
+
+func (d *Document) MarshalJSON() ([]byte, error) {
+	v := serializeIDs((d).self, true, d.populatedFields, "")
+	return json.Marshal(v)
+}
+
 type IDocument interface {
 	Append(field string, a ...interface{}) error
 	Pull(field string, a ...any) error
@@ -23,7 +55,7 @@ type IDocument interface {
 	Remove() error
 	Save() error
 	SaveWith(opts *SaveOptions) error
-	setSelf(arg interface{})
+	SetSelf(arg interface{})
 	getExists() bool
 	setExists(n bool)
 	setModified(Modified time.Time)
@@ -35,6 +67,7 @@ type IDocument interface {
 	setModel(m Model)
 	markPopulated(field string)
 	markDepopulated(field string)
+	newPopulationMap()
 }
 
 type SaveOptions struct {
@@ -56,7 +89,9 @@ func (d *Document) getModified() time.Time {
 func (d *Document) setModified(Modified time.Time) {
 	d.Modified = Modified
 }
-func (d *Document) setSelf(arg interface{}) {
+
+// SetSelf - don't call this lol
+func (d *Document) SetSelf(arg interface{}) {
 	d.self = arg
 }
 
@@ -125,7 +160,7 @@ func (d *Document) Save() error {
 }
 
 func (d *Document) serializeToStore() any {
-	return serializeIDs((d).self)
+	return serializeIDs((d).self, false, d.populatedFields, "")
 }
 
 // Append appends one or more items to `field`.
diff --git a/document_internals.go b/document_internals.go
index 9584c76..34f5c31 100644
--- a/document_internals.go
+++ b/document_internals.go
@@ -12,8 +12,13 @@ import (
 	"time"
 )
 
-func serializeIDs(input interface{}) interface{} {
-
+func serializeIDs(input interface{}, isJson bool, populated map[string]bool, parent string) interface{} {
+	var key string
+	if isJson {
+		key = "json"
+	} else {
+		key = "bson"
+	}
 	vp := reflect.ValueOf(input)
 	mt := reflect.TypeOf(input)
 	var ret interface{}
@@ -46,9 +51,15 @@ func serializeIDs(input interface{}) interface{} {
 		for i := 0; i < vp.Elem().NumField(); i++ {
 			fv := vp.Elem().Field(i)
 			ft := mt.Field(i)
+			var descent string
+			if parent != "" {
+				descent = parent + "." + ft.Name
+			} else {
+				descent = ft.Name
+			}
 			tag, err := structtag.Parse(string(ft.Tag))
 			panik(err)
-			bbson, err := tag.Get("bson")
+			bbson, err := tag.Get(key)
 			if err != nil || bbson.Name == "-" {
 				continue
 			}
@@ -66,12 +77,25 @@ func serializeIDs(input interface{}) interface{} {
 					vp1.Elem().Set(reflect.ValueOf(fv.Interface()))
 					fv.Set(vp1.Elem())
 				}
+				var ip bool
+				for k1, v1 := range populated {
+					if k1 == parent {
+						ip = v1
+						break
+					}
+				}
 				if terr == nil {
 					ifc, ok := fv.Interface().(HasID)
 					if fv.Kind() == reflect.Slice {
 						rarr := bson.A{}
 						for j := 0; j < fv.Len(); j++ {
-							rarr = append(rarr, getID(fv.Index(j).Interface()))
+							if !isJson {
+								rarr = append(rarr, getID(fv.Index(j).Interface()))
+							} else {
+								if ip {
+									ret0[bbson.Name] = serializeIDs(fv.Index(j).Interface(), isJson, populated, descent)
+								}
+							}
 						}
 						ret0[bbson.Name] = rarr
 					} else if !ok {
@@ -80,15 +104,22 @@ func serializeIDs(input interface{}) interface{} {
 						if reflect.ValueOf(ifc).IsNil() {
 							ret0[bbson.Name] = nil
 						} else {
-							ret0[bbson.Name] = ifc.Id()
+							if !isJson {
+								ret0[bbson.Name] = ifc.Id()
+							} else {
+								if ip {
+									ret0[bbson.Name] = serializeIDs(fv.Interface(), isJson, populated, descent)
+								} else {
+									ret0[bbson.Name] = ifc.Id()
+								}
+							}
 						}
 					}
-
 				} else {
 					if fv.Type() == reflect.TypeFor[time.Time]() {
 						ret0[bbson.Name] = fv.Interface()
 					} else {
-						ret0[bbson.Name] = serializeIDs(fv.Interface())
+						ret0[bbson.Name] = serializeIDs(fv.Interface(), isJson, populated, descent)
 					}
 				}
 			}
@@ -99,7 +130,7 @@ func serializeIDs(input interface{}) interface{} {
 		ret0 := bson.A{}
 		for i := 0; i < vp.Elem().Len(); i++ {
 
-			ret0 = append(ret0, serializeIDs(vp.Elem().Index(i).Addr().Interface()))
+			ret0 = append(ret0, serializeIDs(vp.Elem().Index(i).Addr().Interface(), isJson, populated, parent))
 		}
 		ret = ret0
 	default:
@@ -113,7 +144,7 @@ func doSave(c *mongo.Collection, isNew bool, opts *SaveOptions, arg interface{})
 	if !ok {
 		return fmt.Errorf(errFmtNotAModel, nameOf(arg))
 	}
-	d.setSelf(d)
+	d.SetSelf(d)
 	now := time.Now()
 	selfo := reflect.ValueOf(d)
 	vp := selfo
@@ -268,9 +299,17 @@ func incrementAll(item interface{}) {
 }
 
 func (d *Document) markPopulated(field string) {
+	d.newPopulationMap()
 	d.populatedFields[field] = true
 }
 
 func (d *Document) markDepopulated(field string) {
+	d.newPopulationMap()
 	d.populatedFields[field] = false
 }
+
+func (d *Document) newPopulationMap() {
+	if d.populatedFields == nil {
+		d.populatedFields = make(map[string]bool)
+	}
+}
diff --git a/go.mod b/go.mod
index 38a8888..1b4135b 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module rockfic.com/orm
 
-go 1.23
+go 1.24.1
 
 require (
 	github.com/fatih/structtag v1.2.0
@@ -23,9 +23,9 @@ require (
 	github.com/xdg-go/scram v1.1.2 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
-	golang.org/x/crypto v0.33.0 // indirect
-	golang.org/x/sync v0.11.0 // indirect
-	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/crypto v0.36.0 // indirect
+	golang.org/x/sync v0.12.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
 )
 
 replace rockfic.com/orm => C:/rockfic/orm
diff --git a/go.sum b/go.sum
index 0207bac..2a487c1 100644
--- a/go.sum
+++ b/go.sum
@@ -8,14 +8,10 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
-github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
 github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
@@ -27,28 +23,22 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi
 github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
 github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-go.mongodb.org/mongo-driver/v2 v2.0.0-beta2 h1:PRtbRKwblE8ZfI8qOhofcjn9y8CmKZI7trS5vDMeJX0=
-go.mongodb.org/mongo-driver/v2 v2.0.0-beta2/go.mod h1:UGLb3ZgEzaY0cCbJpH9UFt9B6gEXiTPzsnJS38nBeoU=
 go.mongodb.org/mongo-driver/v2 v2.1.0 h1:/ELnVNjmfUKDsoBisXxuJL0noR9CfeUIrP7Yt3R+egg=
 go.mongodb.org/mongo-driver/v2 v2.1.0/go.mod h1:AWiLRShSrk5RHQS3AEn3RL19rqOzVq49MCpWQ3x/huI=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
-golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
-golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
-golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
+golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
+golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
-golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
+golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
+golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
-golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -60,10 +50,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
-golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
-golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
-golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
diff --git a/model.go b/model.go
index fb25cae..03d8db2 100644
--- a/model.go
+++ b/model.go
@@ -189,7 +189,7 @@ func (m *Model) FindOne(query interface{}, options *options.FindOneOptionsBuilde
 		idoc.setExists(true)
 		idoc.setModel(*m)
 	}
-	idoc.setSelf(idoc)
+	idoc.SetSelf(idoc)
 	return qq, err
 }
 
@@ -224,6 +224,7 @@ func createBase(d any) (reflect.Value, int, string) {
 	}
 	ri.setTypeName(n)
 	r.Interface().(IDocument).setModel(*ri)
+	r.Interface().(IDocument).newPopulationMap()
 
 	return r, i, n
 }
@@ -237,7 +238,7 @@ func Create(d any) any {
 	dm.getModel().setTypeName(n)
 	what := r.Interface()
 
-	dm.setSelf(what)
+	dm.SetSelf(what)
 	//df.Set(reflect.ValueOf(dm))
 	return what
 }
diff --git a/query.go b/query.go
index c12ebe7..2addf8a 100644
--- a/query.go
+++ b/query.go
@@ -3,6 +3,7 @@ package orm
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"github.com/fatih/structtag"
 	"go.mongodb.org/mongo-driver/v2/bson"
@@ -157,7 +158,9 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
 		t := rawt.(IDocument)
 		q := bson.M{"_id": rawDoc}
 		reso := DB.Collection(rcoll).FindOne(context.TODO(), q)
-		reso.Decode(t)
+		if !errors.Is(reso.Err(), mongo.ErrNoDocuments) {
+			reso.Decode(t)
+		}
 		hatred := rv
 		if hatred.Kind() == reflect.Pointer {
 			hatred = hatred.Elem()
@@ -174,12 +177,21 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
 			src = t
 			toReturn = src
 		}
+		t.SetSelf(t)
 		t.setExists(true)
 	}
 
 	if toReturn == nil {
+		sidoc, sok := rv.Interface().(IDocument)
+		if sok {
+			sidoc.SetSelf(rv.Interface())
+		}
 		return rv.Interface()
 	}
+	sidoc, sok := src.(IDocument)
+	if sok {
+		sidoc.SetSelf(src)
+	}
 	return src
 }
 
@@ -271,6 +283,10 @@ func (q *Query) Populate(fields ...string) *Query {
 					}
 					tmp1 = slic.Interface()
 				} else {
+					asIDocument, docOk := q.doc.(IDocument)
+					if docOk {
+						asIDocument.markPopulated(field)
+					}
 					tmp1 = populate(r, refColl.collection, rawDoc, field, reflect.ValueOf(q.doc).Interface())
 				}
 				q.doc = tmp1
diff --git a/registry.go b/registry.go
index 8fc566b..c389f6f 100644
--- a/registry.go
+++ b/registry.go
@@ -230,9 +230,14 @@ func (r TModelRegistry) new_(n string) interface{} {
 		df := v.Elem().Field(m.idx)
 		do := reflect.New(df.Type())
 		d := do.Interface().(IDocument)
+		d.newPopulationMap()
 		//d := df.Interface().(IDocument)
+		for k := range m.references {
+			d.markDepopulated(k)
+		}
 		d.setModel(*m)
 		d.getModel().typeName = name
+		d.SetSelf(do.Interface())
 		df.Set(reflect.ValueOf(d).Elem())
 		return v.Interface()
 	}
diff --git a/util.go b/util.go
index b03567d..bbef80c 100644
--- a/util.go
+++ b/util.go
@@ -218,3 +218,13 @@ func normalizeSliceToDocumentSlice(in any) *DocumentSlice {
 	}
 	return &ret
 }
+
+func filterMap[k comparable, v any](input map[k]v, pred func(key k, val v) bool) map[k]v {
+	ret := make(map[k]v)
+	for k1, v1 := range input {
+		if pred(k1, v1) {
+			ret[k1] = v1
+		}
+	}
+	return ret
+}