package orm import ( "context" "encoding/json" "fmt" "github.com/fatih/structtag" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "log" "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 { rv = reflect.New(rt) rv.Elem().Set(reflect.ValueOf(src)) } var fieldsMap [3]string type bsonWhat struct { What string } 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()) 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 { 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 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: tto := r.HydratedType if tto.Kind() == reflect.Pointer || tto.Kind() == reflect.Slice { tto = tto.Elem() } rawt := ModelRegistry.new_(tto.Name()) t := rawt.(IModel) q := bson.M{"_id": rawDoc} reso := DB.Collection(rcoll).FindOne(context.TODO(), q) reso.Decode(t) 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()) hatred.Set(reflect.ValueOf(t).Elem()) } else { hatred.Set(reflect.ValueOf(t)) } } else { src = t toReturn = src } } if toReturn == nil { return rv.Interface() } 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 } // Populate populates document references via reflection func (q *Query) Populate(fields ...string) *Query { _, cm, _ := ModelRegistry.HasByName(q.model.typeName) if cm != nil { rawDoc := q.rawDoc for _, field := range fields { // 0 = fieldname, 1 = typename, 2 = bson name var r Reference for k2, v := range cm.References { if strings.HasPrefix(k2, field) { r = v break } } if r.Exists { // 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{} if arr, ok := rawDoc.(bson.A); ok { typ := reflect.PointerTo(cm.Type) slic := reflect.New(reflect.SliceOf(typ)) for i, val2 := range arr { ref := reflect.ValueOf(q.doc) if ref.Kind() == reflect.Pointer { ref = ref.Elem() } src := ref.Index(i).Interface() inter := populate(r, refColl.Collection, val2, field, src) if reflect.ValueOf(inter).Kind() == reflect.Pointer { slic.Elem().Set(reflect.Append(slic.Elem(), reflect.ValueOf(inter))) } else { slic.Elem().Set(reflect.Append(slic, reflect.ValueOf(inter))) } } tmp1 = slic.Interface() } else { tmp1 = populate(r, refColl.Collection, rawDoc, field, reflect.ValueOf(q.doc).Interface()) } q.doc = tmp1 } } } 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)) /*if inter.Kind() == reflect.Pointer { inter = inter.Elem() }*/ slic.Elem().Set(reflect.Append(slic.Elem(), inter)) } trvo = slic.Elem() } else { trvo = reflect.ValueOf(rerere(q.rawDoc, reflect.TypeOf(q.doc))) /*for { if trvo.Kind() == reflect.Pointer { trvo = trvo.Elem() } else { break } }*/ } 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) interface{} { t := reflect.TypeOf(input) v := reflect.ValueOf(input) 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() } 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 { 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("bson") if err != nil { continue } if btag.Name == "-" { continue } intermediate := mipmap[btag.Name] _, err = tags.Get("ref") if err != nil { tmp := rerere(intermediate, ft.Type) fuck := reflect.ValueOf(tmp) if tmp != nil { if fuck.Type().Kind() == reflect.Pointer { fuck = fuck.Elem() } fv.Set(fuck) } else { fv.Set(reflect.Zero(ft.Type)) } shouldBreak = true } else { tt := ft.Type if tt.Kind() == reflect.Pointer { tt = tt.Elem() } tmp := rerere(intermediate, ft.Type) 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)) } } else { fv.Set(reflect.Zero(ft.Type)) } } } if shouldBreak { // break } } else { nunu := ModelRegistry.new_(resType.Name()) ider, ok := nunu.(HasID) if ok { 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: arr := v.Interface().(bson.A) for _, it := range arr { if it != nil { tmp := reflect.ValueOf(rerere(it, rve.Type().Elem())) 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[primitive.DateTime]() { fval = time.UnixMilli(int64(fval.(primitive.DateTime))) } if valueField.Kind() == reflect.Pointer { valueField.Elem().Set(reflect.ValueOf(fval)) } 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().(IModel) if ok { imodel.setExists(true) doc.Elem().Index(i).Set(reflect.ValueOf(imodel)) } } } reflect.ValueOf(result).Elem().Set(reflect.ValueOf(q.doc).Elem()) q.model.self = q.doc q.done = true }