diff --git a/query_parser/filter.go b/query_parser/filter.go index 59b1205..b32e83a 100644 --- a/query_parser/filter.go +++ b/query_parser/filter.go @@ -53,8 +53,13 @@ const ( ) const ( - QueryDelimiter = "__" // 分隔符 - JsonFieldDelimiter = "." // JSONB字段分隔符 + JSONFilterFieldOperatorDelimiter = "__" // JSON过滤器 - 字段名和操作符的分隔符 + + QueryFilterFieldOperatorDelimiter = ":" // 自定义查询字符串过滤器 - 字段名和操作符的分隔符 + QueryFilterQueriesDelimiter = "," // 自定义查询字符串过滤器 - 多个键值对的分隔符 + QueryFilterValuesDelimiter = "|" // 自定义查询字符串过滤器 - 多个值的分隔符 + + JsonFieldDelimiter = "." // JSON字段分隔符 ) type FilterHandler func(field, operator, value string) @@ -89,6 +94,40 @@ func ParseFilterJSONString(query string, handler FilterHandler) error { return err } +// ParseFilterQueryString 解析过滤条件的查询字符串,调用处理函数 +func ParseFilterQueryString(query string, handler FilterHandler) error { + if query == "" { + return nil // 如果查询字符串为空,直接返回 + } + + // 按逗号分割查询字符串,得到多个键值对 + pairs := SplitQueryQueries(query) + for _, pair := range pairs { + // 按冒号分割键值对,提取字段名和值 + parts := SplitQueryFieldAndOperator(pair) + if len(parts) != 2 { + continue // 跳过无效的键值对 + } + + // 解码字段名 + key, err := DecodeSpecialCharacters(strings.TrimSpace(parts[0])) + if err != nil { + continue // 跳过解码失败的键值对 + } + + // 解码字段值 + value, err := DecodeSpecialCharacters(strings.TrimSpace(parts[1])) + if err != nil { + continue // 跳过解码失败的键值对 + } + + // 调用 ParseFilterField 解析字段和操作符 + ParseFilterField(key, value, handler) + } + + return nil +} + // ParseFilterField 解析过滤条件字符串,调用处理函数 func ParseFilterField(key, value string, handler FilterHandler) { if key == "" || value == "" { @@ -96,7 +135,7 @@ func ParseFilterField(key, value string, handler FilterHandler) { } // 处理字段和操作符 - parts := SplitFieldAndOperator(key) + parts := SplitJsonFieldAndOperator(key) if len(parts) < 1 { return // 无效的字段格式 } @@ -115,9 +154,24 @@ func ParseFilterField(key, value string, handler FilterHandler) { handler(field, op, value) } -// SplitFieldAndOperator 将字段字符串按分隔符分割成字段名和操作符 -func SplitFieldAndOperator(field string) []string { - return strings.Split(field, QueryDelimiter) +// SplitJsonFieldAndOperator JSON过滤器 - 分割“字段名”和“操作符” +func SplitJsonFieldAndOperator(field string) []string { + return strings.Split(field, JSONFilterFieldOperatorDelimiter) +} + +// SplitQueryFieldAndOperator 自定义查询字符串过滤器 - 分割“字段名”和“操作符” +func SplitQueryFieldAndOperator(field string) []string { + return strings.Split(field, QueryFilterFieldOperatorDelimiter) +} + +// SplitQueryQueries 自定义查询字符串过滤器 - 分割多个键值对 +func SplitQueryQueries(field string) []string { + return strings.Split(field, QueryFilterQueriesDelimiter) +} + +// SplitQueryValues 自定义查询字符串过滤器 - 分割多个值 +func SplitQueryValues(field string) []string { + return strings.Split(field, QueryFilterValuesDelimiter) } // SplitJSONField 将JSONB字段字符串按分隔符分割成多个字段 diff --git a/query_parser/filter_test.go b/query_parser/filter_test.go index 8d4121f..935123f 100644 --- a/query_parser/filter_test.go +++ b/query_parser/filter_test.go @@ -81,6 +81,149 @@ func TestParseFilterJSONString(t *testing.T) { assert.Equal(t, 0, len(results)) } +func TestParseFilterQueryString(t *testing.T) { + var results []struct { + Field string + Operator string + Value string + } + + handler := func(field, operator, value string) { + results = append(results, struct { + Field string + Operator string + Value string + }{Field: field, Operator: operator, Value: value}) + } + + // 测试解析单个过滤条件 + results = nil + err := ParseFilterQueryString("name__exact:John", handler) + assert.NoError(t, err) + assert.Equal(t, 1, len(results)) + assert.Equal(t, "name", results[0].Field) + assert.Equal(t, "exact", results[0].Operator) + assert.Equal(t, "John", results[0].Value) + + // 测试解析多个过滤条件 + results = nil + err = ParseFilterQueryString("age__gte:30,status__exact:active", handler) + assert.NoError(t, err) + assert.Equal(t, 2, len(results)) + assert.Equal(t, "age", results[0].Field) + assert.Equal(t, "gte", results[0].Operator) + assert.Equal(t, "30", results[0].Value) + assert.Equal(t, "status", results[1].Field) + assert.Equal(t, "exact", results[1].Operator) + assert.Equal(t, "active", results[1].Value) + + // 测试空字符串 + results = nil + err = ParseFilterQueryString("", handler) + assert.NoError(t, err) + assert.Equal(t, 0, len(results)) + + // 测试无效的查询字符串 + results = nil + err = ParseFilterQueryString("invalid_query", handler) + assert.NoError(t, err) + assert.Equal(t, 0, len(results)) + + // 测试包含特殊字符的字段和值 + results = nil + err = ParseFilterQueryString("na!me__exact:Jo@hn", handler) + assert.NoError(t, err) + assert.Equal(t, 1, len(results)) + assert.Equal(t, "na_me", results[0].Field) + assert.Equal(t, "exact", results[0].Operator) + assert.Equal(t, "Jo@hn", results[0].Value) + + // 测试包含特殊字符的多个过滤条件 + results = nil + err = ParseFilterQueryString("ag#e__gte:30,sta$tus__exact:ac^tive", handler) + assert.NoError(t, err) + assert.Equal(t, 2, len(results)) + assert.Equal(t, "ag_e", results[0].Field) + assert.Equal(t, "gte", results[0].Operator) + assert.Equal(t, "30", results[0].Value) + assert.Equal(t, "sta_tus", results[1].Field) + assert.Equal(t, "exact", results[1].Operator) + assert.Equal(t, "ac^tive", results[1].Value) + + // 测试 Field 中包含分隔符 + results = nil + err = ParseFilterQueryString("na:me__exact:John", handler) + assert.NoError(t, err) + assert.Equal(t, 0, len(results)) + + // 测试 Operator 中包含分隔符 + results = nil + err = ParseFilterQueryString("name__ex:act:John", handler) + assert.NoError(t, err) + assert.Equal(t, 0, len(results)) + + // 测试 Value 中包含分隔符 + results = nil + err = ParseFilterQueryString("name__exact:Jo|hn", handler) + assert.NoError(t, err) + assert.Equal(t, 1, len(results)) + assert.Equal(t, "name", results[0].Field) + assert.Equal(t, "exact", results[0].Operator) + assert.Equal(t, "Jo|hn", results[0].Value) + + // 测试多个过滤条件中包含分隔符 + results = nil + err = ParseFilterQueryString("ag:e__gte:30,sta|tus__exact:ac|tive", handler) + assert.NoError(t, err) + assert.Equal(t, 1, len(results)) + + // 测试 Field 中包含编码后的分隔符 + results = nil + encodedField := EncodeSpecialCharacters("na:me") + err = ParseFilterQueryString(encodedField+"__exact:John", handler) + assert.NoError(t, err) + assert.Equal(t, 1, len(results)) + assert.Equal(t, "na_me", results[0].Field) // 注意:这里的字段名会被转换为 snake_case + assert.Equal(t, "exact", results[0].Operator) + assert.Equal(t, "John", results[0].Value) + + // 测试 Operator 中包含编码后的分隔符 + results = nil + encodedOperator := EncodeSpecialCharacters("ex:act") + err = ParseFilterQueryString("name__"+encodedOperator+":John", handler) + assert.NoError(t, err) + assert.Equal(t, 1, len(results)) + assert.Equal(t, "name", results[0].Field) + assert.Equal(t, "ex:act", results[0].Operator) + assert.Equal(t, "John", results[0].Value) + + // 测试 Value 中包含编码后的分隔符 + results = nil + encodedValue := EncodeSpecialCharacters("Jo|hn") + err = ParseFilterQueryString("name__exact:"+encodedValue, handler) + assert.NoError(t, err) + assert.Equal(t, 1, len(results)) + assert.Equal(t, "name", results[0].Field) + assert.Equal(t, "exact", results[0].Operator) + assert.Equal(t, "Jo|hn", results[0].Value) + + // 测试多个过滤条件中包含编码后的分隔符 + results = nil + encodedField1 := EncodeSpecialCharacters("ag:e") + encodedValue1 := EncodeSpecialCharacters("30") + encodedField2 := EncodeSpecialCharacters("sta|tus") + encodedValue2 := EncodeSpecialCharacters("ac|tive") + err = ParseFilterQueryString(encodedField1+"__gte:"+encodedValue1+","+encodedField2+"__exact:"+encodedValue2, handler) + assert.NoError(t, err) + assert.Equal(t, 2, len(results)) + assert.Equal(t, "ag_e", results[0].Field) // 注意:这里的字段名会被转换为 snake_case + assert.Equal(t, "gte", results[0].Operator) + assert.Equal(t, "30", results[0].Value) + assert.Equal(t, "sta_tus", results[1].Field) // 注意:这里的字段名会被转换为 snake_case + assert.Equal(t, "exact", results[1].Operator) + assert.Equal(t, "ac|tive", results[1].Value) +} + func TestParseFilterField(t *testing.T) { var results []struct { Field string @@ -128,25 +271,25 @@ func TestParseFilterField(t *testing.T) { assert.Equal(t, 0, len(results)) } -func TestSplitFieldAndOperator(t *testing.T) { +func TestSplitJsonFieldAndOperator(t *testing.T) { // 测试正常分割 - result := SplitFieldAndOperator("name__exact") + result := SplitJsonFieldAndOperator("name__exact") assert.Equal(t, 2, len(result)) assert.Equal(t, "name", result[0]) assert.Equal(t, "exact", result[1]) // 测试无操作符 - result = SplitFieldAndOperator("name") + result = SplitJsonFieldAndOperator("name") assert.Equal(t, 1, len(result)) assert.Equal(t, "name", result[0]) // 测试空字符串 - result = SplitFieldAndOperator("") + result = SplitJsonFieldAndOperator("") assert.Equal(t, 1, len(result)) assert.Equal(t, "", result[0]) // 测试多个分隔符 - result = SplitFieldAndOperator("name__exact__extra") + result = SplitJsonFieldAndOperator("name__exact__extra") assert.Equal(t, 3, len(result)) assert.Equal(t, "name", result[0]) assert.Equal(t, "exact", result[1]) @@ -179,3 +322,80 @@ func TestSplitJSONField(t *testing.T) { assert.Equal(t, "address", result[2]) assert.Equal(t, "city", result[3]) } + +func TestSplitQueryFieldAndOperator(t *testing.T) { + // 测试正常分割 + result := SplitQueryFieldAndOperator("name:exact") + assert.Equal(t, 2, len(result)) + assert.Equal(t, "name", result[0]) + assert.Equal(t, "exact", result[1]) + + // 测试无操作符 + result = SplitQueryFieldAndOperator("name") + assert.Equal(t, 1, len(result)) + assert.Equal(t, "name", result[0]) + + // 测试空字符串 + result = SplitQueryFieldAndOperator("") + assert.Equal(t, 1, len(result)) + assert.Equal(t, "", result[0]) + + // 测试多个分隔符 + result = SplitQueryFieldAndOperator("name:exact:extra") + assert.Equal(t, 3, len(result)) + assert.Equal(t, "name", result[0]) + assert.Equal(t, "exact", result[1]) + assert.Equal(t, "extra", result[2]) +} + +func TestSplitQueryQueries(t *testing.T) { + // 测试正常分割多个键值对 + result := SplitQueryQueries("name:John,age:30,status:active") + assert.Equal(t, 3, len(result)) + assert.Equal(t, "name:John", result[0]) + assert.Equal(t, "age:30", result[1]) + assert.Equal(t, "status:active", result[2]) + + // 测试单个键值对 + result = SplitQueryQueries("name:John") + assert.Equal(t, 1, len(result)) + assert.Equal(t, "name:John", result[0]) + + // 测试空字符串 + result = SplitQueryQueries("") + assert.Equal(t, 1, len(result)) + assert.Equal(t, "", result[0]) + + // 测试多个分隔符 + result = SplitQueryQueries("name:John,,age:30") + assert.Equal(t, 3, len(result)) + assert.Equal(t, "name:John", result[0]) + assert.Equal(t, "", result[1]) + assert.Equal(t, "age:30", result[2]) +} + +func TestSplitQueryValues(t *testing.T) { + // 测试正常分割多个值 + result := SplitQueryValues("value1|value2|value3") + assert.Equal(t, 3, len(result)) + assert.Equal(t, "value1", result[0]) + assert.Equal(t, "value2", result[1]) + assert.Equal(t, "value3", result[2]) + + // 测试单个值 + result = SplitQueryValues("value1") + assert.Equal(t, 1, len(result)) + assert.Equal(t, "value1", result[0]) + + // 测试空字符串 + result = SplitQueryValues("") + assert.Equal(t, 1, len(result)) + assert.Equal(t, "", result[0]) + + // 测试多个分隔符 + result = SplitQueryValues("value1||value2") + assert.Equal(t, 3, len(result)) + assert.Equal(t, "value1", result[0]) + assert.Equal(t, "", result[1]) + assert.Equal(t, "value2", result[2]) +} diff --git a/query_parser/utils.go b/query_parser/utils.go new file mode 100644 index 0000000..f351b25 --- /dev/null +++ b/query_parser/utils.go @@ -0,0 +1,13 @@ +package query_parser + +import "net/url" + +// EncodeSpecialCharacters 对字符串进行编码 +func EncodeSpecialCharacters(input string) string { + return url.QueryEscape(input) +} + +// DecodeSpecialCharacters 对字符串进行解码 +func DecodeSpecialCharacters(input string) (string, error) { + return url.QueryUnescape(input) +} diff --git a/tag.bat b/tag.bat index 7bedf7b..9d5d81a 100644 --- a/tag.bat +++ b/tag.bat @@ -10,7 +10,7 @@ git tag slug/v0.0.1 git tag name_generator/v0.0.1 git tag mapper/v0.0.3 git tag password/v0.0.1 -git tag query_parser/v0.0.1 +git tag query_parser/v0.0.2 git tag entgo/v1.1.32 git tag gorm/v1.1.6