From 21f1beba4ef934c367fa8e54c0673782a1a2c1c0 Mon Sep 17 00:00:00 2001 From: tomokazu tantaka Date: Wed, 27 Nov 2024 15:22:45 +0900 Subject: [PATCH] add AddCondition method --- mongox/util.go | 145 +++++++++++++++++++++--- mongox/util_test.go | 270 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 388 insertions(+), 27 deletions(-) diff --git a/mongox/util.go b/mongox/util.go index 7b8ee130..bdb994a6 100644 --- a/mongox/util.go +++ b/mongox/util.go @@ -2,25 +2,38 @@ package mongox import "go.mongodb.org/mongo-driver/bson" -func isEmptyCondition(condition interface{}) bool { - switch c := condition.(type) { +func AppendE(f interface{}, elements ...bson.E) interface{} { + switch f2 := f.(type) { + case bson.D: + for _, e := range elements { + f2 = append(f2, e) + } + return f2 case bson.M: - return len(c) == 0 + f3 := make(bson.M, len(f2)) + for k, v := range f2 { + f3[k] = v + } + for _, e := range elements { + f3[e.Key] = e.Value + } + return f3 + } + return f +} + +func GetE(f interface{}, k string) interface{} { + switch g := f.(type) { case bson.D: - return len(c) == 0 - case bson.A: - return len(c) == 0 - case []bson.M: - return len(c) == 0 - case []bson.D: - return len(c) == 0 - case []bson.A: - return len(c) == 0 - case []interface{}: - return len(c) == 0 - default: - return false + for _, e := range g { + if e.Key == k { + return e.Value + } + } + case bson.M: + return g[k] } + return nil } func DToM(i interface{}) interface{} { @@ -68,7 +81,105 @@ func DToM(i interface{}) interface{} { return i } -func And(filter interface{}, key string, condition interface{}) interface{} { +func AppendI(f interface{}, elements ...interface{}) interface{} { + switch f2 := f.(type) { + case []bson.D: + res := make([]interface{}, 0, len(f2)) + for _, e := range f2 { + res = append(res, e) + } + return append(res, elements...) + case []bson.M: + res := make([]interface{}, 0, len(f2)+len(elements)) + for _, e := range f2 { + res = append(res, e) + } + return append(res, elements...) + case bson.A: + res := make([]interface{}, 0, len(f2)+len(elements)) + return append(res, append(f2, elements...)...) + case []interface{}: + res := make([]interface{}, 0, len(f2)+len(elements)) + return append(res, append(f2, elements...)...) + } + return f +} + +func And(filter interface{}, key string, f interface{}) interface{} { + if f == nil { + return filter + } + if g, ok := f.(bson.M); ok && g == nil { + return filter + } + if g, ok := f.(bson.D); ok && g == nil { + return filter + } + if g, ok := f.(bson.A); ok && g == nil { + return filter + } + if g, ok := f.([]interface{}); ok && g == nil { + return filter + } + if g, ok := f.([]bson.M); ok && g == nil { + return filter + } + if g, ok := f.([]bson.D); ok && g == nil { + return filter + } + if g, ok := f.([]bson.A); ok && g == nil { + return filter + } + + if key != "" && GetE(filter, key) != nil { + return filter + } + var g interface{} + if key == "" { + g = f + } else { + g = bson.M{key: f} + } + if GetE(filter, "$or") != nil { + return bson.M{ + "$and": []interface{}{filter, g}, + } + } + if and := GetE(filter, "$and"); and != nil { + return bson.M{ + "$and": AppendI(and, g), + } + } + if key == "" { + return bson.M{ + "$and": []interface{}{filter, g}, + } + } + return AppendE(filter, bson.E{Key: key, Value: f}) +} + +func isEmptyCondition(condition interface{}) bool { + switch c := condition.(type) { + case bson.M: + return len(c) == 0 + case bson.D: + return len(c) == 0 + case bson.A: + return len(c) == 0 + case []bson.M: + return len(c) == 0 + case []bson.D: + return len(c) == 0 + case []bson.A: + return len(c) == 0 + case []interface{}: + return len(c) == 0 + default: + return false + } +} + +func AddCondition(filter interface{}, key string, condition interface{}) interface{} { if condition == nil || isEmptyCondition(condition) { return filter } diff --git a/mongox/util_test.go b/mongox/util_test.go index a2b4ba33..fed4c9aa 100644 --- a/mongox/util_test.go +++ b/mongox/util_test.go @@ -7,7 +7,7 @@ import ( "go.mongodb.org/mongo-driver/bson" ) -func TestConvertdToM(t *testing.T) { +func TestConvertDToM(t *testing.T) { assert.Equal(t, bson.M{"a": "b"}, DToM(bson.M{"a": "b"})) assert.Equal(t, bson.M{"a": "b"}, DToM(bson.D{{Key: "a", Value: "b"}})) assert.Equal(t, []interface{}{bson.M{"a": "b"}}, DToM([]bson.D{{{Key: "a", Value: "b"}}})) @@ -16,6 +16,88 @@ func TestConvertdToM(t *testing.T) { assert.Equal(t, []interface{}{bson.M{"a": "b"}}, DToM([]interface{}{bson.D{{Key: "a", Value: "b"}}})) } +func TestAppendI(t *testing.T) { + assert.Equal(t, []interface{}{bson.M{"a": "b"}, "x"}, AppendI([]bson.M{{"a": "b"}}, "x")) + assert.Equal(t, []interface{}{bson.D{{Key: "a", Value: "b"}}, "x"}, AppendI([]bson.D{{{Key: "a", Value: "b"}}}, "x")) + assert.Equal(t, []interface{}{bson.D{{Key: "a", Value: "b"}}, "x"}, AppendI(bson.A{bson.D{{Key: "a", Value: "b"}}}, "x")) + assert.Equal(t, []interface{}{bson.D{{Key: "a", Value: "b"}}, "x"}, AppendI([]interface{}{bson.D{{Key: "a", Value: "b"}}}, "x")) +} + +func TestAppendE(t *testing.T) { + assert.Equal(t, bson.M{"a": "b", "c": "d"}, AppendE(bson.M{"a": "b"}, bson.E{Key: "c", Value: "d"})) + assert.Equal(t, bson.D{{Key: "a", Value: "b"}, {Key: "c", Value: "d"}}, AppendE(bson.D{{Key: "a", Value: "b"}}, bson.E{Key: "c", Value: "d"})) + assert.Equal(t, []bson.M{}, AppendE([]bson.M{}, bson.E{Key: "c", Value: "d"})) +} + +func TestGetE(t *testing.T) { + assert.Equal(t, "b", GetE(bson.M{"a": "b"}, "a")) + assert.Nil(t, GetE(bson.M{"a": "b"}, "b")) + assert.Equal(t, "b", GetE(bson.D{{Key: "a", Value: "b"}}, "a")) + assert.Nil(t, GetE(bson.D{{Key: "a", Value: "b"}}, "b")) + assert.Nil(t, GetE(bson.A{}, "b")) +} + +func TestAnd(t *testing.T) { + assert.Equal(t, bson.M{"x": "y"}, And(bson.M{}, "x", "y")) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "x", "y")) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "", nil)) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "", bson.M(nil))) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "", bson.D(nil))) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "", bson.A(nil))) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "", []bson.M(nil))) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "", []bson.D(nil))) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "", []bson.A(nil))) + assert.Equal(t, bson.M{"x": "z"}, And(bson.M{"x": "z"}, "", []interface{}(nil))) + assert.Equal(t, bson.M{ + "$and": []interface{}{ + bson.M{"$or": []bson.M{{"a": "b"}}}, + bson.M{"x": "y"}, + }, + }, And(bson.M{"$or": []bson.M{{"a": "b"}}}, "x", "y")) + assert.Equal(t, bson.M{ + "$and": []interface{}{ + bson.M{"a": "b"}, + bson.M{"x": "y"}, + }, + }, And(bson.M{"$and": []bson.M{{"a": "b"}}}, "x", "y")) + assert.Equal(t, bson.M{ + "$and": []interface{}{ + bson.M{"a": "b"}, + bson.M{"x": "y"}, + }, + }, And(bson.M{"$and": []interface{}{bson.M{"a": "b"}}}, "x", "y")) + + assert.Equal(t, bson.D{{Key: "x", Value: "y"}}, And(bson.D{}, "x", "y")) + assert.Equal(t, bson.D{{Key: "x", Value: "z"}}, And(bson.D{{Key: "x", Value: "z"}}, "x", "y")) + assert.Equal(t, bson.M{ + "$and": []interface{}{ + bson.D{{Key: "$or", Value: []bson.M{{"a": "b"}}}}, + bson.M{"x": "y"}, + }, + }, And(bson.D{{Key: "$or", Value: []bson.M{{"a": "b"}}}}, "x", "y")) + assert.Equal(t, bson.M{ + "$and": []interface{}{ + bson.M{"a": "b"}, + bson.M{"x": "y"}, + }, + }, And(bson.D{{Key: "$and", Value: []bson.M{{"a": "b"}}}}, "x", "y")) + + assert.Equal(t, bson.M{"$and": []interface{}{bson.M{}, "y"}}, And(bson.M{}, "", "y")) + assert.Equal(t, bson.M{"$and": []interface{}{bson.D{}, "y"}}, And(bson.D{}, "", "y")) + assert.Equal(t, bson.M{ + "$and": []interface{}{ + bson.D{{Key: "$or", Value: []bson.M{{"a": "b"}}}}, + "y", + }, + }, And(bson.D{{Key: "$or", Value: []bson.M{{"a": "b"}}}}, "", "y")) + assert.Equal(t, bson.M{ + "$and": []interface{}{ + bson.M{"a": "b"}, + "y", + }, + }, And(bson.D{{Key: "$and", Value: []bson.M{{"a": "b"}}}}, "", "y")) +} + func TestAndEmptyFilter(t *testing.T) { filter := bson.M{} expected := bson.M{ @@ -23,7 +105,7 @@ func TestAndEmptyFilter(t *testing.T) { bson.M{"b": bson.M{"c": 2}}, }, } - actual := And(filter, "b", bson.M{"c": 2}) + actual := AddCondition(filter, "b", bson.M{"c": 2}) assert.Equal(t, expected, actual) } @@ -36,11 +118,11 @@ func TestAndEmptyKey(t *testing.T) { }, "a": 1, } - actual := And(filter, "", bson.M{"c": 3}) + actual := AddCondition(filter, "", bson.M{"c": 3}) assert.Equal(t, expected, actual) } -func TestAndExistingAnd(t *testing.T) { +func TestAndExistingAddCondition(t *testing.T) { filter := bson.M{ "$and": bson.A{ bson.M{"a": 1}, @@ -54,7 +136,7 @@ func TestAndExistingAnd(t *testing.T) { bson.M{"c": bson.M{"d": 3}}, }, } - actual := And(filter, "c", bson.M{"d": 3}) + actual := AddCondition(filter, "c", bson.M{"d": 3}) assert.Equal(t, expected, actual) } @@ -80,7 +162,7 @@ func TestAndWithOrAndEmptyKey(t *testing.T) { bson.M{"b": 2}, }, } - actual := And(filter, "", bson.M{"c": 3}) + actual := AddCondition(filter, "", bson.M{"c": 3}) assert.Equal(t, expected, actual) } @@ -105,7 +187,7 @@ func TestAndComplexFilter(t *testing.T) { bson.M{"b": 2}, }, } - actual := And(filter, "c", bson.M{"d": 3}) + actual := AddCondition(filter, "c", bson.M{"d": 3}) assert.Equal(t, expected, actual) } @@ -116,14 +198,14 @@ func TestAndNilFilter(t *testing.T) { bson.M{"b": bson.M{"c": 2}}, }, } - actual := And(filter, "b", bson.M{"c": 2}) + actual := AddCondition(filter, "b", bson.M{"c": 2}) assert.Equal(t, expected, actual) } func TestAndEmptySliceCondition(t *testing.T) { filter := bson.M{"a": 1} expected := bson.M{"a": 1} - actual := And(filter, "b", bson.A{}) + actual := AddCondition(filter, "b", bson.A{}) assert.Equal(t, expected, actual) } @@ -180,6 +262,174 @@ func TestAndProjectRefetchFilter(t *testing.T) { "team": team, } - actual := And(filter, "", condition) + actual := AddCondition(filter, "", condition) + assert.Equal(t, expected, actual) +} + +func TestAddConditionEmptyFilter(t *testing.T) { + filter := bson.M{} + expected := bson.M{ + "$and": bson.A{ + bson.M{"b": bson.M{"c": 2}}, + }, + } + actual := AddCondition(filter, "b", bson.M{"c": 2}) + assert.Equal(t, expected, actual) +} + +func TestAddConditionEmptyKey(t *testing.T) { + filter := bson.M{"a": 1} + expected := bson.M{ + "$and": bson.A{ + bson.M{"a": 1}, + bson.M{"c": 3}, + }, + "a": 1, + } + actual := AddCondition(filter, "", bson.M{"c": 3}) + assert.Equal(t, expected, actual) +} + +func TestAddConditionExistingAddCondition(t *testing.T) { + filter := bson.M{ + "$and": bson.A{ + bson.M{"a": 1}, + bson.M{"b": 2}, + }, + } + expected := bson.M{ + "$and": bson.A{ + bson.M{"a": 1}, + bson.M{"b": 2}, + bson.M{"c": bson.M{"d": 3}}, + }, + } + actual := AddCondition(filter, "c", bson.M{"d": 3}) + assert.Equal(t, expected, actual) +} + +func TestAddConditionWithOrAndEmptyKey(t *testing.T) { + filter := bson.M{ + "$or": bson.A{ + bson.M{"a": 1}, + bson.M{"b": 2}, + }, + } + expected := bson.M{ + "$and": bson.A{ + bson.M{ + "$or": bson.A{ + bson.M{"a": 1}, + bson.M{"b": 2}, + }, + }, + bson.M{"c": 3}, + }, + "$or": bson.A{ + bson.M{"a": 1}, + bson.M{"b": 2}, + }, + } + actual := AddCondition(filter, "", bson.M{"c": 3}) + assert.Equal(t, expected, actual) +} + +func TestAddConditionComplexFilter(t *testing.T) { + filter := bson.M{ + "$and": bson.A{ + bson.M{"x": 10}, + bson.M{"y": 20}, + }, + "$or": bson.A{ + bson.M{"a": 1}, + bson.M{"b": 2}, + }, + } + expected := bson.M{ + "$and": bson.A{ + bson.M{"x": 10}, + bson.M{"y": 20}, + bson.M{"c": bson.M{"d": 3}}, + }, + "$or": bson.A{bson.M{"a": 1}, + bson.M{"b": 2}, + }, + } + actual := AddCondition(filter, "c", bson.M{"d": 3}) + assert.Equal(t, expected, actual) +} + +func TestAddConditionNilFilter(t *testing.T) { + var filter interface{} + expected := bson.M{ + "$and": bson.A{ + bson.M{"b": bson.M{"c": 2}}, + }, + } + actual := AddCondition(filter, "b", bson.M{"c": 2}) + assert.Equal(t, expected, actual) +} + +func TestAddConditionEmptySliceCondition(t *testing.T) { + filter := bson.M{"a": 1} + expected := bson.M{"a": 1} + actual := AddCondition(filter, "b", bson.A{}) + assert.Equal(t, expected, actual) +} + +func TestAddConditionProjectRefetchFilter(t *testing.T) { + team := "team_id_example" + last := "last_project_id" + updatedat := 1654849072592 + filter := bson.M{ + "$and": bson.A{ + bson.M{ + "$or": bson.A{ + bson.M{"deleted": false}, + bson.M{"deleted": bson.M{"$exists": false}}, + }, + }, + bson.M{ + "$or": bson.A{ + bson.M{"coresupport": true}, + bson.M{"coresupport": bson.M{"$exists": false}}, + }, + }, + }, + "team": team, + } + + condition := bson.M{ + "$or": bson.A{ + bson.M{"updatedat": bson.M{"$lt": updatedat}}, + bson.M{ + "id": bson.M{"$lt": last}, + "updatedat": updatedat, + }, + }, + } + + expected := bson.M{ + "$and": bson.A{ + bson.M{"$or": bson.A{ + bson.M{"deleted": false}, + bson.M{"deleted": bson.M{"$exists": false}}, + }, + }, + bson.M{"$or": bson.A{ + bson.M{"coresupport": true}, + bson.M{"coresupport": bson.M{"$exists": false}}, + }, + }, + bson.M{"$or": bson.A{ + bson.M{"updatedat": bson.M{"$lt": updatedat}}, + bson.M{"id": bson.M{"$lt": last}, "updatedat": updatedat}, + }, + }, + }, + "team": team, + } + + actual := AddCondition(filter, "", condition) assert.Equal(t, expected, actual) }