feat: query_parser support new one filter parse pattern.

This commit is contained in:
Bobo
2025-06-24 10:48:26 +08:00
parent b9665d4f9b
commit bed43b3576
4 changed files with 299 additions and 12 deletions

View File

@@ -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字段字符串按分隔符分割成多个字段

View File

@@ -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])
}

13
query_parser/utils.go Normal file
View File

@@ -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)
}

View File

@@ -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