621 lines
14 KiB
Go
621 lines
14 KiB
Go
package orm
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/fatih/structtag"
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
"go.mongodb.org/mongo-driver/v2/mongo"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Query struct {
|
|
collection *mongo.Collection
|
|
op string
|
|
model *Model
|
|
done bool
|
|
rawDoc any
|
|
doc any
|
|
}
|
|
|
|
const (
|
|
OP_FIND_ONE = "findOne"
|
|
OP_FIND_PAGED = "findPaged"
|
|
OP_FIND_ALL = "findAll"
|
|
OP_FIND = "find"
|
|
)
|
|
|
|
func populate(r Reference, rcoll string, rawDoc interface{}, d string, src interface{}) any {
|
|
rt := reflect.TypeOf(src)
|
|
rv := reflect.ValueOf(src)
|
|
srt := rt
|
|
if srt.Kind() == reflect.Pointer {
|
|
srt = rt.Elem()
|
|
}
|
|
if rv.Kind() == reflect.Pointer && reflect.ValueOf(src).IsNil() {
|
|
return src
|
|
}
|
|
if rv.Kind() != reflect.Pointer {
|
|
rv = reflect.New(rt)
|
|
rv.Elem().Set(reflect.ValueOf(src))
|
|
}
|
|
|
|
var fieldsMap [3]string
|
|
type bsonWhat struct {
|
|
What string
|
|
}
|
|
var isNotStructOrSlice bool
|
|
var w bsonWhat
|
|
switch rawDoc.(type) {
|
|
case bson.A:
|
|
w.What = "A"
|
|
case bson.D:
|
|
w.What = "D"
|
|
case bson.M:
|
|
w.What = "M"
|
|
default:
|
|
w.What = "-"
|
|
}
|
|
var next string
|
|
if len(strings.Split(d, ".")) > 1 {
|
|
next = strings.Join(strings.Split(d, ".")[1:], ".")
|
|
d = strings.Split(d, ".")[0]
|
|
} else {
|
|
next = d
|
|
}
|
|
var toReturn interface{}
|
|
switch w.What {
|
|
case "A":
|
|
rvs := reflect.MakeSlice(rt, 0, 0)
|
|
var rahh reflect.Value
|
|
if rv.Kind() == reflect.Ptr {
|
|
rahh = rv.Elem()
|
|
} else {
|
|
rahh = rv
|
|
}
|
|
for i, el := range rawDoc.(bson.A) {
|
|
it := rahh.Index(i)
|
|
popped := populate(r, rcoll, el, next, it.Interface())
|
|
if pidoc, pok := popped.(IDocument); pok {
|
|
pidoc.setRaw(el)
|
|
}
|
|
poppedVal := reflect.ValueOf(popped)
|
|
if poppedVal.Kind() == reflect.Pointer {
|
|
rvs = reflect.Append(rvs, poppedVal.Elem())
|
|
} else {
|
|
rvs = reflect.Append(rvs, poppedVal)
|
|
}
|
|
}
|
|
if rv.CanSet() {
|
|
rv.Set(rvs)
|
|
} else if rv.Kind() == reflect.Pointer {
|
|
rv.Elem().Set(rvs)
|
|
} else {
|
|
src = rvs.Interface()
|
|
toReturn = src
|
|
}
|
|
case "D":
|
|
loc := rawDoc.(bson.D)
|
|
nrd := bson.M{}
|
|
for _, el := range loc {
|
|
nrd[el.Key] = el.Value
|
|
}
|
|
rawDoc = nrd
|
|
fallthrough
|
|
case "M":
|
|
dd := rawDoc.(bson.M)
|
|
var sf reflect.Value
|
|
var rsf reflect.Value
|
|
if rv.Kind() == reflect.Pointer {
|
|
sf = rv.Elem().FieldByName(d)
|
|
} else {
|
|
sf = rv.FieldByName(d)
|
|
}
|
|
if rv.Kind() == reflect.Pointer {
|
|
rsf = rv.Elem().FieldByName(d)
|
|
} else {
|
|
rsf = rv.FieldByName(d)
|
|
}
|
|
|
|
var ff reflect.StructField
|
|
var ok bool
|
|
if rt.Kind() == reflect.Pointer {
|
|
ff, ok = rt.Elem().FieldByName(d)
|
|
} else {
|
|
ff, ok = rt.FieldByName(d)
|
|
}
|
|
|
|
if ok {
|
|
tag, err := structtag.Parse(string(ff.Tag))
|
|
if err == nil {
|
|
val, err2 := tag.Get("bson")
|
|
if err2 == nil && val.Name != "-" {
|
|
fttt := ff.Type
|
|
if fttt.Kind() == reflect.Pointer || fttt.Kind() == reflect.Slice {
|
|
fttt = fttt.Elem()
|
|
}
|
|
fieldsMap = [3]string{d, fttt.Name(), val.Name}
|
|
}
|
|
}
|
|
}
|
|
intermediate := populate(r, 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)
|
|
if intermediate != nil && iok && !reflect.ValueOf(iiid.Id()).IsZero() {
|
|
iidoc.setRaw(dd[fieldsMap[2]])
|
|
}
|
|
}
|
|
}*/
|
|
if rsf.CanSet() {
|
|
ival := reflect.ValueOf(intermediate)
|
|
if ival.Kind() != reflect.Pointer && rsf.Kind() == reflect.Pointer {
|
|
rsf.Set(ival.Elem())
|
|
} else if ival.Kind() == reflect.Pointer && rsf.Kind() != reflect.Pointer {
|
|
rsf.Set(ival.Elem())
|
|
} else {
|
|
rsf.Set(ival)
|
|
}
|
|
} else {
|
|
src = intermediate
|
|
}
|
|
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 {
|
|
fmt.Println(reflect.ValueOf(t).Elem().Type().String())
|
|
fmt.Println(rv.Type().String())
|
|
if hatred.Kind() == reflect.Pointer {
|
|
hatred.Set(reflect.ValueOf(t))
|
|
} else {
|
|
hatred.Set(reflect.ValueOf(t).Elem())
|
|
}
|
|
recover()
|
|
} else {
|
|
hatred.Set(reflect.ValueOf(t))
|
|
}
|
|
} else {
|
|
src = t
|
|
toReturn = src
|
|
}
|
|
t.SetSelf(t)
|
|
t.setExists(true)
|
|
}
|
|
|
|
if toReturn == nil {
|
|
sidoc, sok := rv.Interface().(IDocument)
|
|
if sok {
|
|
sidoc.SetSelf(rv.Interface())
|
|
if !isNotStructOrSlice {
|
|
sidoc.setRaw(rawDoc)
|
|
}
|
|
}
|
|
return rv.Interface()
|
|
}
|
|
sidoc, sok := src.(IDocument)
|
|
if sok {
|
|
sidoc.SetSelf(src)
|
|
if !isNotStructOrSlice {
|
|
sidoc.setRaw(rawDoc)
|
|
}
|
|
}
|
|
return src
|
|
}
|
|
|
|
// LoadFile - loads the contents of one or more files
|
|
// stored in gridFS into the fields named by `fields`.
|
|
//
|
|
// gridFS fields can be either a `string` or `[]byte`, and are
|
|
// tagged with `gridfs:"BUCKET,FILE_FORMAT`
|
|
// where:
|
|
// - `BUCKET` is the name of the bucket where the files are stored
|
|
// - `FILE_FORMAT` is any valid go template string that resolves to
|
|
// the unique file name.
|
|
// all exported values and methods present in the surrounding
|
|
// struct can be used in this template.
|
|
func (q *Query) LoadFile(fields ...string) *Query {
|
|
_, cm, _ := ModelRegistry.HasByName(q.model.typeName)
|
|
if cm != nil {
|
|
for _, field := range fields {
|
|
var r gridFSReference
|
|
hasAnnotated := false
|
|
for k2, v := range cm.gridFSReferences {
|
|
if strings.HasPrefix(k2, field) {
|
|
r = v
|
|
hasAnnotated = true
|
|
break
|
|
}
|
|
}
|
|
if hasAnnotated {
|
|
q.doc = gridFsLoad(q.doc, r, field)
|
|
}
|
|
}
|
|
}
|
|
return q
|
|
}
|
|
|
|
func readFields(field string, m *Model) (Reference, string) {
|
|
var r Reference
|
|
var keptKey string
|
|
if m == nil {
|
|
return r, keptKey
|
|
}
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if keptKey != "" {
|
|
break
|
|
}
|
|
}
|
|
return r, keptKey
|
|
}
|
|
|
|
// Populate populates document references via reflection
|
|
func (q *Query) Populate(fields ...string) *Query {
|
|
vvv := reflect.ValueOf(q.doc)
|
|
if vvv.Kind() == reflect.Pointer {
|
|
vvv = vvv.Elem()
|
|
}
|
|
if vvv.Kind() == reflect.Slice {
|
|
typ := reflect.PointerTo(q.model.Type)
|
|
slic := reflect.New(reflect.SliceOf(typ))
|
|
for i := 0; i < vvv.Len(); i++ {
|
|
val2 := vvv.Index(i).Interface()
|
|
aid, docOk := val2.(IDocument)
|
|
if docOk {
|
|
rdoc := q.rawDoc.(bson.A)
|
|
aid.setRaw(rdoc[i])
|
|
aid.Populate(fields...)
|
|
if reflect.ValueOf(aid).Kind() == reflect.Pointer {
|
|
slic.Elem().Set(reflect.Append(slic.Elem(), reflect.ValueOf(aid)))
|
|
} else {
|
|
slic.Elem().Set(reflect.Append(slic, reflect.ValueOf(aid)))
|
|
}
|
|
}
|
|
}
|
|
q.doc = slic.Interface()
|
|
} else if asDoc, ok2 := q.doc.(IDocument); ok2 {
|
|
asDoc.setRaw(q.rawDoc)
|
|
asDoc.Populate(fields...)
|
|
}
|
|
return q
|
|
}
|
|
func (q *Query) reOrganize() {
|
|
var trvo reflect.Value
|
|
if arr, ok := q.rawDoc.(bson.A); ok {
|
|
typ := ModelRegistry[q.model.typeName].Type
|
|
if typ.Kind() != reflect.Pointer {
|
|
typ = reflect.PointerTo(typ)
|
|
}
|
|
slic := reflect.New(reflect.SliceOf(typ))
|
|
for _, v2 := range arr {
|
|
inter := reflect.ValueOf(rerere(v2, typ, false))
|
|
slic.Elem().Set(reflect.Append(slic.Elem(), inter))
|
|
}
|
|
trvo = slic.Elem()
|
|
} else {
|
|
trvo = reflect.ValueOf(rerere(q.rawDoc, reflect.TypeOf(q.doc), false))
|
|
}
|
|
|
|
resV := reflect.ValueOf(q.doc)
|
|
for {
|
|
if resV.Kind() == reflect.Pointer {
|
|
if resV.Elem().Kind() == reflect.Slice {
|
|
resV = resV.Elem()
|
|
} else {
|
|
break
|
|
}
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
if resV.CanSet() {
|
|
resV.Set(trvo)
|
|
} else {
|
|
resV.Elem().Set(trvo.Elem())
|
|
}
|
|
}
|
|
|
|
func rerere(input interface{}, resType reflect.Type, isJson bool) interface{} {
|
|
t := reflect.TypeOf(input)
|
|
v := reflect.ValueOf(input)
|
|
var key string
|
|
if isJson {
|
|
key = "json"
|
|
} else {
|
|
key = "bson"
|
|
}
|
|
if input == nil {
|
|
return nil
|
|
}
|
|
if v.Type().Kind() == reflect.Pointer {
|
|
v = v.Elem()
|
|
}
|
|
if t.Kind() == reflect.Pointer {
|
|
t = t.Elem()
|
|
}
|
|
|
|
if resType.Kind() == reflect.Pointer {
|
|
resType = resType.Elem()
|
|
}
|
|
var resV reflect.Value
|
|
var newInstance interface{}
|
|
if _, _, has := ModelRegistry.Has(reflect.New(resType).Elem().Interface()); has {
|
|
newInstance = Create(reflect.New(resType).Elem().Interface())
|
|
resV = reflect.ValueOf(newInstance)
|
|
} else {
|
|
newInstance = ModelRegistry.newForType(resType)
|
|
if newInstance == nil && resType.Kind() == reflect.Pointer {
|
|
newInstance = ModelRegistry.newForType(resType.Elem())
|
|
if newInstance == nil {
|
|
resV = reflect.New(resType)
|
|
} else {
|
|
resV = reflect.ValueOf(newInstance)
|
|
}
|
|
} else if newInstance != nil {
|
|
resV = reflect.ValueOf(newInstance)
|
|
} else {
|
|
resV = reflect.New(resType)
|
|
}
|
|
}
|
|
|
|
var rve = resV
|
|
if rve.Kind() == reflect.Pointer {
|
|
rve = resV.Elem()
|
|
}
|
|
|
|
if d, isD := v.Interface().(bson.D); isD {
|
|
m := bson.M{}
|
|
for _, el := range d {
|
|
m[el.Key] = el.Value
|
|
}
|
|
input = m
|
|
v = reflect.ValueOf(input)
|
|
}
|
|
|
|
switch resType.Kind() {
|
|
case reflect.Struct:
|
|
shouldBreak := false
|
|
mipmap, ok := v.Interface().(bson.M)
|
|
if !ok {
|
|
var omap map[string]interface{}
|
|
if omap, ok = v.Interface().(map[string]interface{}); ok {
|
|
mipmap = bson.M(omap)
|
|
}
|
|
}
|
|
if ok {
|
|
for i := 0; i < resType.NumField(); i++ {
|
|
ft := resType.Field(i)
|
|
fv := rve.Field(i)
|
|
if ft.Anonymous {
|
|
fv.Set(handleAnon(input, ft.Type, fv))
|
|
continue
|
|
}
|
|
tags, err := structtag.Parse(string(ft.Tag))
|
|
panik(err)
|
|
btag, err := tags.Get(key)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if btag.Name == "-" {
|
|
continue
|
|
}
|
|
intermediate := mipmap[btag.Name]
|
|
_, err = tags.Get("ref")
|
|
if err != nil {
|
|
var tmp interface{}
|
|
if ttmp, tok := intermediate.(bson.DateTime); tok {
|
|
tmp = ttmp.Time()
|
|
} else {
|
|
tmp = rerere(intermediate, ft.Type, isJson)
|
|
}
|
|
fuck := reflect.ValueOf(tmp)
|
|
if tmp != nil {
|
|
if fuck.Type().Kind() == reflect.Pointer {
|
|
fuck = fuck.Elem()
|
|
}
|
|
fv.Set(fuck)
|
|
}
|
|
shouldBreak = true
|
|
} else {
|
|
tt := ft.Type
|
|
if tt.Kind() == reflect.Pointer {
|
|
tt = tt.Elem()
|
|
}
|
|
tmp := rerere(intermediate, ft.Type, isJson)
|
|
if tmp != nil {
|
|
if reflect.ValueOf(tmp).Kind() == reflect.Pointer && fv.Kind() != reflect.Pointer {
|
|
fv.Set(reflect.ValueOf(tmp).Elem())
|
|
} else {
|
|
fv.Set(reflect.ValueOf(tmp))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if shouldBreak {
|
|
// break
|
|
}
|
|
} else {
|
|
nunu := ModelRegistry.new_(resType.Name())
|
|
ider, ok := nunu.(HasID)
|
|
if ok {
|
|
toConvert := reflect.ValueOf(ider.Id()).Type()
|
|
if v.Type() != toConvert {
|
|
if v.CanConvert(toConvert) {
|
|
ider.SetId(v.Convert(toConvert).Interface())
|
|
}
|
|
} else {
|
|
ider.SetId(v.Interface())
|
|
}
|
|
if reflect.ValueOf(ider).Kind() == reflect.Pointer {
|
|
nunu = reflect.ValueOf(ider).Elem().Interface()
|
|
}
|
|
rve.Set(reflect.ValueOf(nunu))
|
|
}
|
|
}
|
|
case reflect.Slice:
|
|
_, aOk := v.Interface().(bson.A)
|
|
var arr []interface{}
|
|
if !aOk {
|
|
arr = v.Interface().([]interface{})
|
|
} else {
|
|
arr = []interface{}(v.Interface().(bson.A))
|
|
}
|
|
for _, it := range arr {
|
|
if it != nil {
|
|
tmp := reflect.ValueOf(rerere(it, rve.Type().Elem(), isJson))
|
|
if tmp.Kind() == reflect.Pointer {
|
|
tmp = tmp.Elem()
|
|
}
|
|
rve.Set(reflect.Append(rve, tmp))
|
|
}
|
|
}
|
|
default:
|
|
if resType.AssignableTo(v.Type()) {
|
|
rve.Set(reflect.ValueOf(input))
|
|
} else {
|
|
switch rve.Interface().(type) {
|
|
case int, int32, int64, uint, uint32, uint64:
|
|
rve.Set(reflect.ValueOf(coerceInt(v, rve)))
|
|
}
|
|
}
|
|
}
|
|
|
|
return resV.Interface()
|
|
}
|
|
|
|
func handleAnon(raw interface{}, rtype reflect.Type, rval reflect.Value) reflect.Value {
|
|
f := rval
|
|
g := rtype
|
|
if rtype.Kind() == reflect.Pointer {
|
|
g = rtype.Elem()
|
|
}
|
|
if !f.CanSet() {
|
|
f = reflect.New(g)
|
|
f.Elem().Set(rval)
|
|
}
|
|
if rtype.Kind() != reflect.Struct {
|
|
return rval
|
|
}
|
|
|
|
for i := 0; i < rtype.NumField(); i++ {
|
|
typeField := rtype.Field(i)
|
|
valueField := f.Field(i)
|
|
tags, err := structtag.Parse(string(typeField.Tag))
|
|
if !typeField.IsExported() {
|
|
continue
|
|
}
|
|
if err == nil {
|
|
var btag *structtag.Tag
|
|
btag, err = tags.Get("bson")
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if btag.Name == "-" {
|
|
continue
|
|
}
|
|
amap, ok := raw.(bson.M)
|
|
if ok {
|
|
fval := amap[btag.Name]
|
|
if reflect.TypeOf(fval) == reflect.TypeFor[bson.DateTime]() {
|
|
fval = fval.(bson.DateTime).Time()
|
|
}
|
|
if valueField.Kind() == reflect.Pointer {
|
|
valueField.Elem().Set(reflect.ValueOf(fval))
|
|
} else {
|
|
if reflect.TypeOf(fval) == reflect.TypeFor[string]() && typeField.Type == reflect.TypeFor[time.Time]() {
|
|
tt, _ := time.Parse(time.RFC3339, fval.(string))
|
|
valueField.Set(reflect.ValueOf(tt))
|
|
} else {
|
|
valueField.Set(reflect.ValueOf(fval))
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return f
|
|
}
|
|
|
|
// JSON - marshals this Query's results into json format
|
|
func (q *Query) JSON() (string, error) {
|
|
res, err := json.MarshalIndent(q.doc, "", "\t")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(res[:]), nil
|
|
}
|
|
|
|
// Exec - executes the query and puts its results into the
|
|
// provided argument.
|
|
//
|
|
// Will panic if called more than once on the same Query instance.
|
|
func (q *Query) Exec(result interface{}) {
|
|
if q.done {
|
|
panic("Exec() has already been called!")
|
|
}
|
|
doc := reflect.ValueOf(q.doc)
|
|
if doc.Elem().Kind() == reflect.Slice {
|
|
for i := 0; i < doc.Elem().Len(); i++ {
|
|
cur := doc.Elem().Index(i)
|
|
imodel, ok := cur.Interface().(IDocument)
|
|
if ok {
|
|
imodel.setExists(true)
|
|
doc.Elem().Index(i).Set(reflect.ValueOf(imodel))
|
|
}
|
|
}
|
|
}
|
|
reflect.ValueOf(result).Elem().Set(reflect.ValueOf(q.doc).Elem())
|
|
q.done = true
|
|
}
|