Skip to main content
Glama
sql_where_process.go21 kB
package callbacks import ( "fmt" "reflect" "regexp" "strconv" "strings" "time" "github.com/duke-git/lancet/v2/slice" "github.com/weibaohui/kom/kom" "github.com/weibaohui/kom/utils" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/klog/v2" ) // executeFilter 使用 lancet 执行过滤 func executeFilter(result []*unstructured.Unstructured, conditions []*kom.Condition) []*unstructured.Unstructured { // 最终的结果,按照条件列表,逐一执行过滤 // 2. 按 and or 分组 groupedConditions := groupByOperator(conditions) // 3. 目前暂时改为支持简单的 and or 逻辑。不管括号。 // TODO 需要处理复杂的括号等逻辑情况 // keys and or keys := make([]string, 0, len(groupedConditions)) for k := range groupedConditions { keys = append(keys, k) } // 3. 按and or 两组 逐个访问 map 并处理 for _, key := range keys { // 这个key == and or group := slice.Filter(conditions, func(index int, item *kom.Condition) bool { return item.AndOr == key }) // 按组进行过滤,一般一组为相同的and or 条件 result = evaluateCondition(result, group) } return result } func groupByDepth(conditions []kom.Condition) map[int][]kom.Condition { groups := make(map[int][]kom.Condition) for _, p := range conditions { depth := p.Depth groups[depth] = append(groups[depth], p) } return groups } func groupByOperator(conditions []*kom.Condition) map[string][]*kom.Condition { groups := make(map[string][]*kom.Condition) for _, p := range conditions { operator := p.AndOr groups[operator] = append(groups[operator], p) } return groups } func evaluateCondition(result []*unstructured.Unstructured, group []*kom.Condition) []*unstructured.Unstructured { // 一般一组 and or 都相同 if group[0].AndOr == "OR" { return matchAny(result, group) } else { return matchAll(result, group) } } // matchAll 判断所有条件都满足 (AND 逻辑) func matchAll(result []*unstructured.Unstructured, conditions []*kom.Condition) []*unstructured.Unstructured { return slice.Filter(result, func(index int, item *unstructured.Unstructured) bool { // 遍历所有条件,只有全部条件成立才返回 true for _, c := range conditions { condition := matchCondition(item, c) klog.V(8).Infof("matchAny %s/%s %s %s %s = %v", item.GetNamespace(), item.GetName(), c.Field, c.Operator, c.Value, condition) if !condition { return false // 只要有一个条件不成立,直接返回 false } } return true // 所有条件都成立,返回 true }) } // matchAny 判断任一条件满足 (OR 逻辑) func matchAny(result []*unstructured.Unstructured, conditions []*kom.Condition) []*unstructured.Unstructured { return slice.Filter(result, func(index int, item *unstructured.Unstructured) bool { // 遍历所有条件,任意一个条件成立就返回 true for _, c := range conditions { condition := matchCondition(item, c) klog.V(8).Infof("matchAny %s/%s %s %s %s = %v", item.GetNamespace(), item.GetName(), c.Field, c.Operator, c.Value, condition) if condition { // 任意一个条件达成,就返回。 // 没有达成不要返回,让他执行下一个条件 return true } } return false }) } // matchCondition 判断单个条件是否匹配 // 如果要处理的字段,是一个值而不是列表,那么直接进行比较。 // 如果要处理的字段,对应的是k8s的列表,如 status.addresses[type=InternalIP].address // status.addresses是一个数组,status.addresses[type=InternalIP].address 那么取出的address是一个数组,但是这样使用type进行了过滤 // status.addresses是一个数组,status.addresses.address 那么取出的address也是一个数组,没有按type过滤 // status: // // addresses: // - address: 172.18.0.2 // type: InternalIP // - address: kind-control-plane // type: Hostname // // 对于 正向操作符(如 =, like, in, between),只要找到一个匹配的值就返回 true。 // 对于 负向操作符(如 !=, not in, not between),则要确保所有值都不匹配才返回 true。 func matchCondition(resource *unstructured.Unstructured, condition *kom.Condition) bool { klog.V(6).Infof("matchCondition %s %s %s", condition.Field, condition.Operator, condition.Value) // 获取字段值 fieldValues, found, err := getNestedFieldAsString(resource.Object, condition.Field) if err != nil || !found { klog.V(6).Infof("not found %s,%v", condition.Field, err) return false } // 获取到的值是一个值,不是列表,直接处理 if len(fieldValues) == 1 { fieldValue := fieldValues[0] switch condition.Operator { case "=": if compareValue(fieldValue, condition.Value) { return true } case "!=": if compareValue(fieldValue, condition.Value) { return false // 负向条件,找到一个匹配值即返回 false } case "like": if compareLike(fieldValue, condition.Value) { return true } case "in": if compareIn(fieldValue, condition.Value) { return true } case "not in": if compareIn(fieldValue, condition.Value) { return false } case ">": if compareGreater(fieldValue, condition.Value) { return true } case "<": if compareLess(fieldValue, condition.Value) { return true } case ">=": if compareGreaterOrEqual(fieldValue, condition.Value) { return true } case "<=": if compareLessOrEqual(fieldValue, condition.Value) { return true } case "between": if compareBetween(fieldValue, condition.Value) { return true } case "not between": if compareBetween(fieldValue, condition.Value) { return false } default: return false } } // 获取到的值,是一个列表,属于yaml中的列表属性,那么需要综合思考了。 // 判断是正向条件还是负向条件 isNegativeCondition := condition.Operator == "!=" || condition.Operator == "not in" || condition.Operator == "not between" // 处理每个字段值 for _, fieldValue := range fieldValues { // 对于负向条件(!=, not in, not between),需要确保所有值都不满足条件 switch condition.Operator { case "=": if !isNegativeCondition && compareValue(fieldValue, condition.Value) { return true // 正向条件,找到一个值匹配即返回 true } case "!=": if isNegativeCondition && compareValue(fieldValue, condition.Value) { return false // 负向条件,找到一个匹配值即返回 false } case "like": if !isNegativeCondition && compareLike(fieldValue, condition.Value) { return true } case "in": if !isNegativeCondition && compareIn(fieldValue, condition.Value) { return true } case "not in": if isNegativeCondition && compareIn(fieldValue, condition.Value) { return false } case ">": if !isNegativeCondition && compareGreater(fieldValue, condition.Value) { return true } case "<": if !isNegativeCondition && compareLess(fieldValue, condition.Value) { return true } case ">=": if !isNegativeCondition && compareGreaterOrEqual(fieldValue, condition.Value) { return true } case "<=": if !isNegativeCondition && compareLessOrEqual(fieldValue, condition.Value) { return true } case "between": if !isNegativeCondition && compareBetween(fieldValue, condition.Value) { return true } case "not between": if isNegativeCondition && compareBetween(fieldValue, condition.Value) { return false } default: return false } } // 负向条件的情况:只有所有值都不满足条件,才认为不满足条件 if isNegativeCondition { return true // 所有值都不满足条件,返回 true } // 默认返回 false,表示没有任何一个值满足条件 return false } // compareValue 比较值是否相等,不区分大小写 func compareValue(fieldValue string, value interface{}) bool { klog.V(8).Infof("compareValue (=) %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) switch v := value.(type) { case string: return strings.ToLower(fieldValue) == strings.ToLower(v) case float64, int, int64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat == v case time.Time: // 需要将 fieldValue 转换为 time 类型进行比较 fieldTime, err := time.Parse(time.RFC3339, fieldValue) if err != nil { return false } return fieldTime.Equal(v) default: return false } } // compareLike 判断字符串是否匹配,不区分大小写 func compareLike(fieldValue string, value interface{}) bool { klog.V(6).Infof("compareLike (like) %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) // 处理为小写 fieldValue = strings.ToLower(fieldValue) targetValue := fmt.Sprintf("%v", value) // 提取值 val := strings.TrimPrefix(targetValue, "%") val = strings.TrimSuffix(val, "%") // 处理为小写 val = strings.ToLower(val) // 判断是否包含% if strings.HasSuffix(targetValue, "%") && strings.HasPrefix(targetValue, "%") { // 以%开头,以%结尾,表示包含即可 return strings.Contains(fieldValue, val) } else if strings.HasSuffix(targetValue, "%") { // abc%, 只以%结尾,表示开头必须是abc return strings.HasPrefix(fieldValue, val) } else if strings.HasPrefix(targetValue, "%") { // %abc 只以%开头,表示结尾必须是abc return strings.HasSuffix(fieldValue, val) } else { // 不包含%,表示必须相等 return fieldValue == val } } // compareGreater 比较数值是否大于 func compareGreater(fieldValue string, value interface{}) bool { klog.V(6).Infof("compareGreater(>) %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) switch v := value.(type) { case float64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat > v case int, int64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat > float64(v.(int)) case string: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } valueFloat, err := strconv.ParseFloat(v, 64) if err != nil { return false } return fieldValFloat > valueFloat case time.Time: fieldValTime, err := utils.ParseTime(fieldValue) if err != nil { return false } return fieldValTime.After(v) default: klog.V(6).Infof("%s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) return false } } // compareLess 比较数值是否小于 func compareLess(fieldValue string, value interface{}) bool { klog.V(6).Infof("compareLess(<) %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) switch v := value.(type) { case float64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat < v case int, int64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat < float64(v.(int)) case string: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } valueFloat, err := strconv.ParseFloat(v, 64) if err != nil { return false } return fieldValFloat < valueFloat case time.Time: fieldValTime, err := utils.ParseTime(fieldValue) if err != nil { return false } return fieldValTime.Before(v) default: klog.V(6).Infof("%s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) return false } } // compareGreaterOrEqual 比较数值是否大于或等于 func compareGreaterOrEqual(fieldValue string, value interface{}) bool { klog.V(6).Infof("compareGreaterOrEqual(>=) %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) switch v := value.(type) { case float64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat >= v case int, int64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat >= float64(v.(int)) case string: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } valueFloat, err := strconv.ParseFloat(v, 64) if err != nil { return false } return fieldValFloat >= valueFloat case time.Time: fieldValTime, err := utils.ParseTime(fieldValue) if err != nil { return false } return fieldValTime.After(v) || fieldValTime.Equal(v) default: klog.V(6).Infof("%s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) return false } } // compareLessOrEqual 比较数值是否小于或等于 func compareLessOrEqual(fieldValue string, value interface{}) bool { klog.V(6).Infof("compareLessOrEqual(<=) %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) switch v := value.(type) { case float64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat <= v case int, int64: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } return fieldValFloat <= float64(v.(int)) case string: fieldValFloat, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return false } valueFloat, err := strconv.ParseFloat(v, 64) if err != nil { return false } return fieldValFloat <= valueFloat case time.Time: fieldValTime, err := utils.ParseTime(fieldValue) if err != nil { return false } return fieldValTime.Before(v) || fieldValTime.Equal(v) default: klog.V(6).Infof("%s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) return false } } // compareIn 判断值是否在列表中 func compareIn(fieldValue string, value interface{}) bool { klog.V(6).Infof("compareIn(in []) %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) // value 类型字符串 = (1,2,3,4) ,这个格式决定了只能是string类型 // 如何判断fieldValue 是否在1,2,3,4范围内? if str, ok := value.(string); ok { // 去掉首尾的括号 str = strings.TrimPrefix(str, "(") str = strings.TrimSuffix(str, ")") // 以逗号分割 values := strings.Split(str, ",") for _, v := range values { v = strings.Trim(v, " ") v = utils.TrimQuotes(v) // 时间、字符串、数字 // 只有相等,才能返回,因为in操作符,是or的关系。一个不行,需要判断下一个。 // 先按数字比较 fieldValueNum, err1 := strconv.ParseFloat(fieldValue, 64) toNum, err2 := strconv.ParseFloat(v, 64) if err1 == nil && err2 == nil { if fieldValueNum == toNum { return true } } // 时间不能简单判断,而要判断是否日期、小时、分钟,是否in。 // 是否包含时间部分,如果包含,就是精确匹配。如果不不含,就是判断日期 fieldValueTime, err1 := utils.ParseTime(fieldValue) toTime, err2 := utils.ParseTime(v) if err1 == nil && err2 == nil { // 判断目标时间字符串是否包含时间部分(即时分秒) if hasTimeComponent(v) { // 逐级比较时间分量(小时、分钟、秒) if fieldValueTime.Hour() == toTime.Hour() && fieldValueTime.Minute() == toTime.Minute() && fieldValueTime.Second() == toTime.Second() { return true } } // 比较日期部分(年、月、日) if isSameDate(fieldValueTime, toTime) { return true } } if fieldValue == v { return true } } } return false } // 判断是否包含时间部分 func hasTimeComponent(value string) bool { return strings.Contains(value, ":") // 如果字符串中包含冒号,说明有时间部分 } // 判断两个时间的日期部分是否相同 func isSameDate(t1, t2 time.Time) bool { y1, m1, d1 := t1.Date() y2, m2, d2 := t2.Date() return y1 == y2 && m1 == m2 && d1 == d2 } // compareBetween 判断值是否在范围内 func compareBetween(fieldValue string, value interface{}) bool { klog.V(6).Infof("compareBetween (between x and y) %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) // value格式 举例: 1 and 5 这个格式决定了只能是string类型 // 正则表达式匹配 "from AND to" var from, to string re := regexp.MustCompile(`(?i)(.+?)\s+AND\s+(.+)`) matches := re.FindStringSubmatch(fmt.Sprintf("%v", value)) // 如果匹配成功,提取出 from 和 to if len(matches) == 3 { from = matches[1] to = matches[2] } // 判断 from to 是否为时间类型、数字类、还是字符串 // 数字类型,要做 fieldValue 要转换为对应的类型,并进行>=from <=to的判断 // 1. 尝试作为数字比较 if fieldValNum, err := strconv.ParseFloat(fieldValue, 64); err == nil { fromNum, err1 := strconv.ParseFloat(from, 64) toNum, err2 := strconv.ParseFloat(to, 64) if err1 == nil && err2 == nil { klog.V(6).Infof("compareBetween(between x and y) as number %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) return fieldValNum >= fromNum && fieldValNum <= toNum } else { klog.V(6).Infof("compareBetween(between x and y) as number error %v %v", err1, err2) } } // 2. 尝试作为时间比较 if fieldValTime, err := utils.ParseTime(fieldValue); err == nil { fromTime, err1 := utils.ParseTime(from) toTime, err2 := utils.ParseTime(to) if err1 == nil && err2 == nil { klog.V(6).Infof("compareBetween(between x and y) as date %s,%v(%v)", fieldValue, value, reflect.TypeOf(value)) return (fieldValTime.Equal(fromTime) || fieldValTime.After(fromTime)) && (fieldValTime.Equal(toTime) || fieldValTime.Before(toTime)) } else { klog.V(6).Infof("compareBetween(between x and y) as date error %v %v", err1, err2) } } // 3. 作为字符串比较 return fieldValue >= from && fieldValue <= to } // getNestedFieldAsString 获取嵌套字段值,支持数组筛选并处理数组返回值 func getNestedFieldAsString(obj interface{}, path string) ([]string, bool, error) { fields, arrayCondition, err := parsePathWithCondition(path) if err != nil { return nil, false, err } return getFieldValues(obj, fields, arrayCondition) } // parsePathWithCondition 解析路径,支持数组条件筛选 func parsePathWithCondition(path string) ([]string, map[string]string, error) { // 用 . 分割路径 parts := strings.Split(path, ".") var arrayCondition map[string]string // 检查是否包含条件(如 [type=InternalIP]) if len(parts) > 1 && strings.Contains(parts[1], "[") { // 提取字段和条件 conditionPart := parts[1] startIndex := strings.Index(conditionPart, "[") endIndex := strings.Index(conditionPart, "]") // 提取字段和条件 field := conditionPart[:startIndex] condition := conditionPart[startIndex+1 : endIndex] conditionParts := strings.Split(condition, "=") if len(conditionParts) == 2 { arrayCondition = map[string]string{conditionParts[0]: conditionParts[1]} // 重新组装路径,去掉条件部分 parts[1] = field } } return parts, arrayCondition, nil } // getFieldValues 递归获取字段值,支持数组筛选并返回多个值 func getFieldValues(obj interface{}, fields []string, arrayCondition map[string]string) ([]string, bool, error) { if len(fields) == 0 { if obj != nil { return []string{fmt.Sprintf("%v", obj)}, true, nil } return nil, false, nil } currentField := fields[0] remainingFields := fields[1:] switch v := obj.(type) { case map[string]interface{}: // 从 map 中获取值 if val, exists := v[currentField]; exists { return getFieldValues(val, remainingFields, arrayCondition) } return nil, false, nil case []map[string]interface{}: // 遍历数组,筛选符合条件的项 var results []string for _, item := range v { if matchCondition2(item, arrayCondition) { // 条件匹配,递归获取剩余字段 if val, found, err := getFieldValues(item, remainingFields, nil); found || err != nil { results = append(results, val...) } } } return results, len(results) > 0, nil case []interface{}: // 如果是 interface{} 数组,逐项判断 var results []string for _, item := range v { if val, found, err := getFieldValues(item, fields, arrayCondition); found || err != nil { results = append(results, val...) } } return results, len(results) > 0, nil case string: if len(fields) == 0 { return []string{v}, true, nil } return nil, false, nil case bool, int, int64, float64: if len(fields) == 0 { return []string{fmt.Sprintf("%v", v)}, true, nil } return nil, false, nil default: return nil, false, nil } } // matchCondition 检查数组中的元素是否符合条件 func matchCondition2(value interface{}, condition map[string]string) bool { for key, val := range condition { if valueMap, ok := value.(map[string]interface{}); ok { if mapVal, exists := valueMap[key]; exists && fmt.Sprintf("%v", mapVal) == val { continue } return false } } return true }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/weibaohui/kom'

If you have feedback or need assistance with the MCP directory API, please join our Discord server