feat(coperator): 实现比较运算符功能
- 新增 compare 函数用于比较两个任意类型的值 - 实现了 $gt、$ge、$lt、$le、$eq、$ne、$in、$nin 等比较运算符 - 更新了 DocumentOperator 函数,支持使用比较运算符进行过滤 - 添加了比较运算符相关的单元测试
This commit is contained in:
parent
93fd7363a3
commit
9c2512ea96
|
@ -0,0 +1,188 @@
|
||||||
|
package coperator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compare 比较两个任意类型的值
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
a: 第一个比较值
|
||||||
|
b: 第二个比较值
|
||||||
|
|
||||||
|
返回值:
|
||||||
|
|
||||||
|
int: 比较结果 (-1:a < b, 0:a == b, 1:a > b)
|
||||||
|
error: 类型不匹配或不可比较时返回错误
|
||||||
|
*/
|
||||||
|
func compare(a, b interface{}) (int, error) {
|
||||||
|
// 快速返回:如果值完全相等直接返回0
|
||||||
|
|
||||||
|
// 获取类型信息并验证类型一致性
|
||||||
|
atype := reflect.TypeOf(a)
|
||||||
|
btype := reflect.TypeOf(b)
|
||||||
|
if atype != btype {
|
||||||
|
return 0, errors.New("types are not equal: " + atype.String() + " != " + btype.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证类型是否可比较
|
||||||
|
if !atype.Comparable() {
|
||||||
|
return 0, errors.New("types are not comparable: " + atype.String())
|
||||||
|
}
|
||||||
|
if reflect.DeepEqual(a, b) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理不同类型的基础类型比较
|
||||||
|
switch atype.Kind() {
|
||||||
|
// 处理有符号整数类型比较
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
av := reflect.ValueOf(a).Int()
|
||||||
|
bv := reflect.ValueOf(b).Int()
|
||||||
|
if av < bv {
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
if av > bv {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
|
||||||
|
// 处理无符号整数类型比较
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
av := reflect.ValueOf(a).Uint()
|
||||||
|
bv := reflect.ValueOf(b).Uint()
|
||||||
|
if av < bv {
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
if av > bv {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
|
||||||
|
// 处理浮点数类型比较
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
av := reflect.ValueOf(a).Float()
|
||||||
|
bv := reflect.ValueOf(b).Float()
|
||||||
|
if av < bv {
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
if av > bv {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
|
||||||
|
// 处理字符串类型比较
|
||||||
|
case reflect.String:
|
||||||
|
av := reflect.ValueOf(a).String()
|
||||||
|
bv := reflect.ValueOf(b).String()
|
||||||
|
if av < bv {
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
if av > bv {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
|
||||||
|
// 布尔类型特殊处理
|
||||||
|
case reflect.Bool:
|
||||||
|
return 0, errors.New("cannot compare boolean values")
|
||||||
|
|
||||||
|
// 默认不支持的类型
|
||||||
|
default:
|
||||||
|
return 0, errors.New("unsupported type for comparison: " + atype.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparOp(doc *Document, key string, value interface{}) (int, error) {
|
||||||
|
v := doc.Get(key)
|
||||||
|
if v == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
ret, err := compare(v, value)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
func operatorGt(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
||||||
|
v, e := comparOp(doc, key, value)
|
||||||
|
if e != nil {
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
return v > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func operatorGe(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
||||||
|
v, e := comparOp(doc, key, value)
|
||||||
|
if e != nil {
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
return v >= 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func operatorLt(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
||||||
|
v, e := comparOp(doc, key, value)
|
||||||
|
if e != nil {
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
return v < 0, nil
|
||||||
|
}
|
||||||
|
func operatorLe(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
||||||
|
v, e := comparOp(doc, key, value)
|
||||||
|
if e != nil {
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
return v <= 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func operatorEq(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
||||||
|
v, e := comparOp(doc, key, value)
|
||||||
|
if e != nil {
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
return v == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func operatorNe(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
||||||
|
v, e := comparOp(doc, key, value)
|
||||||
|
if e != nil {
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
return v != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func operatorIn(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
||||||
|
if value == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
slice, ok := value.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return false, errors.New("value for $in operator must be a slice")
|
||||||
|
}
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range slice {
|
||||||
|
cmp, err := compare(doc.Get(key), v)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if cmp == 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func operatorNotIn(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
||||||
|
lret, err := operatorIn(doc, filter, key, value)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return !lret, nil
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package coperator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCompare(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
a, b interface{}
|
||||||
|
expected int
|
||||||
|
err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "int equal",
|
||||||
|
a: 1,
|
||||||
|
b: 1,
|
||||||
|
expected: 0,
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int less",
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
expected: -1,
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int greater",
|
||||||
|
a: 3,
|
||||||
|
b: 2,
|
||||||
|
expected: 1,
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "string less",
|
||||||
|
a: "a",
|
||||||
|
b: "b",
|
||||||
|
expected: -1,
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "different types",
|
||||||
|
a: 1,
|
||||||
|
b: "1",
|
||||||
|
expected: 0,
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unsupported type",
|
||||||
|
a: []int{1},
|
||||||
|
b: []int{1},
|
||||||
|
expected: 0,
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
res, err := compare(tc.a, tc.b)
|
||||||
|
if tc.err {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error but got nil")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if res != tc.expected {
|
||||||
|
t.Errorf("expected %d but got %d", tc.expected, res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
11
coperator.go
11
coperator.go
|
@ -143,6 +143,13 @@ func FieldOperator(doc *Document, filter Filter, key string, value interface{})
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func operatorGt(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) {
|
func init() {
|
||||||
return false, nil
|
operatorMap["$gt"] = operatorGt
|
||||||
|
operatorMap["$ge"] = operatorGe
|
||||||
|
operatorMap["$lt"] = operatorLt
|
||||||
|
operatorMap["$le"] = operatorLe
|
||||||
|
operatorMap["$eq"] = operatorEq
|
||||||
|
operatorMap["$ne"] = operatorNe
|
||||||
|
operatorMap["$in"] = operatorIn
|
||||||
|
operatorMap["$nin"] = operatorNotIn
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,55 @@ func TestDocumentOperator(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "test with operator",
|
||||||
|
doc: Document{
|
||||||
|
"age": 18,
|
||||||
|
},
|
||||||
|
filter: Filter{
|
||||||
|
"age": Filter{
|
||||||
|
"$gt": 17,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test with multi operator",
|
||||||
|
doc: Document{
|
||||||
|
"age": 18,
|
||||||
|
},
|
||||||
|
filter: Filter{
|
||||||
|
"age": Filter{
|
||||||
|
"$gt": 17,
|
||||||
|
"$lt": 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test with multi operator",
|
||||||
|
doc: Document{
|
||||||
|
"age": 18,
|
||||||
|
},
|
||||||
|
filter: Filter{
|
||||||
|
"age": Filter{
|
||||||
|
"$in": []interface{}{16, 17, 18},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test with multi operator",
|
||||||
|
doc: Document{
|
||||||
|
"age": 22,
|
||||||
|
},
|
||||||
|
filter: Filter{
|
||||||
|
"age": Filter{
|
||||||
|
"$nin": []interface{}{16, 17, 18},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
Loading…
Reference in New Issue