fix(engine): 修复聚合引擎和查询引擎中的类型转换问题
- 在 executeMatch 中添加对 types.Filter 类型的支持 - 在 evaluateExpression 中处理 types.Filter 类型转换 - 修复 updateArrayElement 函数的返回逻辑 - 修正 projection 中负数 limit 的处理逻辑 - 更新测试用例中的数值类型为 float64 - 修复 handleExpr 中的类型断言问题 - 修正 compareRegex 调用的参数类型 - 增强 matchField 中对多种条件类型的处理 - 调整测试数据中的数值精度表示
This commit is contained in:
parent
9f8ecdd112
commit
4cb3990679
|
|
@ -86,8 +86,13 @@ func (e *AggregationEngine) executeStage(stage types.AggregateStage, docs []type
|
||||||
|
|
||||||
// executeMatch 执行 $match 阶段
|
// executeMatch 执行 $match 阶段
|
||||||
func (e *AggregationEngine) executeMatch(spec interface{}, docs []types.Document) ([]types.Document, error) {
|
func (e *AggregationEngine) executeMatch(spec interface{}, docs []types.Document) ([]types.Document, error) {
|
||||||
filter, ok := spec.(map[string]interface{})
|
// 处理 types.Filter 类型
|
||||||
if !ok {
|
var filter map[string]interface{}
|
||||||
|
if f, ok := spec.(types.Filter); ok {
|
||||||
|
filter = f
|
||||||
|
} else if f, ok := spec.(map[string]interface{}); ok {
|
||||||
|
filter = f
|
||||||
|
} else {
|
||||||
return docs, nil
|
return docs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,6 +406,11 @@ func (e *AggregationEngine) projectDocument(data map[string]interface{}, spec ma
|
||||||
|
|
||||||
// evaluateExpression 评估表达式
|
// evaluateExpression 评估表达式
|
||||||
func (e *AggregationEngine) evaluateExpression(data map[string]interface{}, expr interface{}) interface{} {
|
func (e *AggregationEngine) evaluateExpression(data map[string]interface{}, expr interface{}) interface{} {
|
||||||
|
// 处理 types.Filter 类型(转换为 map[string]interface{})
|
||||||
|
if filter, ok := expr.(types.Filter); ok {
|
||||||
|
expr = map[string]interface{}(filter)
|
||||||
|
}
|
||||||
|
|
||||||
// 处理字段引用(以 $ 开头的字符串)
|
// 处理字段引用(以 $ 开头的字符串)
|
||||||
if fieldStr, ok := expr.(string); ok && len(fieldStr) > 0 && fieldStr[0] == '$' {
|
if fieldStr, ok := expr.(string); ok && len(fieldStr) > 0 && fieldStr[0] == '$' {
|
||||||
fieldName := fieldStr[1:] // 移除 $ 前缀
|
fieldName := fieldStr[1:] // 移除 $ 前缀
|
||||||
|
|
|
||||||
|
|
@ -355,9 +355,8 @@ func updateArrayElement(data map[string]interface{}, field string, value interfa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 普通字段更新
|
// 普通字段更新 - 不处理,返回 false 让调用者自行处理
|
||||||
setNestedValue(data, field, value)
|
return false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateArrayAtPath 在指定路径更新数组
|
// updateArrayAtPath 在指定路径更新数组
|
||||||
|
|
|
||||||
|
|
@ -169,8 +169,10 @@ func projectSlice(data map[string]interface{}, field string, sliceSpec interface
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用限制
|
// 应用限制
|
||||||
if limit > 0 && limit < len(array) {
|
if limit >= 0 && limit < len(array) {
|
||||||
array = array[:limit]
|
array = array[:limit]
|
||||||
|
} else if limit < 0 {
|
||||||
|
// 负数 limit 已经在上面处理过了
|
||||||
}
|
}
|
||||||
|
|
||||||
return array
|
return array
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ func TestProjectionElemMatch(t *testing.T) {
|
||||||
name: "elemMatch finds first matching element",
|
name: "elemMatch finds first matching element",
|
||||||
data: map[string]interface{}{
|
data: map[string]interface{}{
|
||||||
"scores": []interface{}{
|
"scores": []interface{}{
|
||||||
map[string]interface{}{"subject": "math", "score": 85},
|
map[string]interface{}{"subject": "math", "score": float64(85)},
|
||||||
map[string]interface{}{"subject": "english", "score": 92},
|
map[string]interface{}{"subject": "english", "score": float64(92)},
|
||||||
map[string]interface{}{"subject": "science", "score": 78},
|
map[string]interface{}{"subject": "science", "score": float64(78)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
field: "scores",
|
field: "scores",
|
||||||
|
|
@ -99,7 +99,7 @@ func TestProjectionSlice(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "slice with skip and limit",
|
name: "slice with skip and limit",
|
||||||
data: map[string]interface{}{
|
data: map[string]interface{}{
|
||||||
"items": []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
"items": []interface{}{float64(1), float64(2), float64(3), float64(4), float64(5), float64(6), float64(7), float64(8), float64(9), float64(10)},
|
||||||
},
|
},
|
||||||
field: "items",
|
field: "items",
|
||||||
sliceSpec: []interface{}{float64(5), float64(3)},
|
sliceSpec: []interface{}{float64(5), float64(3)},
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,21 @@ func MatchFilter(doc map[string]interface{}, filter types.Filter) bool {
|
||||||
|
|
||||||
// handleExpr 处理 $expr 操作符(聚合表达式查询)
|
// handleExpr 处理 $expr 操作符(聚合表达式查询)
|
||||||
func handleExpr(doc map[string]interface{}, condition interface{}) bool {
|
func handleExpr(doc map[string]interface{}, condition interface{}) bool {
|
||||||
|
// 将 types.Filter 转换为 map[string]interface{}
|
||||||
|
var exprMap map[string]interface{}
|
||||||
|
if filter, ok := condition.(types.Filter); ok {
|
||||||
|
exprMap = filter
|
||||||
|
} else if m, ok := condition.(map[string]interface{}); ok {
|
||||||
|
exprMap = m
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// 创建临时引擎实例用于评估表达式
|
// 创建临时引擎实例用于评估表达式
|
||||||
engine := &AggregationEngine{}
|
engine := &AggregationEngine{}
|
||||||
|
|
||||||
// 评估聚合表达式
|
// 评估聚合表达式
|
||||||
result := engine.evaluateExpression(doc, condition)
|
result := engine.evaluateExpression(doc, exprMap)
|
||||||
|
|
||||||
// 转换为布尔值
|
// 转换为布尔值
|
||||||
return isTrueValue(result)
|
return isTrueValue(result)
|
||||||
|
|
@ -277,7 +287,7 @@ func validateFieldValue(value interface{}, schema map[string]interface{}) bool {
|
||||||
if patternRaw, exists := schema["pattern"]; exists {
|
if patternRaw, exists := schema["pattern"]; exists {
|
||||||
if str, ok := value.(string); ok {
|
if str, ok := value.(string); ok {
|
||||||
if pattern, ok := patternRaw.(string); ok {
|
if pattern, ok := patternRaw.(string); ok {
|
||||||
if !compareRegex(str, map[string]interface{}{"$regex": pattern}) {
|
if !compareRegex(str, pattern) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -573,8 +583,15 @@ func handleNot(doc map[string]interface{}, condition interface{}) bool {
|
||||||
func matchField(doc map[string]interface{}, key string, condition interface{}) bool {
|
func matchField(doc map[string]interface{}, key string, condition interface{}) bool {
|
||||||
value := getNestedValue(doc, key)
|
value := getNestedValue(doc, key)
|
||||||
|
|
||||||
// 处理操作符条件
|
// 处理操作符条件(支持 types.Filter 和 map[string]interface{})
|
||||||
if condMap, ok := condition.(map[string]interface{}); ok {
|
var condMap map[string]interface{}
|
||||||
|
if f, ok := condition.(types.Filter); ok {
|
||||||
|
condMap = f
|
||||||
|
} else if m, ok := condition.(map[string]interface{}); ok {
|
||||||
|
condMap = m
|
||||||
|
}
|
||||||
|
|
||||||
|
if condMap != nil {
|
||||||
return evaluateOperators(value, condMap)
|
return evaluateOperators(value, condMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func TestExpr(t *testing.T) {
|
||||||
name: "comparison fails with $expr",
|
name: "comparison fails with $expr",
|
||||||
doc: map[string]interface{}{"qty": 3, "minQty": 5},
|
doc: map[string]interface{}{"qty": 3, "minQty": 5},
|
||||||
filter: types.Filter{
|
filter: types.Filter{
|
||||||
"$expr": types.Filter{
|
"$expr": map[string]interface{}{
|
||||||
"$gt": []interface{}{"$qty", "$minQty"},
|
"$gt": []interface{}{"$qty", "$minQty"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -34,31 +34,31 @@ func TestMatchFilter(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "greater than",
|
name: "greater than",
|
||||||
doc: map[string]interface{}{"age": 30},
|
doc: map[string]interface{}{"age": 30},
|
||||||
filter: types.Filter{"age": types.Filter{"$gt": 25}},
|
filter: types.Filter{"age": map[string]interface{}{"$gt": float64(25)}},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "less than",
|
name: "less than",
|
||||||
doc: map[string]interface{}{"age": 20},
|
doc: map[string]interface{}{"age": 20},
|
||||||
filter: types.Filter{"age": types.Filter{"$lt": 25}},
|
filter: types.Filter{"age": map[string]interface{}{"$lt": float64(25)}},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "in array",
|
name: "in array",
|
||||||
doc: map[string]interface{}{"status": "active"},
|
doc: map[string]interface{}{"status": "active"},
|
||||||
filter: types.Filter{"status": types.Filter{"$in": []interface{}{"active", "pending"}}},
|
filter: types.Filter{"status": map[string]interface{}{"$in": []interface{}{"active", "pending"}}},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "exists",
|
name: "exists",
|
||||||
doc: map[string]interface{}{"name": "Alice"},
|
doc: map[string]interface{}{"name": "Alice"},
|
||||||
filter: types.Filter{"name": types.Filter{"$exists": true}},
|
filter: types.Filter{"name": map[string]interface{}{"$exists": true}},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "not exists",
|
name: "not exists",
|
||||||
doc: map[string]interface{}{"name": "Alice"},
|
doc: map[string]interface{}{"name": "Alice"},
|
||||||
filter: types.Filter{"email": types.Filter{"$exists": false}},
|
filter: types.Filter{"email": map[string]interface{}{"$exists": false}},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -98,19 +98,19 @@ func TestApplyUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "increment field",
|
name: "increment field",
|
||||||
data: map[string]interface{}{"count": 5},
|
data: map[string]interface{}{"count": float64(5)},
|
||||||
update: types.Update{
|
update: types.Update{
|
||||||
Inc: map[string]interface{}{"count": 3},
|
Inc: map[string]interface{}{"count": float64(3)},
|
||||||
},
|
},
|
||||||
expected: map[string]interface{}{"count": 8},
|
expected: map[string]interface{}{"count": float64(8)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "multiply field",
|
name: "multiply field",
|
||||||
data: map[string]interface{}{"price": 100},
|
data: map[string]interface{}{"price": float64(100)},
|
||||||
update: types.Update{
|
update: types.Update{
|
||||||
Mul: map[string]interface{}{"price": 0.9},
|
Mul: map[string]interface{}{"price": float64(0.9)},
|
||||||
},
|
},
|
||||||
expected: map[string]interface{}{"price": 90},
|
expected: map[string]interface{}{"price": float64(90)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue