diamond-orm/document.go

342 lines
7.7 KiB
Go

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 := range val.Len() {
cur := val.Index(i)
if err := doSave(d.model.Collection(), !d.exists, opts, cur.Interface()); err != nil {
return err
}
}
return nil
} else {
return doSave(d.model.Collection(), !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())
}
}
}
}