can delete + search
This commit is contained in:
132
dbs/dbs.go
132
dbs/dbs.go
@@ -2,6 +2,7 @@ package dbs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
@@ -148,6 +149,137 @@ type Filter struct {
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// FiltersFromFlatMap builds a *Filters from a map[string]interface{} whose structure
|
||||
// mirrors the JSON form of Filters:
|
||||
//
|
||||
// {
|
||||
// "and": { "name": [{"operator":"like","value":"foo"}] },
|
||||
// "or": { "source": [{"operator":"equal","value":"bar"}] }
|
||||
// }
|
||||
//
|
||||
// Keys inside "and"/"or" are json tag names; the function resolves each to its
|
||||
// full dotted BSON path using the target struct. Unknown keys are kept as-is.
|
||||
func FiltersFromFlatMap(flatMap map[string]interface{}, target interface{}) *Filters {
|
||||
filters := &Filters{
|
||||
And: make(map[string][]Filter),
|
||||
Or: make(map[string][]Filter),
|
||||
}
|
||||
paths := jsonToBsonPaths(reflect.TypeOf(target), "")
|
||||
resolve := func(jsonKey string) string {
|
||||
if p, ok := paths[jsonKey]; ok {
|
||||
return p
|
||||
}
|
||||
return jsonKey
|
||||
}
|
||||
parseFilters := func(raw interface{}) map[string][]Filter {
|
||||
out := make(map[string][]Filter)
|
||||
m, ok := raw.(map[string]interface{})
|
||||
if !ok {
|
||||
return out
|
||||
}
|
||||
for jsonKey, val := range m {
|
||||
bsonKey := resolve(jsonKey)
|
||||
items, ok := val.([]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, item := range items {
|
||||
entry, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
f := Filter{}
|
||||
if op, ok := entry["operator"].(string); ok {
|
||||
f.Operator = op
|
||||
}
|
||||
if v, ok := entry["value"]; ok {
|
||||
f.Value = v
|
||||
}
|
||||
out[bsonKey] = append(out[bsonKey], f)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
if and, ok := flatMap["and"]; ok {
|
||||
filters.And = parseFilters(and)
|
||||
}
|
||||
if or, ok := flatMap["or"]; ok {
|
||||
filters.Or = parseFilters(or)
|
||||
}
|
||||
return filters
|
||||
}
|
||||
|
||||
// jsonToBsonPaths recursively walks a struct type and returns a map of
|
||||
// json_name → dotted_bson_path for every field reachable from that type.
|
||||
//
|
||||
// Anonymous embedded fields without any tag follow the BSON convention of this
|
||||
// codebase: they are stored as a nested sub-document whose key is the lowercased
|
||||
// struct type name (e.g. utils.AbstractObject → "abstractobject").
|
||||
func jsonToBsonPaths(t reflect.Type, prefix string) map[string]string {
|
||||
for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
|
||||
t = t.Elem()
|
||||
}
|
||||
result := make(map[string]string)
|
||||
if t.Kind() != reflect.Struct {
|
||||
return result
|
||||
}
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
jsonTag := field.Tag.Get("json")
|
||||
bsonTag := field.Tag.Get("bson")
|
||||
jsonName := strings.Split(jsonTag, ",")[0]
|
||||
bsonName := strings.Split(bsonTag, ",")[0]
|
||||
|
||||
// Anonymous embedded struct with no tags: use lowercase type name as BSON prefix.
|
||||
if field.Anonymous && jsonName == "" && bsonName == "" {
|
||||
ft := field.Type
|
||||
for ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
if ft.Kind() == reflect.Struct {
|
||||
embedPrefix := strings.ToLower(ft.Name())
|
||||
if prefix != "" {
|
||||
embedPrefix = prefix + "." + embedPrefix
|
||||
}
|
||||
for k, v := range jsonToBsonPaths(ft, embedPrefix) {
|
||||
if _, exists := result[k]; !exists {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if jsonName == "" || jsonName == "-" {
|
||||
continue
|
||||
}
|
||||
if bsonName == "" || bsonName == "-" {
|
||||
bsonName = jsonName
|
||||
}
|
||||
|
||||
fullPath := bsonName
|
||||
if prefix != "" {
|
||||
fullPath = prefix + "." + bsonName
|
||||
}
|
||||
|
||||
result[jsonName] = fullPath
|
||||
|
||||
ft := field.Type
|
||||
for ft.Kind() == reflect.Ptr || ft.Kind() == reflect.Slice {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
if ft.Kind() == reflect.Struct {
|
||||
for k, v := range jsonToBsonPaths(ft, fullPath) {
|
||||
if _, exists := result[k]; !exists {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type Input = map[string]interface{}
|
||||
|
||||
func InputToBson(i Input, isUpdate bool) bson.D {
|
||||
|
||||
Reference in New Issue
Block a user