full filter interpretation

This commit is contained in:
mr
2026-06-02 11:35:19 +02:00
parent 3924fca289
commit a7d0c1208b
+43 -14
View File
@@ -165,7 +165,7 @@ func FiltersFromFlatMap(flatMap map[string]interface{}, target interface{}) *Fil
And: make(map[string][]Filter), And: make(map[string][]Filter),
Or: make(map[string][]Filter), Or: make(map[string][]Filter),
} }
paths := jsonToBsonPaths(reflect.TypeOf(target), "") paths := jsonToBsonPaths(reflect.TypeOf(target), "", "")
resolve := func(jsonKey string) string { resolve := func(jsonKey string) string {
if p, ok := paths[jsonKey]; ok { if p, ok := paths[jsonKey]; ok {
return p return p
@@ -215,11 +215,22 @@ func FiltersFromFlatMap(flatMap map[string]interface{}, target interface{}) *Fil
// //
// Anonymous embedded fields without any tag follow the BSON convention of this // 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 // codebase: they are stored as a nested sub-document whose key is the lowercased
// struct type name (e.g. utils.AbstractObject → "abstractobject"). // struct type name (e.g. utils.AbstractObject → "abstractobject"). Their JSON
func jsonToBsonPaths(t reflect.Type, prefix string) map[string]string { // fields are promoted (flat), so bsonPrefix advances but jsonPrefix does not.
//
// For fields inside slices or maps, both the leaf json name and the full dotted
// json path (e.g. "instances.access_protocol") are registered as keys so callers
// can use either form unambiguously.
func jsonToBsonPaths(t reflect.Type, bsonPrefix string, jsonPrefix string) map[string]string {
for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice { for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
t = t.Elem() t = t.Elem()
} }
if t.Kind() == reflect.Map {
t = t.Elem()
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
}
result := make(map[string]string) result := make(map[string]string)
if t.Kind() != reflect.Struct { if t.Kind() != reflect.Struct {
return result return result
@@ -233,20 +244,21 @@ func jsonToBsonPaths(t reflect.Type, prefix string) map[string]string {
bsonName := strings.Split(bsonTag, ",")[0] bsonName := strings.Split(bsonTag, ",")[0]
// Anonymous embedded struct with no tags: use lowercase type name as BSON prefix. // Anonymous embedded struct with no tags: use lowercase type name as BSON prefix.
// JSON fields are promoted so jsonPrefix stays the same.
if field.Anonymous && jsonName == "" && bsonName == "" { if field.Anonymous && jsonName == "" && bsonName == "" {
ft := field.Type ft := field.Type
for ft.Kind() == reflect.Ptr { for ft.Kind() == reflect.Ptr {
ft = ft.Elem() ft = ft.Elem()
} }
if ft.Kind() == reflect.Struct { if ft.Kind() == reflect.Struct {
embedPrefix := strings.ToLower(ft.Name()) embedBsonPrefix := strings.ToLower(ft.Name())
re := regexp.MustCompile(`\[[^\]]*\]`) re := regexp.MustCompile(`\[[^\]]*\]`)
embedPrefix = re.ReplaceAllString(embedPrefix, "") embedBsonPrefix = re.ReplaceAllString(embedBsonPrefix, "")
embedPrefix = strings.ReplaceAll(embedPrefix, "*", "") embedBsonPrefix = strings.ReplaceAll(embedBsonPrefix, "*", "")
if prefix != "" { if bsonPrefix != "" {
embedPrefix = prefix + "." + embedPrefix embedBsonPrefix = bsonPrefix + "." + embedBsonPrefix
} }
for k, v := range jsonToBsonPaths(ft, embedPrefix) { for k, v := range jsonToBsonPaths(ft, embedBsonPrefix, jsonPrefix) {
if _, exists := result[k]; !exists { if _, exists := result[k]; !exists {
result[k] = v result[k] = v
} }
@@ -262,19 +274,36 @@ func jsonToBsonPaths(t reflect.Type, prefix string) map[string]string {
bsonName = jsonName bsonName = jsonName
} }
fullPath := bsonName fullBsonPath := bsonName
if prefix != "" { if bsonPrefix != "" {
fullPath = prefix + "." + bsonName fullBsonPath = bsonPrefix + "." + bsonName
}
fullJsonPath := jsonName
if jsonPrefix != "" {
fullJsonPath = jsonPrefix + "." + jsonName
} }
result[jsonName] = fullPath result[jsonName] = fullBsonPath
// Also register the full dotted JSON path so callers can use
// "instances.access_protocol" instead of just "access_protocol".
if fullJsonPath != jsonName {
if _, exists := result[fullJsonPath]; !exists {
result[fullJsonPath] = fullBsonPath
}
}
ft := field.Type ft := field.Type
for ft.Kind() == reflect.Ptr || ft.Kind() == reflect.Slice { for ft.Kind() == reflect.Ptr || ft.Kind() == reflect.Slice {
ft = ft.Elem() ft = ft.Elem()
} }
if ft.Kind() == reflect.Map {
ft = ft.Elem()
for ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
}
if ft.Kind() == reflect.Struct { if ft.Kind() == reflect.Struct {
for k, v := range jsonToBsonPaths(ft, fullPath) { for k, v := range jsonToBsonPaths(ft, fullBsonPath, fullJsonPath) {
if _, exists := result[k]; !exists { if _, exists := result[k]; !exists {
result[k] = v result[k] = v
} }