package orm import ( "encoding/json" "fmt" "go.mongodb.org/mongo-driver/v2/bson" "log" "reflect" "strings" "time" ) type Document struct { // Created time. updated/added automatically. Created time.Time `bson:"createdAt" json:"createdAt" tstype:"Date"` // Modified time. updated/added automatically. Modified time.Time `bson:"updatedAt" json:"updatedAt" tstype:"Date"` model *Model `bson:"-"` exists bool `bson:"-"` self any `bson:"-"` raw 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() var arr []interface{} reified := rerere(tmpV, typ, true) if ta, ok := reified.(bson.A); ok { arr = []interface{}(ta) } else { arr = reified.([]interface{}) } fmt.Println(len(arr)) break case map[string]interface{}: tmpV := make(bson.M) err = json.Unmarshal(bytes, &tmpV) typ := reflect.PointerTo(d.model.Type) self := reflect.ValueOf(d.self) nself := reflect.NewAt(typ.Elem(), self.UnsafePointer()) reified := rerere(tmpV, typ, true) nself.Elem().Set(reflect.ValueOf(reified).Elem()) d.self = nself.Interface() 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 Swap(field string, i, j int) error Delete() error Remove() error Save() error SaveWith(opts *SaveOptions) error Populate(fields ...string) SetSelf(arg interface{}) getExists() bool setExists(n bool) setModified(Modified time.Time) setCreated(Modified time.Time) getModified() time.Time getCreated() time.Time serializeToStore() any getModel() *Model setModel(m Model) markPopulated(field string) markDepopulated(field string) newPopulationMap() getPopulated() map[string]bool getRaw() any setRaw(raw any) } type SaveOptions struct { SetTimestamps bool } func (d *Document) getCreated() time.Time { return d.Created } func (d *Document) setCreated(Created time.Time) { d.Created = Created } func (d *Document) getModified() time.Time { return d.Modified } func (d *Document) setModified(Modified time.Time) { d.Modified = Modified } // SetSelf - don't call this lol func (d *Document) SetSelf(arg interface{}) { d.self = arg } func (d *Document) getModel() *Model { return d.model } func (d *Document) setModel(m Model) { d.model = &m } func (d *Document) getExists() bool { return d.exists } func (d *Document) setExists(n bool) { d.exists = n } // Delete - deletes a model instance from the database func (d *Document) Delete() error { var err error val := valueOf(d.self) if val.Kind() == reflect.Slice { for i := 0; i < val.Len(); i++ { cur := val.Index(i) if err = doDelete(d, cur.Interface()); err != nil { return err } } return nil } else { return doDelete(d, d.self) } } // Remove - alias for Delete func (d *Document) Remove() error { return d.Delete() } // SaveWith - updates this Model in the database, // or inserts it if it doesn't exist, using the provided // SaveOptions func (d *Document) SaveWith(opts *SaveOptions) error { val := valueOf(d.self) if val.Kind() == reflect.Slice { for i := 0; i < val.Len(); i++ { cur := val.Index(i) if err := doSave(d.model.getColl(), !d.exists, opts, cur.Interface()); err != nil { return err } } return nil } else { return doSave(d.model.getColl(), !d.exists, opts, d.self) } } // Save - updates this Model in the database, // or inserts it if it doesn't exist, using // default SaveOptions func (d *Document) Save() error { return d.SaveWith(&SaveOptions{ SetTimestamps: true, }) } func (d *Document) serializeToStore() any { return serializeIDs((d).self, false, d.populatedFields, "") } // Append appends one or more items to `field`. // will error if this Model contains a reference // to multiple documents, or if `field` is not a // slice. func (d *Document) Append(field string, a ...interface{}) error { var d0 IDocument = d d0.getCreated() rv := reflect.ValueOf(d.self) selfRef := rv rt := reflect.TypeOf(d.self) if selfRef.Kind() == reflect.Pointer { selfRef = selfRef.Elem() rt = rt.Elem() } if err := checkStruct(selfRef); err != nil { return err } _, origV, err := getNested(field, selfRef) if err != nil { return err } origRef := makeSettable(*origV, (*origV).Interface()) fv := origRef if fv.Kind() == reflect.Pointer { fv = fv.Elem() } if fv.Kind() != reflect.Slice { return ErrNotASlice } for _, b := range a { val := reflect.ValueOf(incrementTagged(b)) fv.Set(reflect.Append(fv, val)) } return nil } // Pull - removes elements from the subdocument slice stored in `field`. func (d *Document) Pull(field string, a ...any) error { rv := reflect.ValueOf(d.self) selfRef := rv rt := reflect.TypeOf(d.self) if selfRef.Kind() == reflect.Pointer { selfRef = selfRef.Elem() rt = rt.Elem() } if err := checkStruct(selfRef); err != nil { return err } _, origV, err := getNested(field, selfRef) if err != nil { return err } origRef := makeSettable(*origV, (*origV).Interface()) fv := origRef if fv.Kind() == reflect.Pointer { fv = fv.Elem() } if fv.Kind() != reflect.Slice { return ErrNotASlice } for _, b := range a { inner: for i := 0; i < fv.Len(); i++ { if reflect.DeepEqual(b, fv.Index(i).Interface()) { fv.Set(pull(fv, i, fv.Index(i).Type())) break inner } } } return nil } // Swap - swaps the elements at indexes `i` and `j` in the // slice stored at `field` func (d *Document) Swap(field string, i, j int) error { rv := reflect.ValueOf(d.self) selfRef := rv rt := reflect.TypeOf(d.self) if selfRef.Kind() == reflect.Pointer { selfRef = selfRef.Elem() rt = rt.Elem() } if err := checkStruct(selfRef); err != nil { return err } _, origV, err := getNested(field, selfRef) if err != nil { return err } origRef := makeSettable(*origV, (*origV).Interface()) fv := origRef if fv.Kind() == reflect.Pointer { fv = fv.Elem() } if err = checkSlice(fv); err != nil { return err } if i >= fv.Len() || j >= fv.Len() { return ErrOutOfBounds } oi := fv.Index(i).Interface() oj := fv.Index(j).Interface() fv.Index(i).Set(reflect.ValueOf(oj)) fv.Index(j).Set(reflect.ValueOf(oi)) return nil } func (d *Document) Populate(fields ...string) { _, cm, _ := ModelRegistry.HasByName(d.model.typeName) if cm != nil { rawDoc := d.raw for _, field := range fields { // 0 = fieldname, 1 = typename, 2 = bson name r, refOk := cm.references[field] if refOk { // get self // get ptr // find // unmarshal... htt := r.HydratedType if htt.Kind() == reflect.Pointer || htt.Kind() == reflect.Slice { htt = htt.Elem() } if strings.HasSuffix(field, ".") || strings.HasPrefix(field, ".") { log.Printf("WARN: invalid field name passed to Populate(). skipping...\n") continue } tto := r.HydratedType if tto.Kind() == reflect.Pointer || tto.Kind() == reflect.Slice { tto = tto.Elem() } _, refColl, _ := ModelRegistry.HasByName(tto.Name()) var tmp1 interface{} asIDocument, docOk := d.self.(IDocument) if docOk { asIDocument.markPopulated(field) } v := reflect.ValueOf(d.self) tt := v.Elem().Type() tmp1 = populate(r, d.populatedFields, refColl.collection, rawDoc, field, d.self) nv := reflect.NewAt(tt, v.UnsafePointer()) nv.Elem().Set(reflect.ValueOf(tmp1).Elem()) } } } }