diff --git a/mongox/util.go b/mongox/util.go index d1fb14c5..b52b906e 100644 --- a/mongox/util.go +++ b/mongox/util.go @@ -2,38 +2,25 @@ package mongox import "go.mongodb.org/mongo-driver/bson" -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 +func isEmptyCondition(condition interface{}) bool { + switch c := condition.(type) { case bson.M: - 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) { + return len(c) == 0 case bson.D: - for _, e := range g { - if e.Key == k { - return e.Value - } - } - case bson.M: - return g[k] + 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 } - return nil } func DToM(i interface{}) interface{} { @@ -81,79 +68,30 @@ func DToM(i interface{}) interface{} { return i } -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 { +func And(filter interface{}, key string, condition interface{}) interface{} { + if condition == nil || isEmptyCondition(condition) { 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}, - } + filterMap, ok := filter.(bson.M) + if !ok || len(filterMap) == 0 { + filterMap = bson.M{} } - if and := GetE(filter, "$and"); and != nil { - return bson.M{ - "$and": AppendI(and, g), + + var newCondition interface{} + if key != "" { + if _, exists := filterMap[key]; exists { + return filter } + newCondition = bson.M{key: condition} + } else { + newCondition = condition } - if key == "" { - return bson.M{ - "$and": []interface{}{filter, g}, - } + + if existingAnd, ok := filterMap["$and"].(bson.A); ok { + filterMap["$and"] = append(existingAnd, newCondition) + } else { + filterMap["$and"] = bson.A{newCondition} } - return AppendE(filter, bson.E{Key: key, Value: f}) + return filterMap } diff --git a/mongox/util_test.go b/mongox/util_test.go index caab5903..0b9a8034 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,84 +16,161 @@ 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 TestAndEmptyFilter(t *testing.T) { + filter := bson.M{} + expected := bson.M{ + "$and": bson.A{ + bson.M{"b": bson.M{"c": 2}}, + }, + } + actual := And(filter, "b", bson.M{"c": 2}) + assert.Equal(t, expected, actual) +} + +func TestAndEmptyKey(t *testing.T) { + filter := bson.M{"a": 1} + expected := bson.M{ + "$and": bson.A{ + bson.M{"c": 3}, + }, + "a": 1, + } + actual := And(filter, "", bson.M{"c": 3}) + assert.Equal(t, expected, actual) } -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 TestAndExistingAnd(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 := And(filter, "c", bson.M{"d": 3}) + assert.Equal(t, expected, actual) } -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 TestAndWithOrAndEmptyKey(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{"c": 3}}, + "$or": bson.A{ + bson.M{"a": 1}, + bson.M{"b": 2}, + }, + } + actual := And(filter, "", bson.M{"c": 3}) + assert.Equal(t, expected, actual) } -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"}, +func TestAndComplexFilter(t *testing.T) { + filter := bson.M{ + "$and": bson.A{ + bson.M{"x": 10}, + bson.M{"y": 20}, }, - }, 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"}, + "$or": bson.A{ + bson.M{"a": 1}, + bson.M{"b": 2}, }, - }, 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"}, + } + expected := bson.M{ + "$and": bson.A{ + bson.M{"x": 10}, + bson.M{"y": 20}, + bson.M{"c": bson.M{"d": 3}}, }, - }, And(bson.M{"$and": []interface{}{bson.M{"a": "b"}}}, "x", "y")) + "$or": bson.A{bson.M{"a": 1}, + bson.M{"b": 2}, + }, + } + actual := And(filter, "c", bson.M{"d": 3}) + assert.Equal(t, expected, actual) +} - 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"}, +func TestAndNilFilter(t *testing.T) { + var filter interface{} + expected := bson.M{ + "$and": bson.A{ + bson.M{"b": bson.M{"c": 2}}, }, - }, 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"}, + } + actual := And(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{}) + assert.Equal(t, expected, actual) +} + +func TestAndProjectRefetchFilter(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}}, + }, + }, }, - }, And(bson.D{{Key: "$and", Value: []bson.M{{"a": "b"}}}}, "x", "y")) + "team": team, + } - 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", + condition := bson.M{ + "$or": bson.A{ + bson.M{"updatedat": bson.M{"$lt": updatedat}}, + bson.M{ + "id": bson.M{"$lt": last}, + "updatedat": updatedat, + }, }, - }, And(bson.D{{Key: "$or", Value: []bson.M{{"a": "b"}}}}, "", "y")) - assert.Equal(t, bson.M{ - "$and": []interface{}{ - bson.M{"a": "b"}, - "y", + } + + 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}, + }, + }, }, - }, And(bson.D{{Key: "$and", Value: []bson.M{{"a": "b"}}}}, "", "y")) + "team": team, + } + + actual := And(filter, "", condition) + assert.Equal(t, expected, actual) }