overhaul models to be more sensible by separating "instance" logic into a separate Document type ✨🪷
This commit is contained in:
parent
bcf3985360
commit
fcd7cb2013
223
document.go
Normal file
223
document.go
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Document struct {
|
||||||
|
// Created time. updated/added automatically.
|
||||||
|
Created time.Time `bson:"createdAt" json:"createdAt"`
|
||||||
|
// Modified time. updated/added automatically.
|
||||||
|
Modified time.Time `bson:"updatedAt" json:"updatedAt"`
|
||||||
|
model *Model
|
||||||
|
exists bool `bson:"-"`
|
||||||
|
self any `bson:"-"`
|
||||||
|
}
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save - updates this Model in the database,
|
||||||
|
// or inserts it if it doesn't exist
|
||||||
|
func (d *Document) Save() 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, cur.Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return doSave(d.model.getColl(), !d.exists, d.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Document) serializeToStore() any {
|
||||||
|
return serializeIDs((d).self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
outer:
|
||||||
|
for _, b := range a {
|
||||||
|
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 outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
@ -125,25 +125,25 @@ func serializeIDs(input interface{}) interface{} {
|
|||||||
}
|
}
|
||||||
func doSave(c *mongo.Collection, isNew bool, arg interface{}) error {
|
func doSave(c *mongo.Collection, isNew bool, arg interface{}) error {
|
||||||
var err error
|
var err error
|
||||||
m, ok := arg.(IModel)
|
d, ok := arg.(IDocument)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf(errFmtNotAModel, nameOf(arg))
|
return fmt.Errorf(errFmtNotAModel, nameOf(arg))
|
||||||
}
|
}
|
||||||
m.setSelf(m)
|
d.setSelf(d)
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
selfo := reflect.ValueOf(m)
|
selfo := reflect.ValueOf(d)
|
||||||
vp := selfo
|
vp := selfo
|
||||||
if vp.Kind() != reflect.Ptr {
|
if vp.Kind() != reflect.Ptr {
|
||||||
vp = reflect.New(selfo.Type())
|
vp = reflect.New(selfo.Type())
|
||||||
vp.Elem().Set(selfo)
|
vp.Elem().Set(selfo)
|
||||||
}
|
}
|
||||||
var asHasId = vp.Interface().(HasID)
|
var asHasId = vp.Interface().(HasID)
|
||||||
var asModel = vp.Interface().(IModel)
|
var asModel = vp.Interface().(IDocument)
|
||||||
if isNew {
|
if isNew {
|
||||||
m.setCreated(now)
|
d.setCreated(now)
|
||||||
}
|
}
|
||||||
m.setModified(now)
|
d.setModified(now)
|
||||||
idxs := m.getIdxs()
|
idxs := d.getModel().getIdxs()
|
||||||
for _, i := range idxs {
|
for _, i := range idxs {
|
||||||
_, err = c.Indexes().CreateOne(context.TODO(), *i)
|
_, err = c.Indexes().CreateOne(context.TODO(), *i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -157,28 +157,28 @@ func doSave(c *mongo.Collection, isNew bool, arg interface{}) error {
|
|||||||
(asHasId).SetId(pnid)
|
(asHasId).SetId(pnid)
|
||||||
}
|
}
|
||||||
incrementAll(asHasId)
|
incrementAll(asHasId)
|
||||||
_, im, _ := ModelRegistry.HasByName(asModel.getTypeName())
|
_, im, _ := ModelRegistry.HasByName(asModel.getModel().getTypeName())
|
||||||
_ = gridFsSave(asHasId, *im)
|
_ = gridFsSave(asHasId, *im)
|
||||||
|
|
||||||
_, err = c.InsertOne(context.TODO(), m.serializeToStore())
|
_, err = c.InsertOne(context.TODO(), d.serializeToStore())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m.setExists(true)
|
d.setExists(true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = c.ReplaceOne(context.TODO(), bson.D{{Key: "_id", Value: m.(HasID).Id()}}, m.serializeToStore())
|
_, err = c.ReplaceOne(context.TODO(), bson.D{{Key: "_id", Value: d.(HasID).Id()}}, d.serializeToStore())
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
func doDelete(m *Model, arg interface{}) error {
|
func doDelete(d *Document, arg interface{}) error {
|
||||||
self, ok := arg.(HasID)
|
self, ok := arg.(HasID)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf(errFmtNotHasID, nameOf(arg))
|
return fmt.Errorf(errFmtNotHasID, nameOf(arg))
|
||||||
}
|
}
|
||||||
c := m.getColl()
|
c := d.model.getColl()
|
||||||
_, err := c.DeleteOne(context.TODO(), bson.M{"_id": self.Id()})
|
_, err := c.DeleteOne(context.TODO(), bson.M{"_id": self.Id()})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m.exists = false
|
d.exists = false
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
47
document_slice.go
Normal file
47
document_slice.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
type IDocumentSlice interface {
|
||||||
|
Delete() error
|
||||||
|
Remove() error
|
||||||
|
Save() error
|
||||||
|
setExists(n bool)
|
||||||
|
getModel() *Model
|
||||||
|
}
|
||||||
|
|
||||||
|
type DocumentSlice []IDocument
|
||||||
|
|
||||||
|
func (d *DocumentSlice) Delete() error {
|
||||||
|
var err error
|
||||||
|
for _, doc := range *d {
|
||||||
|
err = doc.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DocumentSlice) Remove() error {
|
||||||
|
return d.Delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DocumentSlice) Save() error {
|
||||||
|
var err error
|
||||||
|
for _, doc := range *d {
|
||||||
|
err = doc.Save()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DocumentSlice) setExists(b bool) {
|
||||||
|
for _, s := range *d {
|
||||||
|
s.setExists(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DocumentSlice) getModel() *Model {
|
||||||
|
return (*d)[0].getModel()
|
||||||
|
}
|
@ -107,7 +107,7 @@ func gridFsLoad(val any, g GridFSReference, field string) any {
|
|||||||
return doc.Interface()
|
return doc.Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
func gridFsSave(val any, imodel InternalModel) error {
|
func gridFsSave(val any, imodel Model) error {
|
||||||
var rerr error
|
var rerr error
|
||||||
v := reflect.ValueOf(val)
|
v := reflect.ValueOf(val)
|
||||||
el := v
|
el := v
|
||||||
|
250
model.go
250
model.go
@ -3,39 +3,22 @@ package orm
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Model - "base" struct for all queryable models
|
// Model - "base" struct for all queryable models
|
||||||
type Model struct {
|
type Model struct {
|
||||||
// Created time. updated/added automatically.
|
typeName string `bson:"-"`
|
||||||
Created time.Time `bson:"createdAt" json:"createdAt"`
|
Idx int
|
||||||
// Modified time. updated/added automatically.
|
Type reflect.Type
|
||||||
Modified time.Time `bson:"updatedAt" json:"updatedAt"`
|
Collection string
|
||||||
typeName string `bson:"-"`
|
References map[string]Reference
|
||||||
self any `bson:"-"`
|
Indexes map[string][]InternalIndex
|
||||||
exists bool `bson:"-"`
|
GridFSReferences map[string]GridFSReference
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) getCreated() time.Time {
|
|
||||||
return m.Created
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) setCreated(Created time.Time) {
|
|
||||||
m.Created = Created
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) getModified() time.Time {
|
|
||||||
return m.Modified
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) setModified(Modified time.Time) {
|
|
||||||
m.Modified = Modified
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasID is a simple interface that you must implement
|
// HasID is a simple interface that you must implement
|
||||||
@ -52,29 +35,17 @@ type HasID interface {
|
|||||||
type HasIDSlice []HasID
|
type HasIDSlice []HasID
|
||||||
|
|
||||||
type IModel interface {
|
type IModel interface {
|
||||||
Append(field string, a ...interface{}) error
|
|
||||||
Delete() error
|
|
||||||
FindRaw(query interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error)
|
FindRaw(query interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error)
|
||||||
Find(query interface{}, opts ...*options.FindOptions) (*Query, error)
|
Find(query interface{}, opts ...*options.FindOptions) (*Query, error)
|
||||||
FindByID(id interface{}) (*Query, error)
|
FindByID(id interface{}) (*Query, error)
|
||||||
FindOne(query interface{}, options ...*options.FindOneOptions) (*Query, error)
|
FindOne(query interface{}, options ...*options.FindOneOptions) (*Query, error)
|
||||||
FindPaged(query interface{}, page int64, perPage int64, options ...*options.FindOptions) (*Query, error)
|
FindPaged(query interface{}, page int64, perPage int64, options ...*options.FindOptions) (*Query, error)
|
||||||
Pull(field string, a ...any) error
|
|
||||||
Remove() error
|
|
||||||
Save() error
|
|
||||||
getColl() *mongo.Collection
|
getColl() *mongo.Collection
|
||||||
getIdxs() []*mongo.IndexModel
|
getIdxs() []*mongo.IndexModel
|
||||||
getParsedIdxs() map[string][]InternalIndex
|
getParsedIdxs() map[string][]InternalIndex
|
||||||
serializeToStore() any
|
|
||||||
getTypeName() string
|
getTypeName() string
|
||||||
setTypeName(str string)
|
setTypeName(str string)
|
||||||
getExists() bool
|
|
||||||
setExists(n bool)
|
|
||||||
setModified(Modified time.Time)
|
|
||||||
setCreated(Modified time.Time)
|
|
||||||
getModified() time.Time
|
|
||||||
getCreated() time.Time
|
|
||||||
setSelf(arg interface{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) getTypeName() string {
|
func (m *Model) getTypeName() string {
|
||||||
@ -85,17 +56,6 @@ func (m *Model) setTypeName(str string) {
|
|||||||
m.typeName = str
|
m.typeName = str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) setSelf(arg interface{}) {
|
|
||||||
m.self = arg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) getExists() bool {
|
|
||||||
return m.exists
|
|
||||||
}
|
|
||||||
func (m *Model) setExists(n bool) {
|
|
||||||
m.exists = n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) getColl() *mongo.Collection {
|
func (m *Model) getColl() *mongo.Collection {
|
||||||
_, ri, ok := ModelRegistry.HasByName(m.typeName)
|
_, ri, ok := ModelRegistry.HasByName(m.typeName)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -146,12 +106,12 @@ func (m *Model) Find(query interface{}, opts ...*options.FindOptions) (*Query, e
|
|||||||
op: OP_FIND_ALL,
|
op: OP_FIND_ALL,
|
||||||
}
|
}
|
||||||
q, err := m.FindRaw(query, opts...)
|
q, err := m.FindRaw(query, opts...)
|
||||||
|
idoc := (*DocumentSlice)(unsafe.Pointer(qqv.Elem().UnsafeAddr()))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rawRes := bson.A{}
|
rawRes := bson.A{}
|
||||||
err = q.All(context.TODO(), &rawRes)
|
err = q.All(context.TODO(), &rawRes)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m.exists = true
|
idoc.setExists(true)
|
||||||
}
|
}
|
||||||
qq.rawDoc = rawRes
|
qq.rawDoc = rawRes
|
||||||
err = q.All(context.TODO(), &qq.doc)
|
err = q.All(context.TODO(), &qq.doc)
|
||||||
@ -195,14 +155,16 @@ func (m *Model) FindOne(query interface{}, options ...*options.FindOneOptions) (
|
|||||||
raw := bson.M{}
|
raw := bson.M{}
|
||||||
err := rip.Decode(&raw)
|
err := rip.Decode(&raw)
|
||||||
panik(err)
|
panik(err)
|
||||||
m.exists = true
|
|
||||||
qqn := ModelRegistry.new_(m.typeName)
|
qqn := ModelRegistry.new_(m.typeName)
|
||||||
v := reflect.New(reflect.TypeOf(qqn))
|
idoc, ok := qqn.(IDocument)
|
||||||
v.Elem().Set(reflect.ValueOf(qqn))
|
if ok {
|
||||||
|
idoc.setExists(true)
|
||||||
|
}
|
||||||
|
|
||||||
qq := &Query{
|
qq := &Query{
|
||||||
collection: m.getColl(),
|
collection: m.getColl(),
|
||||||
rawDoc: raw,
|
rawDoc: raw,
|
||||||
doc: v.Elem().Interface(),
|
doc: idoc,
|
||||||
op: OP_FIND_ONE,
|
op: OP_FIND_ONE,
|
||||||
model: m,
|
model: m,
|
||||||
}
|
}
|
||||||
@ -212,166 +174,13 @@ func (m *Model) FindOne(query interface{}, options ...*options.FindOneOptions) (
|
|||||||
qq.reOrganize()
|
qq.reOrganize()
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
m.self = qq.doc
|
idoc.setSelf(idoc)
|
||||||
return qq, err
|
return qq, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete - deletes a model instance from the database
|
|
||||||
func (m *Model) Delete() error {
|
|
||||||
var err error
|
|
||||||
val := valueOf(m.self)
|
|
||||||
if val.Kind() == reflect.Slice {
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
|
||||||
cur := val.Index(i)
|
|
||||||
if err = doDelete(m, cur.Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return doDelete(m, m.self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove - alias for Delete
|
|
||||||
func (m *Model) Remove() error {
|
|
||||||
return m.Delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (m *Model) Append(field string, a ...interface{}) error {
|
|
||||||
rv := reflect.ValueOf(m.self)
|
|
||||||
selfRef := rv
|
|
||||||
rt := reflect.TypeOf(m.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 (m *Model) Pull(field string, a ...any) error {
|
|
||||||
rv := reflect.ValueOf(m.self)
|
|
||||||
selfRef := rv
|
|
||||||
rt := reflect.TypeOf(m.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
|
|
||||||
}
|
|
||||||
outer:
|
|
||||||
for _, b := range a {
|
|
||||||
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 outer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap - swaps the elements at indexes `i` and `j` in the
|
|
||||||
// slice stored at `field`
|
|
||||||
func (m *Model) Swap(field string, i, j int) error {
|
|
||||||
rv := reflect.ValueOf(m.self)
|
|
||||||
selfRef := rv
|
|
||||||
rt := reflect.TypeOf(m.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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save - updates this Model in the database,
|
|
||||||
// or inserts it if it doesn't exist
|
|
||||||
func (m *Model) Save() error {
|
|
||||||
val := valueOf(m.self)
|
|
||||||
if val.Kind() == reflect.Slice {
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
|
||||||
cur := val.Index(i)
|
|
||||||
if err := doSave(m.getColl(), !m.exists, cur.Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return doSave(m.getColl(), !m.exists, m.self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) serializeToStore() any {
|
|
||||||
return serializeIDs((m).self)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createBase(d any) (reflect.Value, int, string) {
|
func createBase(d any) (reflect.Value, int, string) {
|
||||||
var n string
|
var n string
|
||||||
var ri *InternalModel
|
var ri *Model
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
n, ri, ok = ModelRegistry.HasByName(nameOf(d))
|
n, ri, ok = ModelRegistry.HasByName(nameOf(d))
|
||||||
@ -393,20 +202,23 @@ func createBase(d any) (reflect.Value, int, string) {
|
|||||||
} else {
|
} else {
|
||||||
r.Elem().Set(reflect.ValueOf(d))
|
r.Elem().Set(reflect.ValueOf(d))
|
||||||
}
|
}
|
||||||
|
ri.setTypeName(n)
|
||||||
|
r.Interface().(IDocument).setModel(*ri)
|
||||||
|
|
||||||
return r, i, n
|
return r, i, n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new instance of a given model
|
// Create creates a new instance of a given Document
|
||||||
// and returns a pointer to it.
|
// type and returns a pointer to it.
|
||||||
func Create(d any) any {
|
func Create(d any) any {
|
||||||
r, i, n := createBase(d)
|
r, _, n := createBase(d)
|
||||||
df := r.Elem().Field(i)
|
//df := r.Elem().Field(i)
|
||||||
dm := df.Interface().(Model)
|
dm := r.Interface().(IDocument)
|
||||||
dm.typeName = n
|
dm.getModel().setTypeName(n)
|
||||||
what := r.Interface()
|
what := r.Interface()
|
||||||
dm.self = what
|
|
||||||
df.Set(reflect.ValueOf(dm))
|
dm.setSelf(what)
|
||||||
|
//df.Set(reflect.ValueOf(dm))
|
||||||
return what
|
return what
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,9 +11,10 @@ import (
|
|||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
doc := Create(iti_single()).(*story)
|
is := iti_single()
|
||||||
assert.Equal(t, iti_single().Title, doc.Title)
|
doc := Create(is).(*story)
|
||||||
assert.Equal(t, iti_single().Chapters[0].Summary, doc.Chapters[0].Summary)
|
assert.Equal(t, is.Title, doc.Title)
|
||||||
|
assert.Equal(t, is.Chapters[0].Summary, doc.Chapters[0].Summary)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSave(t *testing.T) {
|
func TestSave(t *testing.T) {
|
||||||
@ -46,7 +47,7 @@ func TestPopulate(t *testing.T) {
|
|||||||
saveDoc(t, storyDoc)
|
saveDoc(t, storyDoc)
|
||||||
assert.Greater(t, storyDoc.ID, int64(0))
|
assert.Greater(t, storyDoc.ID, int64(0))
|
||||||
|
|
||||||
smodel := Create(story{}).(*story)
|
smodel := ModelRegistry["story"]
|
||||||
q, err := smodel.FindByID(storyDoc.ID)
|
q, err := smodel.FindByID(storyDoc.ID)
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.NotPanics(t, func() {
|
assert.NotPanics(t, func() {
|
||||||
@ -67,7 +68,7 @@ func TestUpdate(t *testing.T) {
|
|||||||
nb.Locked = true
|
nb.Locked = true
|
||||||
saveDoc(t, nb)
|
saveDoc(t, nb)
|
||||||
|
|
||||||
foundM := Create(band{}).(*band)
|
foundM := ModelRegistry["band"]
|
||||||
q, err := foundM.FindByID(int64(1))
|
q, err := foundM.FindByID(int64(1))
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
found := &band{}
|
found := &band{}
|
||||||
@ -81,7 +82,7 @@ func TestModel_FindAll(t *testing.T) {
|
|||||||
initTest()
|
initTest()
|
||||||
im := iti_multi()
|
im := iti_multi()
|
||||||
createAndSave(t, &im)
|
createAndSave(t, &im)
|
||||||
smodel := Create(story{}).(*story)
|
smodel := ModelRegistry["story"]
|
||||||
query, err := smodel.Find(bson.M{}, options.Find())
|
query, err := smodel.Find(bson.M{}, options.Find())
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
final := CreateSlice(story{})
|
final := CreateSlice(story{})
|
||||||
@ -98,7 +99,7 @@ func TestModel_PopulateMulti(t *testing.T) {
|
|||||||
im := iti_multi()
|
im := iti_multi()
|
||||||
im.Author = mauthor
|
im.Author = mauthor
|
||||||
createAndSave(t, &im)
|
createAndSave(t, &im)
|
||||||
smodel := Create(story{}).(*story)
|
smodel := ModelRegistry["story"]
|
||||||
query, err := smodel.Find(bson.M{}, options.Find())
|
query, err := smodel.Find(bson.M{}, options.Find())
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
final := CreateSlice(story{})
|
final := CreateSlice(story{})
|
||||||
@ -118,7 +119,7 @@ func TestModel_PopulateChained_Multi(t *testing.T) {
|
|||||||
saveDoc(t, mauthor)
|
saveDoc(t, mauthor)
|
||||||
im.Author = mauthor
|
im.Author = mauthor
|
||||||
createAndSave(t, &im)
|
createAndSave(t, &im)
|
||||||
smodel := Create(story{}).(*story)
|
smodel := ModelRegistry["story"]
|
||||||
query, err := smodel.Find(bson.M{}, options.Find())
|
query, err := smodel.Find(bson.M{}, options.Find())
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
final := CreateSlice(story{})
|
final := CreateSlice(story{})
|
||||||
@ -141,7 +142,7 @@ func TestPopulate_Chained(t *testing.T) {
|
|||||||
saveDoc(t, storyDoc)
|
saveDoc(t, storyDoc)
|
||||||
assert.Greater(t, storyDoc.ID, int64(0))
|
assert.Greater(t, storyDoc.ID, int64(0))
|
||||||
|
|
||||||
smodel := Create(story{}).(*story)
|
smodel := ModelRegistry["story"]
|
||||||
q, err := smodel.FindByID(storyDoc.ID)
|
q, err := smodel.FindByID(storyDoc.ID)
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.NotPanics(t, func() {
|
assert.NotPanics(t, func() {
|
||||||
@ -159,15 +160,15 @@ func TestModel_Append(t *testing.T) {
|
|||||||
initTest()
|
initTest()
|
||||||
bandDoc := Create(metallica).(*band)
|
bandDoc := Create(metallica).(*band)
|
||||||
saveDoc(t, bandDoc)
|
saveDoc(t, bandDoc)
|
||||||
bmodel := Create(band{}).(*band)
|
bmodel := ModelRegistry["band"]
|
||||||
query, err := bmodel.FindByID(int64(1))
|
query, err := bmodel.FindByID(int64(1))
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
fin := &band{}
|
fin := &band{}
|
||||||
query.Exec(fin)
|
query.Exec(fin)
|
||||||
assert.Greater(t, fin.ID, int64(0))
|
assert.Greater(t, fin.ID, int64(0))
|
||||||
err = bmodel.Append("Characters", "Robert Trujillo")
|
err = fin.Append("Characters", "Robert Trujillo")
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
saveDoc(t, bmodel)
|
saveDoc(t, fin)
|
||||||
fin = &band{}
|
fin = &band{}
|
||||||
query, _ = bmodel.FindByID(int64(1))
|
query, _ = bmodel.FindByID(int64(1))
|
||||||
query.Exec(fin)
|
query.Exec(fin)
|
||||||
@ -185,7 +186,7 @@ func TestModel_Delete(t *testing.T) {
|
|||||||
func TestModel_Pull(t *testing.T) {
|
func TestModel_Pull(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
storyDoc := Create(iti_multi()).(*story)
|
storyDoc := Create(iti_multi()).(*story)
|
||||||
smodel := Create(story{}).(*story)
|
smodel := ModelRegistry["story"]
|
||||||
saveDoc(t, storyDoc)
|
saveDoc(t, storyDoc)
|
||||||
err := storyDoc.Pull("Chapters", storyDoc.Chapters[4])
|
err := storyDoc.Pull("Chapters", storyDoc.Chapters[4])
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@ -217,7 +218,7 @@ func TestModel_Swap(t *testing.T) {
|
|||||||
func TestModel_GridFSLoad(t *testing.T) {
|
func TestModel_GridFSLoad(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
ModelRegistry.Model(somethingWithNestedChapters{})
|
ModelRegistry.Model(somethingWithNestedChapters{})
|
||||||
model := Create(somethingWithNestedChapters{}).(*somethingWithNestedChapters)
|
model := ModelRegistry["somethingWithNestedChapters"]
|
||||||
thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters)
|
thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters)
|
||||||
found := &somethingWithNestedChapters{}
|
found := &somethingWithNestedChapters{}
|
||||||
|
|
||||||
@ -236,7 +237,7 @@ func TestModel_GridFSLoad(t *testing.T) {
|
|||||||
func TestModel_GridFSLoad_Chained(t *testing.T) {
|
func TestModel_GridFSLoad_Chained(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
ModelRegistry.Model(somethingWithNestedChapters{})
|
ModelRegistry.Model(somethingWithNestedChapters{})
|
||||||
model := Create(somethingWithNestedChapters{}).(*somethingWithNestedChapters)
|
model := ModelRegistry["somethingWithNestedChapters"]
|
||||||
thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters)
|
thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters)
|
||||||
found := &somethingWithNestedChapters{}
|
found := &somethingWithNestedChapters{}
|
||||||
|
|
||||||
@ -254,7 +255,7 @@ func TestModel_GridFSLoad_Chained(t *testing.T) {
|
|||||||
|
|
||||||
func TestModel_GridFSLoad_Complex(t *testing.T) {
|
func TestModel_GridFSLoad_Complex(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
model := Create(story{}).(*story)
|
model := ModelRegistry["story"]
|
||||||
bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band)
|
bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band)
|
||||||
thingDoc := Create(iti_multi()).(*story)
|
thingDoc := Create(iti_multi()).(*story)
|
||||||
mauthor := Create(author).(*user)
|
mauthor := Create(author).(*user)
|
||||||
|
5
query.go
5
query.go
@ -156,7 +156,7 @@ func populate(r Reference, rcoll string, rawDoc interface{}, d string, src inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
rawt := ModelRegistry.new_(tto.Name())
|
rawt := ModelRegistry.new_(tto.Name())
|
||||||
t := rawt.(IModel)
|
t := rawt.(IDocument)
|
||||||
q := bson.M{"_id": rawDoc}
|
q := bson.M{"_id": rawDoc}
|
||||||
reso := DB.Collection(rcoll).FindOne(context.TODO(), q)
|
reso := DB.Collection(rcoll).FindOne(context.TODO(), q)
|
||||||
reso.Decode(t)
|
reso.Decode(t)
|
||||||
@ -516,7 +516,7 @@ func (q *Query) Exec(result interface{}) {
|
|||||||
if doc.Elem().Kind() == reflect.Slice {
|
if doc.Elem().Kind() == reflect.Slice {
|
||||||
for i := 0; i < doc.Elem().Len(); i++ {
|
for i := 0; i < doc.Elem().Len(); i++ {
|
||||||
cur := doc.Elem().Index(i)
|
cur := doc.Elem().Index(i)
|
||||||
imodel, ok := cur.Interface().(IModel)
|
imodel, ok := cur.Interface().(IDocument)
|
||||||
if ok {
|
if ok {
|
||||||
imodel.setExists(true)
|
imodel.setExists(true)
|
||||||
doc.Elem().Index(i).Set(reflect.ValueOf(imodel))
|
doc.Elem().Index(i).Set(reflect.ValueOf(imodel))
|
||||||
@ -524,6 +524,5 @@ func (q *Query) Exec(result interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
reflect.ValueOf(result).Elem().Set(reflect.ValueOf(q.doc).Elem())
|
reflect.ValueOf(result).Elem().Set(reflect.ValueOf(q.doc).Elem())
|
||||||
q.model.self = q.doc
|
|
||||||
q.done = true
|
q.done = true
|
||||||
}
|
}
|
||||||
|
40
registry.go
40
registry.go
@ -16,17 +16,6 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InternalModel, as the name suggests, is used
|
|
||||||
// internally by the model registry
|
|
||||||
type InternalModel struct {
|
|
||||||
Idx int
|
|
||||||
Type reflect.Type
|
|
||||||
Collection string
|
|
||||||
References map[string]Reference
|
|
||||||
Indexes map[string][]InternalIndex
|
|
||||||
GridFSReferences map[string]GridFSReference
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference stores a typed document reference
|
// Reference stores a typed document reference
|
||||||
type Reference struct {
|
type Reference struct {
|
||||||
// owning model name
|
// owning model name
|
||||||
@ -51,10 +40,10 @@ type GridFSReference struct {
|
|||||||
Idx int
|
Idx int
|
||||||
}
|
}
|
||||||
|
|
||||||
type TModelRegistry map[string]*InternalModel
|
type TModelRegistry map[string]*Model
|
||||||
|
|
||||||
// ModelRegistry - the ModelRegistry stores a map containing
|
// ModelRegistry - the ModelRegistry stores a map containing
|
||||||
// pointers to InternalModel instances, keyed by an associated
|
// pointers to Model instances, keyed by an associated
|
||||||
// model name
|
// model name
|
||||||
var ModelRegistry = make(TModelRegistry, 0)
|
var ModelRegistry = make(TModelRegistry, 0)
|
||||||
|
|
||||||
@ -182,7 +171,7 @@ func parseTags(t reflect.Type, v reflect.Value) (map[string][]InternalIndex, map
|
|||||||
ft = ft.Elem()
|
ft = ft.Elem()
|
||||||
fallthrough
|
fallthrough
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
if ft.ConvertibleTo(reflect.TypeOf(Model{})) {
|
if ft.ConvertibleTo(reflect.TypeOf(Document{})) {
|
||||||
collTag, err := tags.Get("coll")
|
collTag, err := tags.Get("coll")
|
||||||
panik(err)
|
panik(err)
|
||||||
coll = collTag.Name
|
coll = collTag.Name
|
||||||
@ -223,10 +212,10 @@ func parseTags(t reflect.Type, v reflect.Value) (map[string][]InternalIndex, map
|
|||||||
return idcs, refs, gfsRefs, coll
|
return idcs, refs, gfsRefs, coll
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has returns the model typename and InternalModel instance corresponding
|
// Has returns the model typename and Model instance corresponding
|
||||||
// to the argument passed, as well as a boolean indicating whether it
|
// to the argument passed, as well as a boolean indicating whether it
|
||||||
// was found. otherwise returns `"", nil, false`
|
// was found. otherwise returns `"", nil, false`
|
||||||
func (r TModelRegistry) Has(i interface{}) (string, *InternalModel, bool) {
|
func (r TModelRegistry) Has(i interface{}) (string, *Model, bool) {
|
||||||
t := reflect.TypeOf(i)
|
t := reflect.TypeOf(i)
|
||||||
if t.Kind() == reflect.Ptr {
|
if t.Kind() == reflect.Ptr {
|
||||||
t = t.Elem()
|
t = t.Elem()
|
||||||
@ -240,14 +229,14 @@ func (r TModelRegistry) Has(i interface{}) (string, *InternalModel, bool) {
|
|||||||
|
|
||||||
// HasByName functions almost identically to Has,
|
// HasByName functions almost identically to Has,
|
||||||
// except that it takes a string as its argument.
|
// except that it takes a string as its argument.
|
||||||
func (r TModelRegistry) HasByName(n string) (string, *InternalModel, bool) {
|
func (r TModelRegistry) HasByName(n string) (string, *Model, bool) {
|
||||||
if t, ok := ModelRegistry[n]; ok {
|
if t, ok := ModelRegistry[n]; ok {
|
||||||
return n, t, true
|
return n, t, true
|
||||||
}
|
}
|
||||||
return "", nil, false
|
return "", nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index returns the index at which the Model struct is embedded
|
// Index returns the index at which the Document struct is embedded
|
||||||
func (r TModelRegistry) Index(n string) int {
|
func (r TModelRegistry) Index(n string) int {
|
||||||
if v, ok := ModelRegistry[n]; ok {
|
if v, ok := ModelRegistry[n]; ok {
|
||||||
return v.Idx
|
return v.Idx
|
||||||
@ -259,9 +248,12 @@ func (r TModelRegistry) new_(n string) interface{} {
|
|||||||
if name, m, ok := ModelRegistry.HasByName(n); ok {
|
if name, m, ok := ModelRegistry.HasByName(n); ok {
|
||||||
v := reflect.New(m.Type)
|
v := reflect.New(m.Type)
|
||||||
df := v.Elem().Field(m.Idx)
|
df := v.Elem().Field(m.Idx)
|
||||||
d := df.Interface().(Model)
|
do := reflect.New(df.Type())
|
||||||
d.typeName = name
|
d := do.Interface().(IDocument)
|
||||||
df.Set(reflect.ValueOf(d))
|
//d := df.Interface().(IDocument)
|
||||||
|
d.setModel(*m)
|
||||||
|
d.getModel().typeName = name
|
||||||
|
df.Set(reflect.ValueOf(d).Elem())
|
||||||
return v.Interface()
|
return v.Interface()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -303,19 +295,19 @@ func (r TModelRegistry) Model(mdl ...any) {
|
|||||||
idx := -1
|
idx := -1
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
ft := t.Field(i)
|
ft := t.Field(i)
|
||||||
if (ft.Type.ConvertibleTo(reflect.TypeOf(Model{}))) {
|
if (ft.Type.ConvertibleTo(reflect.TypeOf(Document{}))) {
|
||||||
idx = i
|
idx = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
panic("A model must embed the Model struct!")
|
panic("A model must embed the Document struct!")
|
||||||
}
|
}
|
||||||
inds, refs, gfs, coll := parseTags(t, v)
|
inds, refs, gfs, coll := parseTags(t, v)
|
||||||
if coll == "" {
|
if coll == "" {
|
||||||
panic(fmt.Sprintf("a model needs to be given a collection name! (passed type: %s)", n))
|
panic(fmt.Sprintf("a model needs to be given a collection name! (passed type: %s)", n))
|
||||||
}
|
}
|
||||||
ModelRegistry[n] = &InternalModel{
|
ModelRegistry[n] = &Model{
|
||||||
Idx: idx,
|
Idx: idx,
|
||||||
Type: t,
|
Type: t,
|
||||||
Collection: coll,
|
Collection: coll,
|
||||||
|
16
testing.go
16
testing.go
@ -37,19 +37,19 @@ type chapter struct {
|
|||||||
|
|
||||||
type band struct {
|
type band struct {
|
||||||
ID int64 `bson:"_id" json:"_id"`
|
ID int64 `bson:"_id" json:"_id"`
|
||||||
Model `bson:",inline" json:",inline" coll:"bands"`
|
Document `bson:",inline" json:",inline" coll:"bands"`
|
||||||
Name string `bson:"name" json:"name" form:"name"`
|
Name string `bson:"name" json:"name" form:"name"`
|
||||||
Locked bool `bson:"locked" json:"locked" form:"locked"`
|
Locked bool `bson:"locked" json:"locked" form:"locked"`
|
||||||
Characters []string `bson:"characters" json:"characters" form:"characters"`
|
Characters []string `bson:"characters" json:"characters" form:"characters"`
|
||||||
}
|
}
|
||||||
type user struct {
|
type user struct {
|
||||||
ID int64 `bson:"_id" json:"_id"`
|
ID int64 `bson:"_id" json:"_id"`
|
||||||
Model `bson:",inline" json:",inline" coll:"users"`
|
Document `bson:",inline" json:",inline" coll:"users"`
|
||||||
Username string `bson:"username" json:"username"`
|
Username string `bson:"username" json:"username"`
|
||||||
}
|
}
|
||||||
type story struct {
|
type story struct {
|
||||||
ID int64 `bson:"_id" json:"_id"`
|
ID int64 `bson:"_id" json:"_id"`
|
||||||
Model `bson:",inline" json:",inline" coll:"stories"`
|
Document `bson:",inline" json:",inline" coll:"stories"`
|
||||||
Title string `bson:"title" json:"title" form:"title"`
|
Title string `bson:"title" json:"title" form:"title"`
|
||||||
Author *user `bson:"author" json:"author" ref:"user"`
|
Author *user `bson:"author" json:"author" ref:"user"`
|
||||||
CoAuthor *user `bson:"coAuthor" json:"coAuthor" ref:"user"`
|
CoAuthor *user `bson:"coAuthor" json:"coAuthor" ref:"user"`
|
||||||
@ -62,7 +62,7 @@ type story struct {
|
|||||||
}
|
}
|
||||||
type somethingWithNestedChapters struct {
|
type somethingWithNestedChapters struct {
|
||||||
ID int64 `bson:"_id" json:"_id"`
|
ID int64 `bson:"_id" json:"_id"`
|
||||||
Model `bson:",inline" json:",inline" coll:"nested_stuff"`
|
Document `bson:",inline" json:",inline" coll:"nested_stuff"`
|
||||||
Chapters []chapter `bson:"chapters" json:"chapters"`
|
Chapters []chapter `bson:"chapters" json:"chapters"`
|
||||||
NestedText string `json:"text" bson:"-" gridfs:"nested_text,/nested/{{.ID}}.txt"`
|
NestedText string `json:"text" bson:"-" gridfs:"nested_text,/nested/{{.ID}}.txt"`
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ func (s *user) Id() any {
|
|||||||
|
|
||||||
func (s *story) SetId(id any) {
|
func (s *story) SetId(id any) {
|
||||||
s.ID = id.(int64)
|
s.ID = id.(int64)
|
||||||
//var t IModel = s
|
//var t IDocument =s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *band) SetId(id any) {
|
func (s *band) SetId(id any) {
|
||||||
@ -245,12 +245,12 @@ var bodom = band{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveDoc(t *testing.T, doc IModel) {
|
func saveDoc(t *testing.T, doc IDocument) {
|
||||||
err := doc.Save()
|
err := doc.Save()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAndSave(t *testing.T, doc IModel) {
|
func createAndSave(t *testing.T, doc IDocument) {
|
||||||
mdl := Create(doc).(IModel)
|
mdl := Create(doc).(IDocument)
|
||||||
saveDoc(t, mdl)
|
saveDoc(t, mdl)
|
||||||
}
|
}
|
||||||
|
44
util.go
44
util.go
@ -167,3 +167,47 @@ func checkSlice(ref reflect.Value) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertSlice[In, Out any](in []In) []Out {
|
||||||
|
out := make([]Out, 0)
|
||||||
|
for _, i := range in {
|
||||||
|
ii, ok := any(i).(Out)
|
||||||
|
if ok {
|
||||||
|
out = append(out, ii)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeSliceToDocumentSlice(in any) *DocumentSlice {
|
||||||
|
ret := make(DocumentSlice, 0)
|
||||||
|
val := reflect.ValueOf(in)
|
||||||
|
if val.Kind() == reflect.Pointer {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
if val.Kind() == reflect.Slice {
|
||||||
|
for i := 0; i < val.Len(); i++ {
|
||||||
|
if idoc, ok := val.Index(i).Interface().(IDocument); ok {
|
||||||
|
ret = append(ret, idoc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func normalizeDocSlice(iput reflect.Value) reflect.Value {
|
||||||
|
idslice, idsliceOk := iput.Interface().(IDocumentSlice)
|
||||||
|
dslice, dsliceOk := resV.Interface().(DocumentSlice)
|
||||||
|
switch {
|
||||||
|
case idsliceOk:
|
||||||
|
|
||||||
|
case dsliceOk:
|
||||||
|
default:
|
||||||
|
return iput
|
||||||
|
}
|
||||||
|
//if {
|
||||||
|
// resV.Set(trvo.Elem())
|
||||||
|
//} else {
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
}*/
|
||||||
|
Loading…
Reference in New Issue
Block a user