From 1ed936eb097d10f3aaf40a44d282778f879eac32 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:47:25 +0100 Subject: [PATCH 01/11] DOC-4232 stream code examples (#3128) * DOC-4232 added first stream example * DOC-4232 examples up to xadd_7 * DOC-4232 examples up to xread * DOC-4232 examples up to xclaim * DOC-4232 added remaining examples * DOC-4232 more fixes * DOC-4232 fix for test fail on CI build --------- Co-authored-by: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> --- doctests/stream_tutorial_test.go | 1073 ++++++++++++++++++++++++++++++ 1 file changed, 1073 insertions(+) create mode 100644 doctests/stream_tutorial_test.go diff --git a/doctests/stream_tutorial_test.go b/doctests/stream_tutorial_test.go new file mode 100644 index 000000000..093324705 --- /dev/null +++ b/doctests/stream_tutorial_test.go @@ -0,0 +1,1073 @@ +// EXAMPLE: stream_tutorial +// HIDE_START +package example_commands_test + +import ( + "context" + "fmt" + + "github.com/redis/go-redis/v9" +) + +// HIDE_END + +// REMOVE_START +func UNUSED(v ...interface{}) {} + +// REMOVE_END + +func ExampleClient_xadd() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:france") + // REMOVE_END + + // STEP_START xadd + res1, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Castilla", + "speed": 30.2, + "position": 1, + "location_id": 1, + }, + }).Result() + + if err != nil { + panic(err) + } + + // fmt.Println(res1) // >>> 1692632086370-0 + + res2, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Norem", + "speed": 28.8, + "position": 3, + "location_id": 1, + }, + }).Result() + + if err != nil { + panic(err) + } + + // fmt.PrintLn(res2) // >>> 1692632094485-0 + + res3, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Prickett", + "speed": 29.7, + "position": 2, + "location_id": 1, + }, + }).Result() + + if err != nil { + panic(err) + } + + // fmt.Println(res3) // >>> 1692632102976-0 + // STEP_END + + // REMOVE_START + UNUSED(res1, res2, res3) + // REMOVE_END + + xlen, err := rdb.XLen(ctx, "race:france").Result() + + if err != nil { + panic(err) + } + + fmt.Println(xlen) // >>> 3 + + // Output: + // 3 +} + +func ExampleClient_racefrance1() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:france") + // REMOVE_END + + _, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Castilla", + "speed": 30.2, + "position": 1, + "location_id": 1, + }, + ID: "1692632086370-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Norem", + "speed": 28.8, + "position": 3, + "location_id": 1, + }, + ID: "1692632094485-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Prickett", + "speed": 29.7, + "position": 2, + "location_id": 1, + }, + ID: "1692632102976-0", + }).Result() + + if err != nil { + panic(err) + } + + // STEP_START xrange + res4, err := rdb.XRangeN(ctx, "race:france", "1691765278160-0", "+", 2).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) + // >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla... + // STEP_END + + // STEP_START xread_block + res5, err := rdb.XRead(ctx, &redis.XReadArgs{ + Streams: []string{"race:france", "0"}, + Count: 100, + Block: 300, + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res5) + // >>> // [{race:france [{1692632086370-0 map[location_id:1 position:1... + // STEP_END + + // STEP_START xadd_2 + res6, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Castilla", + "speed": 29.9, + "position": 1, + "location_id": 2, + }, + }).Result() + + if err != nil { + panic(err) + } + + //fmt.Println(res6) // >>> 1692632147973-0 + // STEP_END + + // STEP_START xlen + res7, err := rdb.XLen(ctx, "race:france").Result() + + if err != nil { + panic(err) + } + + fmt.Println(res7) // >>> 4 + // STEP_END + + // REMOVE_START + UNUSED(res6) + // REMOVE_END + + // Output: + // [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]}] + // [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]} {1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7]}]}] + // 4 +} + +func ExampleClient_raceusa() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:usa") + // REMOVE_END + + // STEP_START xadd_id + res8, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:usa", + Values: map[string]interface{}{ + "racer": "Castilla", + }, + ID: "0-1", + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res8) // >>> 0-1 + + res9, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:usa", + Values: map[string]interface{}{ + "racer": "Norem", + }, + ID: "0-2", + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res9) // >>> 0-2 + // STEP_END + + // STEP_START xadd_bad_id + res10, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Values: map[string]interface{}{ + "racer": "Prickett", + }, + ID: "0-1", + }).Result() + + if err != nil { + // fmt.Println(err) + // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item + } + // STEP_END + + // STEP_START xadd_7 + res11, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:usa", + Values: map[string]interface{}{ + "racer": "Prickett", + }, + ID: "0-*", + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res11) // >>> 0-3 + // STEP_END + + // REMOVE_START + UNUSED(res10) + // REMOVE_END + + // Output: + // 0-1 + // 0-2 + // 0-3 +} + +func ExampleClient_racefrance2() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:france") + // REMOVE_END + + _, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Castilla", + "speed": 30.2, + "position": 1, + "location_id": 1, + }, + ID: "1692632086370-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Norem", + "speed": 28.8, + "position": 3, + "location_id": 1, + }, + ID: "1692632094485-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Prickett", + "speed": 29.7, + "position": 2, + "location_id": 1, + }, + ID: "1692632102976-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Castilla", + "speed": 29.9, + "position": 1, + "location_id": 2, + }, + ID: "1692632147973-0", + }).Result() + + if err != nil { + panic(err) + } + // STEP_START xrange_all + res12, err := rdb.XRange(ctx, "race:france", "-", "+").Result() + + if err != nil { + panic(err) + } + + fmt.Println(res12) + // >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla... + // STEP_END + + // STEP_START xrange_time + res13, err := rdb.XRange(ctx, "race:france", + "1692632086369", "1692632086371", + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res13) + // >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]}] + // STEP_END + + // STEP_START xrange_step_1 + res14, err := rdb.XRangeN(ctx, "race:france", "-", "+", 2).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res14) + // >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]}] + // STEP_END + + // STEP_START xrange_step_2 + res15, err := rdb.XRangeN(ctx, "race:france", + "(1692632094485-0", "+", 2, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res15) + // >>> [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7]} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9]}] + // STEP_END + + // STEP_START xrange_empty + res16, err := rdb.XRangeN(ctx, "race:france", + "(1692632147973-0", "+", 2, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res16) + // >>> [] + // STEP_END + + // STEP_START xrevrange + res17, err := rdb.XRevRangeN(ctx, "race:france", "+", "-", 1).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res17) + // >>> [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9]}] + // STEP_END + + // STEP_START xread + res18, err := rdb.XRead(ctx, &redis.XReadArgs{ + Streams: []string{"race:france", "0"}, + Count: 2, + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res18) + // >>> [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]}]}] + // STEP_END + + // Output: + // [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]} {1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7]} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9]}] + // [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]}] + // [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]}] + // [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7]} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9]}] + // [] + // [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9]}] + // [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]}]}] +} + +func ExampleClient_xgroupcreate() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:france") + // REMOVE_END + + _, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:france", + Values: map[string]interface{}{ + "rider": "Castilla", + "speed": 30.2, + "position": 1, + "location_id": 1, + }, + ID: "1692632086370-0", + }).Result() + + if err != nil { + panic(err) + } + + // STEP_START xgroup_create + res19, err := rdb.XGroupCreate(ctx, "race:france", "france_riders", "$").Result() + + if err != nil { + panic(err) + } + + fmt.Println(res19) // >>> OK + // STEP_END + + // Output: + // OK +} + +func ExampleClient_xgroupcreatemkstream() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:italy") + // REMOVE_END + + // STEP_START xgroup_create_mkstream + res20, err := rdb.XGroupCreateMkStream(ctx, + "race:italy", "italy_riders", "$", + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res20) // >>> OK + // STEP_END + + // Output: + // OK +} + +func ExampleClient_xgroupread() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:italy") + // REMOVE_END + + _, err := rdb.XGroupCreateMkStream(ctx, + "race:italy", "italy_riders", "$", + ).Result() + + if err != nil { + panic(err) + } + + // STEP_START xgroup_read + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Castilla"}, + }).Result() + // >>> 1692632639151-0 + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Royce"}, + }).Result() + // >>> 1692632647899-0 + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Sam-Bodden"}, + }).Result() + // >>> 1692632662819-0 + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Prickett"}, + }).Result() + // >>> 1692632670501-0 + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Norem"}, + }).Result() + // >>> 1692632678249-0 + + if err != nil { + panic(err) + } + + // fmt.Println(res25) + + res21, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{ + Streams: []string{"race:italy", ">"}, + Group: "italy_riders", + Consumer: "Alice", + Count: 1, + }).Result() + + if err != nil { + panic(err) + } + + // fmt.Println(res21) + // >>> [{race:italy [{1692632639151-0 map[rider:Castilla]}]}] + // STEP_END + + // REMOVE_START + UNUSED(res21) + // REMOVE_END + + xlen, err := rdb.XLen(ctx, "race:italy").Result() + + if err != nil { + panic(err) + } + + fmt.Println(xlen) + + // Output: + // 5 +} + +func ExampleClient_raceitaly() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:italy") + rdb.XGroupDestroy(ctx, "race:italy", "italy_riders") + // REMOVE_END + + _, err := rdb.XGroupCreateMkStream(ctx, + "race:italy", "italy_riders", "$", + ).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Castilla"}, + ID: "1692632639151-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Royce"}, + ID: "1692632647899-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Sam-Bodden"}, + ID: "1692632662819-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Prickett"}, + ID: "1692632670501-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + Values: map[string]interface{}{"rider": "Norem"}, + ID: "1692632678249-0", + }).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XReadGroup(ctx, &redis.XReadGroupArgs{ + Streams: []string{"race:italy", ">"}, + Group: "italy_riders", + Consumer: "Alice", + Count: 1, + }).Result() + + if err != nil { + panic(err) + } + // STEP_START xgroup_read_id + res22, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{ + Streams: []string{"race:italy", "0"}, + Group: "italy_riders", + Consumer: "Alice", + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res22) + // >>> [{race:italy [{1692632639151-0 map[rider:Castilla]}]}] + // STEP_END + + // STEP_START xack + res23, err := rdb.XAck(ctx, + "race:italy", "italy_riders", "1692632639151-0", + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res23) // >>> 1 + + res24, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{ + Streams: []string{"race:italy", "0"}, + Group: "italy_riders", + Consumer: "Alice", + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res24) + // >>> [{race:italy []}] + // STEP_END + + // STEP_START xgroup_read_bob + res25, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{ + Streams: []string{"race:italy", ">"}, + Group: "italy_riders", + Consumer: "Bob", + Count: 2, + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res25) + // >>> [{race:italy [{1692632647899-0 map[rider:Royce]} {1692632662819-0 map[rider:Sam-Bodden]}]}] + + // STEP_END + + // STEP_START xpending + res26, err := rdb.XPending(ctx, "race:italy", "italy_riders").Result() + + if err != nil { + panic(err) + } + + fmt.Println(res26) + // >>> &{2 1692632647899-0 1692632662819-0 map[Bob:2]} + // STEP_END + + // STEP_START xpending_plus_minus + res27, err := rdb.XPendingExt(ctx, &redis.XPendingExtArgs{ + Stream: "race:italy", + Group: "italy_riders", + Start: "-", + End: "+", + Count: 10, + }).Result() + + if err != nil { + panic(err) + } + + // fmt.Println(res27) + // >>> [{1692632647899-0 Bob 0s 1} {1692632662819-0 Bob 0s 1}] + // STEP_END + + // STEP_START xrange_pending + res28, err := rdb.XRange(ctx, "race:italy", + "1692632647899-0", "1692632647899-0", + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res28) // >>> [{1692632647899-0 map[rider:Royce]}] + // STEP_END + + // STEP_START xclaim + res29, err := rdb.XClaim(ctx, &redis.XClaimArgs{ + Stream: "race:italy", + Group: "italy_riders", + Consumer: "Alice", + MinIdle: 0, + Messages: []string{"1692632647899-0"}, + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res29) + // STEP_END + + // STEP_START xautoclaim + res30, res30a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{ + Stream: "race:italy", + Group: "italy_riders", + Consumer: "Alice", + Start: "0-0", + Count: 1, + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res30) // >>> [{1692632647899-0 map[rider:Royce]}] + fmt.Println(res30a) // >>> 1692632662819-0 + // STEP_END + + // STEP_START xautoclaim_cursor + res31, res31a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{ + Stream: "race:italy", + Group: "italy_riders", + Consumer: "Lora", + Start: "(1692632662819-0", + Count: 1, + }).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res31) // >>> [] + fmt.Println(res31a) // >>> 0-0 + // STEP_END + + // STEP_START xinfo + res32, err := rdb.XInfoStream(ctx, "race:italy").Result() + + if err != nil { + panic(err) + } + + fmt.Println(res32) + // >>> &{5 1 2 1 1692632678249-0 0-0 5 {1692632639151-0 map[rider:Castilla]} {1692632678249-0 map[rider:Norem]} 1692632639151-0} + // STEP_END + + // STEP_START xinfo_groups + res33, err := rdb.XInfoGroups(ctx, "race:italy").Result() + + if err != nil { + panic(err) + } + + fmt.Println(res33) + // >>> [{italy_riders 3 2 1692632662819-0 3 2}] + // STEP_END + + // STEP_START xinfo_consumers + res34, err := rdb.XInfoConsumers(ctx, "race:italy", "italy_riders").Result() + + if err != nil { + panic(err) + } + + // fmt.Println(res34) + // >>> [{Alice 1 1ms 1ms} {Bob 1 2ms 2ms} {Lora 0 1ms -1ms}] + // STEP_END + + // STEP_START maxlen + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + MaxLen: 2, + Values: map[string]interface{}{"rider": "Jones"}, + }, + ).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + MaxLen: 2, + Values: map[string]interface{}{"rider": "Wood"}, + }, + ).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + MaxLen: 2, + Values: map[string]interface{}{"rider": "Henshaw"}, + }, + ).Result() + + if err != nil { + panic(err) + } + + res35, err := rdb.XLen(ctx, "race:italy").Result() + + if err != nil { + panic(err) + } + + fmt.Println(res35) // >>> 2 + + res36, err := rdb.XRange(ctx, "race:italy", "-", "+").Result() + + if err != nil { + panic(err) + } + + // fmt.Println(res36) + // >>> [{1726649529170-1 map[rider:Wood]} {1726649529171-0 map[rider:Henshaw]}] + // STEP_END + + // STEP_START xtrim + res37, err := rdb.XTrimMaxLen(ctx, "race:italy", 10).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res37) // >>> 0 + // STEP_END + + // STEP_START xtrim2 + res38, err := rdb.XTrimMaxLenApprox(ctx, "race:italy", 10, 20).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res38) // >>> 0 + // STEP_END + + // REMOVE_START + UNUSED(res27, res34, res36) + // REMOVE_END + + // Output: + // [{race:italy [{1692632639151-0 map[rider:Castilla]}]}] + // 1 + // [{race:italy []}] + // [{race:italy [{1692632647899-0 map[rider:Royce]} {1692632662819-0 map[rider:Sam-Bodden]}]}] + // &{2 1692632647899-0 1692632662819-0 map[Bob:2]} + // [{1692632647899-0 map[rider:Royce]}] + // [{1692632647899-0 map[rider:Royce]}] + // [{1692632647899-0 map[rider:Royce]}] + // 1692632662819-0 + // [] + // 0-0 + // &{5 1 2 1 1692632678249-0 0-0 5 {1692632639151-0 map[rider:Castilla]} {1692632678249-0 map[rider:Norem]} 1692632639151-0} + // [{italy_riders 3 2 1692632662819-0 3 2}] + // 2 + // 0 + // 0 +} + +func ExampleClient_xdel() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "race:italy") + // REMOVE_END + + _, err := rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + MaxLen: 2, + Values: map[string]interface{}{"rider": "Wood"}, + ID: "1692633198206-0", + }, + ).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: "race:italy", + MaxLen: 2, + Values: map[string]interface{}{"rider": "Henshaw"}, + ID: "1692633208557-0", + }, + ).Result() + + if err != nil { + panic(err) + } + + // STEP_START xdel + res39, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res39) + // >>> [{1692633198206-0 map[rider:Wood]} {1692633208557-0 map[rider:Henshaw]}] + + res40, err := rdb.XDel(ctx, "race:italy", "1692633208557-0").Result() + + if err != nil { + panic(err) + } + + fmt.Println(res40) // 1 + + res41, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res41) + // >>> [{1692633198206-0 map[rider:Wood]}] + // STEP_END + + // Output: + // [{1692633198206-0 map[rider:Wood]} {1692633208557-0 map[rider:Henshaw]}] + // 1 + // [{1692633198206-0 map[rider:Wood]}] +} From 80c9f5bb777dd4e6393d014cffe9d28428ecf756 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:25:46 +0000 Subject: [PATCH 02/11] DOC-4345 added JSON samples for home page (#3183) --- doctests/home_json_example_test.go | 199 +++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 doctests/home_json_example_test.go diff --git a/doctests/home_json_example_test.go b/doctests/home_json_example_test.go new file mode 100644 index 000000000..b9e46a638 --- /dev/null +++ b/doctests/home_json_example_test.go @@ -0,0 +1,199 @@ +// EXAMPLE: go_home_json +// HIDE_START +package example_commands_test + +// HIDE_END +// STEP_START import +import ( + "context" + "fmt" + "sort" + + "github.com/redis/go-redis/v9" +) + +// STEP_END + +func ExampleClient_search_json() { + // STEP_START connect + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + Protocol: 2, + }) + // STEP_END + // REMOVE_START + rdb.Del(ctx, "user:1", "user:2", "user:3") + rdb.FTDropIndex(ctx, "idx:users") + // REMOVE_END + + // STEP_START create_data + user1 := map[string]interface{}{ + "name": "Paul John", + "email": "paul.john@example.com", + "age": 42, + "city": "London", + } + + user2 := map[string]interface{}{ + "name": "Eden Zamir", + "email": "eden.zamir@example.com", + "age": 29, + "city": "Tel Aviv", + } + + user3 := map[string]interface{}{ + "name": "Paul Zamir", + "email": "paul.zamir@example.com", + "age": 35, + "city": "Tel Aviv", + } + // STEP_END + + // STEP_START make_index + _, err := rdb.FTCreate( + ctx, + "idx:users", + // Options: + &redis.FTCreateOptions{ + OnJSON: true, + Prefix: []interface{}{"user:"}, + }, + // Index schema fields: + &redis.FieldSchema{ + FieldName: "$.name", + As: "name", + FieldType: redis.SearchFieldTypeText, + }, + &redis.FieldSchema{ + FieldName: "$.city", + As: "city", + FieldType: redis.SearchFieldTypeTag, + }, + &redis.FieldSchema{ + FieldName: "$.age", + As: "age", + FieldType: redis.SearchFieldTypeNumeric, + }, + ).Result() + + if err != nil { + panic(err) + } + // STEP_END + + // STEP_START add_data + _, err = rdb.JSONSet(ctx, "user:1", "$", user1).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.JSONSet(ctx, "user:2", "$", user2).Result() + + if err != nil { + panic(err) + } + + _, err = rdb.JSONSet(ctx, "user:3", "$", user3).Result() + + if err != nil { + panic(err) + } + // STEP_END + + // STEP_START query1 + findPaulResult, err := rdb.FTSearch( + ctx, + "idx:users", + "Paul @age:[30 40]", + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(findPaulResult) + // >>> {1 [{user:3 map[$:{"age":35,"city":"Tel Aviv"... + // STEP_END + + // STEP_START query2 + citiesResult, err := rdb.FTSearchWithArgs( + ctx, + "idx:users", + "Paul", + &redis.FTSearchOptions{ + Return: []redis.FTSearchReturn{ + { + FieldName: "$.city", + As: "city", + }, + }, + }, + ).Result() + + if err != nil { + panic(err) + } + + sort.Slice(citiesResult.Docs, func(i, j int) bool { + return citiesResult.Docs[i].Fields["city"] < citiesResult.Docs[j].Fields["city"] + }) + + for _, result := range citiesResult.Docs { + fmt.Println(result.Fields["city"]) + } + // >>> London + // >>> Tel Aviv + // STEP_END + + // STEP_START query3 + aggOptions := redis.FTAggregateOptions{ + GroupBy: []redis.FTAggregateGroupBy{ + { + Fields: []interface{}{"@city"}, + Reduce: []redis.FTAggregateReducer{ + { + Reducer: redis.SearchCount, + As: "count", + }, + }, + }, + }, + } + + aggResult, err := rdb.FTAggregateWithArgs( + ctx, + "idx:users", + "*", + &aggOptions, + ).Result() + + if err != nil { + panic(err) + } + + sort.Slice(aggResult.Rows, func(i, j int) bool { + return aggResult.Rows[i].Fields["city"].(string) < + aggResult.Rows[j].Fields["city"].(string) + }) + + for _, row := range aggResult.Rows { + fmt.Printf("%v - %v\n", + row.Fields["city"], row.Fields["count"], + ) + } + // >>> City: London - 1 + // >>> City: Tel Aviv - 2 + // STEP_END + + // Output: + // {1 [{user:3 map[$:{"age":35,"city":"Tel Aviv","email":"paul.zamir@example.com","name":"Paul Zamir"}]}]} + // London + // Tel Aviv + // London - 1 + // Tel Aviv - 2 +} From d1b4eaed41a0cf43ed0e7129ad7b783971c9629a Mon Sep 17 00:00:00 2001 From: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:27:00 +0200 Subject: [PATCH 03/11] Support TimeSeries commands with RESP 2 protocol (#3184) * Support Timeseries resp 2 * Change to resp 2 * Support Resp2 for TimeSeries commands --- command.go | 54 +- timeseries_commands_test.go | 2289 +++++++++++++++++++---------------- 2 files changed, 1270 insertions(+), 1073 deletions(-) diff --git a/command.go b/command.go index 4ced2979d..7ea7862d5 100644 --- a/command.go +++ b/command.go @@ -1403,27 +1403,63 @@ func (cmd *MapStringSliceInterfaceCmd) Val() map[string][]interface{} { } func (cmd *MapStringSliceInterfaceCmd) readReply(rd *proto.Reader) (err error) { - n, err := rd.ReadMapLen() + readType, err := rd.PeekReplyType() if err != nil { return err } - cmd.val = make(map[string][]interface{}, n) - for i := 0; i < n; i++ { - k, err := rd.ReadString() + + cmd.val = make(map[string][]interface{}) + + if readType == proto.RespMap { + n, err := rd.ReadMapLen() if err != nil { return err } - nn, err := rd.ReadArrayLen() + for i := 0; i < n; i++ { + k, err := rd.ReadString() + if err != nil { + return err + } + nn, err := rd.ReadArrayLen() + if err != nil { + return err + } + cmd.val[k] = make([]interface{}, nn) + for j := 0; j < nn; j++ { + value, err := rd.ReadReply() + if err != nil { + return err + } + cmd.val[k][j] = value + } + } + } else if readType == proto.RespArray { + // RESP2 response + n, err := rd.ReadArrayLen() if err != nil { return err } - cmd.val[k] = make([]interface{}, nn) - for j := 0; j < nn; j++ { - value, err := rd.ReadReply() + + for i := 0; i < n; i++ { + // Each entry in this array is itself an array with key details + itemLen, err := rd.ReadArrayLen() if err != nil { return err } - cmd.val[k][j] = value + + key, err := rd.ReadString() + if err != nil { + return err + } + cmd.val[key] = make([]interface{}, 0, itemLen-1) + for j := 1; j < itemLen; j++ { + // Read the inner array for timestamp-value pairs + data, err := rd.ReadReply() + if err != nil { + return err + } + cmd.val[key] = append(cmd.val[key], data) + } } } diff --git a/timeseries_commands_test.go b/timeseries_commands_test.go index c62367a76..a2d4ba293 100644 --- a/timeseries_commands_test.go +++ b/timeseries_commands_test.go @@ -2,6 +2,7 @@ package redis_test import ( "context" + "fmt" "strings" . "github.com/bsm/ginkgo/v2" @@ -12,1068 +13,1228 @@ import ( var _ = Describe("RedisTimeseries commands", Label("timeseries"), func() { ctx := context.TODO() - var client *redis.Client - - BeforeEach(func() { - client = redis.NewClient(&redis.Options{Addr: rediStackAddr}) - Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - Expect(client.Close()).NotTo(HaveOccurred()) - }) - - It("should TSCreate and TSCreateWithArgs", Label("timeseries", "tscreate", "tscreateWithArgs", "NonRedisEnterprise"), func() { - result, err := client.TSCreate(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - // Test TSCreateWithArgs - opt := &redis.TSOptions{Retention: 5} - result, err = client.TSCreateWithArgs(ctx, "2", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - opt = &redis.TSOptions{Labels: map[string]string{"Redis": "Labs"}} - result, err = client.TSCreateWithArgs(ctx, "3", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - opt = &redis.TSOptions{Labels: map[string]string{"Time": "Series"}, Retention: 20} - result, err = client.TSCreateWithArgs(ctx, "4", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - resultInfo, err := client.TSInfo(ctx, "4").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["labels"].(map[interface{}]interface{})["Time"]).To(BeEquivalentTo("Series")) - // Test chunk size - opt = &redis.TSOptions{ChunkSize: 128} - result, err = client.TSCreateWithArgs(ctx, "ts-cs-1", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - resultInfo, err = client.TSInfo(ctx, "ts-cs-1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["chunkSize"]).To(BeEquivalentTo(128)) - // Test duplicate policy - duplicate_policies := []string{"BLOCK", "LAST", "FIRST", "MIN", "MAX"} - for _, dup := range duplicate_policies { - keyName := "ts-dup-" + dup - opt = &redis.TSOptions{DuplicatePolicy: dup} - result, err = client.TSCreateWithArgs(ctx, keyName, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - resultInfo, err = client.TSInfo(ctx, keyName).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(strings.ToUpper(resultInfo["duplicatePolicy"].(string))).To(BeEquivalentTo(dup)) - } - // Test insertion filters - opt = &redis.TSOptions{IgnoreMaxTimeDiff: 5, DuplicatePolicy: "LAST", IgnoreMaxValDiff: 10.0} - result, err = client.TSCreateWithArgs(ctx, "ts-if-1", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - resultAdd, err := client.TSAdd(ctx, "ts-if-1", 1000, 1.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1000)) - resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1010, 11.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1010)) - resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1013, 10.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1010)) - resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1020, 11.5).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1020)) - resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1021, 22.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1021)) - - rangePoints, err := client.TSRange(ctx, "ts-if-1", 1000, 1021).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rangePoints)).To(BeEquivalentTo(4)) - Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{ - {Timestamp: 1000, Value: 1.0}, - {Timestamp: 1010, Value: 11.0}, - {Timestamp: 1020, Value: 11.5}, - {Timestamp: 1021, Value: 22.0}})) - // Test insertion filters with other duplicate policy - opt = &redis.TSOptions{IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0} - result, err = client.TSCreateWithArgs(ctx, "ts-if-2", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - resultAdd1, err := client.TSAdd(ctx, "ts-if-1", 1000, 1.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd1).To(BeEquivalentTo(1000)) - resultAdd1, err = client.TSAdd(ctx, "ts-if-1", 1010, 11.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd1).To(BeEquivalentTo(1010)) - resultAdd1, err = client.TSAdd(ctx, "ts-if-1", 1013, 10.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd1).To(BeEquivalentTo(1013)) - - rangePoints, err = client.TSRange(ctx, "ts-if-1", 1000, 1013).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rangePoints)).To(BeEquivalentTo(3)) - Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{ - {Timestamp: 1000, Value: 1.0}, - {Timestamp: 1010, Value: 11.0}, - {Timestamp: 1013, Value: 10.0}})) - }) - It("should TSAdd and TSAddWithArgs", Label("timeseries", "tsadd", "tsaddWithArgs", "NonRedisEnterprise"), func() { - result, err := client.TSAdd(ctx, "1", 1, 1).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - // Test TSAddWithArgs - opt := &redis.TSOptions{Retention: 10} - result, err = client.TSAddWithArgs(ctx, "2", 2, 3, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(2)) - opt = &redis.TSOptions{Labels: map[string]string{"Redis": "Labs"}} - result, err = client.TSAddWithArgs(ctx, "3", 3, 2, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(3)) - opt = &redis.TSOptions{Labels: map[string]string{"Redis": "Labs", "Time": "Series"}, Retention: 10} - result, err = client.TSAddWithArgs(ctx, "4", 4, 2, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(4)) - resultInfo, err := client.TSInfo(ctx, "4").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["labels"].(map[interface{}]interface{})["Time"]).To(BeEquivalentTo("Series")) - // Test chunk size - opt = &redis.TSOptions{ChunkSize: 128} - result, err = client.TSAddWithArgs(ctx, "ts-cs-1", 1, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - resultInfo, err = client.TSInfo(ctx, "ts-cs-1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["chunkSize"]).To(BeEquivalentTo(128)) - // Test duplicate policy - // LAST - opt = &redis.TSOptions{DuplicatePolicy: "LAST"} - result, err = client.TSAddWithArgs(ctx, "tsal-1", 1, 5, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - result, err = client.TSAddWithArgs(ctx, "tsal-1", 1, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - resultGet, err := client.TSGet(ctx, "tsal-1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet.Value).To(BeEquivalentTo(10)) - // FIRST - opt = &redis.TSOptions{DuplicatePolicy: "FIRST"} - result, err = client.TSAddWithArgs(ctx, "tsaf-1", 1, 5, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - result, err = client.TSAddWithArgs(ctx, "tsaf-1", 1, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - resultGet, err = client.TSGet(ctx, "tsaf-1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet.Value).To(BeEquivalentTo(5)) - // MAX - opt = &redis.TSOptions{DuplicatePolicy: "MAX"} - result, err = client.TSAddWithArgs(ctx, "tsam-1", 1, 5, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - result, err = client.TSAddWithArgs(ctx, "tsam-1", 1, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - resultGet, err = client.TSGet(ctx, "tsam-1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet.Value).To(BeEquivalentTo(10)) - // MIN - opt = &redis.TSOptions{DuplicatePolicy: "MIN"} - result, err = client.TSAddWithArgs(ctx, "tsami-1", 1, 5, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - result, err = client.TSAddWithArgs(ctx, "tsami-1", 1, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1)) - resultGet, err = client.TSGet(ctx, "tsami-1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet.Value).To(BeEquivalentTo(5)) - // Insertion filters - opt = &redis.TSOptions{IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0, DuplicatePolicy: "LAST"} - result, err = client.TSAddWithArgs(ctx, "ts-if-1", 1000, 1.0, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1000)) - - result, err = client.TSAddWithArgs(ctx, "ts-if-1", 1004, 3.0, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(1000)) - - rangePoints, err := client.TSRange(ctx, "ts-if-1", 1000, 1004).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rangePoints)).To(BeEquivalentTo(1)) - Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: 1.0}})) - }) - - It("should TSAlter", Label("timeseries", "tsalter", "NonRedisEnterprise"), func() { - result, err := client.TSCreate(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - resultInfo, err := client.TSInfo(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(0)) - - opt := &redis.TSAlterOptions{Retention: 10} - resultAlter, err := client.TSAlter(ctx, "1", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAlter).To(BeEquivalentTo("OK")) - - resultInfo, err = client.TSInfo(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10)) - - resultInfo, err = client.TSInfo(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["labels"]).To(BeEquivalentTo(map[interface{}]interface{}{})) - - opt = &redis.TSAlterOptions{Labels: map[string]string{"Time": "Series"}} - resultAlter, err = client.TSAlter(ctx, "1", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAlter).To(BeEquivalentTo("OK")) - - resultInfo, err = client.TSInfo(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["labels"].(map[interface{}]interface{})["Time"]).To(BeEquivalentTo("Series")) - Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10)) - Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo(redis.Nil)) - opt = &redis.TSAlterOptions{DuplicatePolicy: "min"} - resultAlter, err = client.TSAlter(ctx, "1", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAlter).To(BeEquivalentTo("OK")) - - resultInfo, err = client.TSInfo(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo("min")) - // Test insertion filters - resultAdd, err := client.TSAdd(ctx, "ts-if-1", 1000, 1.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1000)) - resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1010, 11.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1010)) - resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1013, 10.0).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1013)) - - alterOpt := &redis.TSAlterOptions{IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0, DuplicatePolicy: "LAST"} - resultAlter, err = client.TSAlter(ctx, "ts-if-1", alterOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAlter).To(BeEquivalentTo("OK")) - - resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1015, 11.5).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(1013)) - - rangePoints, err := client.TSRange(ctx, "ts-if-1", 1000, 1013).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rangePoints)).To(BeEquivalentTo(3)) - Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{ - {Timestamp: 1000, Value: 1.0}, - {Timestamp: 1010, Value: 11.0}, - {Timestamp: 1013, Value: 10.0}})) - }) - - It("should TSCreateRule and TSDeleteRule", Label("timeseries", "tscreaterule", "tsdeleterule"), func() { - result, err := client.TSCreate(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - result, err = client.TSCreate(ctx, "2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - result, err = client.TSCreateRule(ctx, "1", "2", redis.Avg, 100).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo("OK")) - for i := 0; i < 50; i++ { - resultAdd, err := client.TSAdd(ctx, "1", 100+i*2, 1).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(100 + i*2)) - resultAdd, err = client.TSAdd(ctx, "1", 100+i*2+1, 2).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(100 + i*2 + 1)) - - } - resultAdd, err := client.TSAdd(ctx, "1", 100*2, 1.5).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeEquivalentTo(100 * 2)) - resultGet, err := client.TSGet(ctx, "2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet.Value).To(BeEquivalentTo(1.5)) - Expect(resultGet.Timestamp).To(BeEquivalentTo(100)) - - resultDeleteRule, err := client.TSDeleteRule(ctx, "1", "2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultDeleteRule).To(BeEquivalentTo("OK")) - resultInfo, err := client.TSInfo(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["rules"]).To(BeEquivalentTo(map[interface{}]interface{}{})) - }) - - It("should TSIncrBy, TSIncrByWithArgs, TSDecrBy and TSDecrByWithArgs", Label("timeseries", "tsincrby", "tsdecrby", "tsincrbyWithArgs", "tsdecrbyWithArgs", "NonRedisEnterprise"), func() { - for i := 0; i < 100; i++ { - _, err := client.TSIncrBy(ctx, "1", 1).Result() - Expect(err).NotTo(HaveOccurred()) - } - result, err := client.TSGet(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Value).To(BeEquivalentTo(100)) - - for i := 0; i < 100; i++ { - _, err := client.TSDecrBy(ctx, "1", 1).Result() - Expect(err).NotTo(HaveOccurred()) - } - result, err = client.TSGet(ctx, "1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Value).To(BeEquivalentTo(0)) - - opt := &redis.TSIncrDecrOptions{Timestamp: 5} - _, err = client.TSIncrByWithArgs(ctx, "2", 1.5, opt).Result() - Expect(err).NotTo(HaveOccurred()) - - result, err = client.TSGet(ctx, "2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Timestamp).To(BeEquivalentTo(5)) - Expect(result.Value).To(BeEquivalentTo(1.5)) - - opt = &redis.TSIncrDecrOptions{Timestamp: 7} - _, err = client.TSIncrByWithArgs(ctx, "2", 2.25, opt).Result() - Expect(err).NotTo(HaveOccurred()) - - result, err = client.TSGet(ctx, "2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Timestamp).To(BeEquivalentTo(7)) - Expect(result.Value).To(BeEquivalentTo(3.75)) - - opt = &redis.TSIncrDecrOptions{Timestamp: 15} - _, err = client.TSDecrByWithArgs(ctx, "2", 1.5, opt).Result() - Expect(err).NotTo(HaveOccurred()) - - result, err = client.TSGet(ctx, "2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Timestamp).To(BeEquivalentTo(15)) - Expect(result.Value).To(BeEquivalentTo(2.25)) - - // Test chunk size INCRBY - opt = &redis.TSIncrDecrOptions{ChunkSize: 128} - _, err = client.TSIncrByWithArgs(ctx, "3", 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - - resultInfo, err := client.TSInfo(ctx, "3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["chunkSize"]).To(BeEquivalentTo(128)) - - // Test chunk size DECRBY - opt = &redis.TSIncrDecrOptions{ChunkSize: 128} - _, err = client.TSDecrByWithArgs(ctx, "4", 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - - resultInfo, err = client.TSInfo(ctx, "4").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo["chunkSize"]).To(BeEquivalentTo(128)) - - // Test insertion filters INCRBY - opt = &redis.TSIncrDecrOptions{Timestamp: 1000, IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0, DuplicatePolicy: "LAST"} - res, err := client.TSIncrByWithArgs(ctx, "ts-if-1", 1.0, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(res).To(BeEquivalentTo(1000)) - - res, err = client.TSIncrByWithArgs(ctx, "ts-if-1", 3.0, &redis.TSIncrDecrOptions{Timestamp: 1000}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(res).To(BeEquivalentTo(1000)) - - rangePoints, err := client.TSRange(ctx, "ts-if-1", 1000, 1004).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rangePoints)).To(BeEquivalentTo(1)) - Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: 1.0}})) - - res, err = client.TSIncrByWithArgs(ctx, "ts-if-1", 10.1, &redis.TSIncrDecrOptions{Timestamp: 1000}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(res).To(BeEquivalentTo(1000)) - - rangePoints, err = client.TSRange(ctx, "ts-if-1", 1000, 1004).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rangePoints)).To(BeEquivalentTo(1)) - Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: 11.1}})) - - // Test insertion filters DECRBY - opt = &redis.TSIncrDecrOptions{Timestamp: 1000, IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0, DuplicatePolicy: "LAST"} - res, err = client.TSDecrByWithArgs(ctx, "ts-if-2", 1.0, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(res).To(BeEquivalentTo(1000)) - - res, err = client.TSDecrByWithArgs(ctx, "ts-if-2", 3.0, &redis.TSIncrDecrOptions{Timestamp: 1000}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(res).To(BeEquivalentTo(1000)) - - rangePoints, err = client.TSRange(ctx, "ts-if-2", 1000, 1004).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rangePoints)).To(BeEquivalentTo(1)) - Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: -1.0}})) - - res, err = client.TSDecrByWithArgs(ctx, "ts-if-2", 10.1, &redis.TSIncrDecrOptions{Timestamp: 1000}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(res).To(BeEquivalentTo(1000)) - - rangePoints, err = client.TSRange(ctx, "ts-if-2", 1000, 1004).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rangePoints)).To(BeEquivalentTo(1)) - Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: -11.1}})) - }) - - It("should TSGet", Label("timeseries", "tsget"), func() { - opt := &redis.TSOptions{DuplicatePolicy: "max"} - resultGet, err := client.TSAddWithArgs(ctx, "foo", 2265985, 151, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet).To(BeEquivalentTo(2265985)) - result, err := client.TSGet(ctx, "foo").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Timestamp).To(BeEquivalentTo(2265985)) - Expect(result.Value).To(BeEquivalentTo(151)) - }) - - It("should TSGet Latest", Label("timeseries", "tsgetlatest", "NonRedisEnterprise"), func() { - resultGet, err := client.TSCreate(ctx, "tsgl-1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet).To(BeEquivalentTo("OK")) - resultGet, err = client.TSCreate(ctx, "tsgl-2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet).To(BeEquivalentTo("OK")) - - resultGet, err = client.TSCreateRule(ctx, "tsgl-1", "tsgl-2", redis.Sum, 10).Result() - Expect(err).NotTo(HaveOccurred()) - - Expect(resultGet).To(BeEquivalentTo("OK")) - _, err = client.TSAdd(ctx, "tsgl-1", 1, 1).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "tsgl-1", 2, 3).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "tsgl-1", 11, 7).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "tsgl-1", 13, 1).Result() - Expect(err).NotTo(HaveOccurred()) - result, errGet := client.TSGet(ctx, "tsgl-2").Result() - Expect(errGet).NotTo(HaveOccurred()) - Expect(result.Timestamp).To(BeEquivalentTo(0)) - Expect(result.Value).To(BeEquivalentTo(4)) - result, errGet = client.TSGetWithArgs(ctx, "tsgl-2", &redis.TSGetOptions{Latest: true}).Result() - Expect(errGet).NotTo(HaveOccurred()) - Expect(result.Timestamp).To(BeEquivalentTo(10)) - Expect(result.Value).To(BeEquivalentTo(8)) - }) - - It("should TSInfo", Label("timeseries", "tsinfo"), func() { - resultGet, err := client.TSAdd(ctx, "foo", 2265985, 151).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet).To(BeEquivalentTo(2265985)) - result, err := client.TSInfo(ctx, "foo").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["firstTimestamp"]).To(BeEquivalentTo(2265985)) - }) - - It("should TSMAdd", Label("timeseries", "tsmadd"), func() { - resultGet, err := client.TSCreate(ctx, "a").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultGet).To(BeEquivalentTo("OK")) - ktvSlices := make([][]interface{}, 3) - for i := 0; i < 3; i++ { - ktvSlices[i] = make([]interface{}, 3) - ktvSlices[i][0] = "a" - for j := 1; j < 3; j++ { - ktvSlices[i][j] = (i + j) * j - } - } - result, err := client.TSMAdd(ctx, ktvSlices).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo([]int64{1, 2, 3})) - }) - - It("should TSMGet and TSMGetWithArgs", Label("timeseries", "tsmget", "tsmgetWithArgs", "NonRedisEnterprise"), func() { - opt := &redis.TSOptions{Labels: map[string]string{"Test": "This"}} - resultCreate, err := client.TSCreateWithArgs(ctx, "a", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - opt = &redis.TSOptions{Labels: map[string]string{"Test": "This", "Taste": "That"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "b", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - _, err = client.TSAdd(ctx, "a", "*", 15).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "b", "*", 25).Result() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.TSMGet(ctx, []string{"Test=This"}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][1].([]interface{})[1]).To(BeEquivalentTo(15)) - Expect(result["b"][1].([]interface{})[1]).To(BeEquivalentTo(25)) - mgetOpt := &redis.TSMGetOptions{WithLabels: true} - result, err = client.TSMGetWithArgs(ctx, []string{"Test=This"}, mgetOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["b"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"Test": "This", "Taste": "That"})) - - resultCreate, err = client.TSCreate(ctx, "c").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - opt = &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "d", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - resultCreateRule, err := client.TSCreateRule(ctx, "c", "d", redis.Sum, 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreateRule).To(BeEquivalentTo("OK")) - _, err = client.TSAdd(ctx, "c", 1, 1).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 2, 3).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 11, 7).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 13, 1).Result() - Expect(err).NotTo(HaveOccurred()) - result, err = client.TSMGet(ctx, []string{"is_compaction=true"}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["d"][1]).To(BeEquivalentTo([]interface{}{int64(0), 4.0})) - mgetOpt = &redis.TSMGetOptions{Latest: true} - result, err = client.TSMGetWithArgs(ctx, []string{"is_compaction=true"}, mgetOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["d"][1]).To(BeEquivalentTo([]interface{}{int64(10), 8.0})) - }) - - It("should TSQueryIndex", Label("timeseries", "tsqueryindex"), func() { - opt := &redis.TSOptions{Labels: map[string]string{"Test": "This"}} - resultCreate, err := client.TSCreateWithArgs(ctx, "a", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - opt = &redis.TSOptions{Labels: map[string]string{"Test": "This", "Taste": "That"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "b", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - result, err := client.TSQueryIndex(ctx, []string{"Test=This"}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - result, err = client.TSQueryIndex(ctx, []string{"Taste=That"}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(1)) - }) - - It("should TSDel and TSRange", Label("timeseries", "tsdel", "tsrange"), func() { - for i := 0; i < 100; i++ { - _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() - Expect(err).NotTo(HaveOccurred()) - } - resultDelete, err := client.TSDel(ctx, "a", 0, 21).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultDelete).To(BeEquivalentTo(22)) - - resultRange, err := client.TSRange(ctx, "a", 0, 21).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange).To(BeEquivalentTo([]redis.TSTimestampValue{})) - - resultRange, err = client.TSRange(ctx, "a", 22, 22).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 22, Value: 1})) - }) - - It("should TSRange, TSRangeWithArgs", Label("timeseries", "tsrange", "tsrangeWithArgs", "NonRedisEnterprise"), func() { - for i := 0; i < 100; i++ { - _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() - Expect(err).NotTo(HaveOccurred()) - - } - result, err := client.TSRange(ctx, "a", 0, 200).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(100)) - for i := 0; i < 100; i++ { - client.TSAdd(ctx, "a", i+200, float64(i%7)) - } - result, err = client.TSRange(ctx, "a", 0, 500).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(200)) - fts := make([]int, 0) - for i := 10; i < 20; i++ { - fts = append(fts, i) - } - opt := &redis.TSRangeOptions{FilterByTS: fts, FilterByValue: []int{1, 2}} - result, err = client.TSRangeWithArgs(ctx, "a", 0, 500, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - opt = &redis.TSRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "+"} - result, err = client.TSRangeWithArgs(ctx, "a", 0, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 0, Value: 10}, {Timestamp: 10, Value: 1}})) - opt = &redis.TSRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "5"} - result, err = client.TSRangeWithArgs(ctx, "a", 0, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 0, Value: 5}, {Timestamp: 5, Value: 6}})) - opt = &redis.TSRangeOptions{Aggregator: redis.Twa, BucketDuration: 10} - result, err = client.TSRangeWithArgs(ctx, "a", 0, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 0, Value: 2.55}, {Timestamp: 10, Value: 3}})) - // Test Range Latest - resultCreate, err := client.TSCreate(ctx, "t1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - resultCreate, err = client.TSCreate(ctx, "t2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - resultRule, err := client.TSCreateRule(ctx, "t1", "t2", redis.Sum, 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRule).To(BeEquivalentTo("OK")) - _, errAdd := client.TSAdd(ctx, "t1", 1, 1).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t1", 2, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t1", 11, 7).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t1", 13, 1).Result() - Expect(errAdd).NotTo(HaveOccurred()) - resultRange, err := client.TSRange(ctx, "t1", 0, 20).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 1, Value: 1})) - - opt = &redis.TSRangeOptions{Latest: true} - resultRange, err = client.TSRangeWithArgs(ctx, "t2", 0, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 0, Value: 4})) - // Test Bucket Timestamp - resultCreate, err = client.TSCreate(ctx, "t3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - _, errAdd = client.TSAdd(ctx, "t3", 15, 1).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t3", 17, 4).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t3", 51, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t3", 73, 5).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t3", 75, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - - opt = &redis.TSRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10} - resultRange, err = client.TSRangeWithArgs(ctx, "t3", 0, 100, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 10, Value: 4})) - Expect(len(resultRange)).To(BeEquivalentTo(3)) - - opt = &redis.TSRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10, BucketTimestamp: "+"} - resultRange, err = client.TSRangeWithArgs(ctx, "t3", 0, 100, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 20, Value: 4})) - Expect(len(resultRange)).To(BeEquivalentTo(3)) - // Test Empty - _, errAdd = client.TSAdd(ctx, "t4", 15, 1).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t4", 17, 4).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t4", 51, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t4", 73, 5).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t4", 75, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - - opt = &redis.TSRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10} - resultRange, err = client.TSRangeWithArgs(ctx, "t4", 0, 100, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 10, Value: 4})) - Expect(len(resultRange)).To(BeEquivalentTo(3)) - - opt = &redis.TSRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10, Empty: true} - resultRange, err = client.TSRangeWithArgs(ctx, "t4", 0, 100, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 10, Value: 4})) - Expect(len(resultRange)).To(BeEquivalentTo(7)) - }) - - It("should TSRevRange, TSRevRangeWithArgs", Label("timeseries", "tsrevrange", "tsrevrangeWithArgs", "NonRedisEnterprise"), func() { - for i := 0; i < 100; i++ { - _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() - Expect(err).NotTo(HaveOccurred()) - - } - result, err := client.TSRange(ctx, "a", 0, 200).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(100)) - for i := 0; i < 100; i++ { - client.TSAdd(ctx, "a", i+200, float64(i%7)) - } - result, err = client.TSRange(ctx, "a", 0, 500).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(200)) - - opt := &redis.TSRevRangeOptions{Aggregator: redis.Avg, BucketDuration: 10} - result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 500, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(20)) - - opt = &redis.TSRevRangeOptions{Count: 10} - result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 500, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(10)) - - fts := make([]int, 0) - for i := 10; i < 20; i++ { - fts = append(fts, i) - } - opt = &redis.TSRevRangeOptions{FilterByTS: fts, FilterByValue: []int{1, 2}} - result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 500, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - - opt = &redis.TSRevRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "+"} - result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 10, Value: 1}, {Timestamp: 0, Value: 10}})) - - opt = &redis.TSRevRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "1"} - result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1, Value: 10}, {Timestamp: 0, Value: 1}})) - - opt = &redis.TSRevRangeOptions{Aggregator: redis.Twa, BucketDuration: 10} - result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 10, Value: 3}, {Timestamp: 0, Value: 2.55}})) - // Test Range Latest - resultCreate, err := client.TSCreate(ctx, "t1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - resultCreate, err = client.TSCreate(ctx, "t2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - resultRule, err := client.TSCreateRule(ctx, "t1", "t2", redis.Sum, 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRule).To(BeEquivalentTo("OK")) - _, errAdd := client.TSAdd(ctx, "t1", 1, 1).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t1", 2, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t1", 11, 7).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t1", 13, 1).Result() - Expect(errAdd).NotTo(HaveOccurred()) - resultRange, err := client.TSRange(ctx, "t2", 0, 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 0, Value: 4})) - opt = &redis.TSRevRangeOptions{Latest: true} - resultRange, err = client.TSRevRangeWithArgs(ctx, "t2", 0, 10, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 10, Value: 8})) - resultRange, err = client.TSRevRangeWithArgs(ctx, "t2", 0, 9, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 0, Value: 4})) - // Test Bucket Timestamp - resultCreate, err = client.TSCreate(ctx, "t3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - _, errAdd = client.TSAdd(ctx, "t3", 15, 1).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t3", 17, 4).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t3", 51, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t3", 73, 5).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t3", 75, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - - opt = &redis.TSRevRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10} - resultRange, err = client.TSRevRangeWithArgs(ctx, "t3", 0, 100, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 70, Value: 5})) - Expect(len(resultRange)).To(BeEquivalentTo(3)) - - opt = &redis.TSRevRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10, BucketTimestamp: "+"} - resultRange, err = client.TSRevRangeWithArgs(ctx, "t3", 0, 100, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 80, Value: 5})) - Expect(len(resultRange)).To(BeEquivalentTo(3)) - // Test Empty - _, errAdd = client.TSAdd(ctx, "t4", 15, 1).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t4", 17, 4).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t4", 51, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t4", 73, 5).Result() - Expect(errAdd).NotTo(HaveOccurred()) - _, errAdd = client.TSAdd(ctx, "t4", 75, 3).Result() - Expect(errAdd).NotTo(HaveOccurred()) - - opt = &redis.TSRevRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10} - resultRange, err = client.TSRevRangeWithArgs(ctx, "t4", 0, 100, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 70, Value: 5})) - Expect(len(resultRange)).To(BeEquivalentTo(3)) - - opt = &redis.TSRevRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10, Empty: true} - resultRange, err = client.TSRevRangeWithArgs(ctx, "t4", 0, 100, opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 70, Value: 5})) - Expect(len(resultRange)).To(BeEquivalentTo(7)) - }) - - It("should TSMRange and TSMRangeWithArgs", Label("timeseries", "tsmrange", "tsmrangeWithArgs"), func() { - createOpt := &redis.TSOptions{Labels: map[string]string{"Test": "This", "team": "ny"}} - resultCreate, err := client.TSCreateWithArgs(ctx, "a", createOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - createOpt = &redis.TSOptions{Labels: map[string]string{"Test": "This", "Taste": "That", "team": "sf"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "b", createOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - - for i := 0; i < 100; i++ { - _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "b", i, float64(i%11)).Result() - Expect(err).NotTo(HaveOccurred()) - } - - result, err := client.TSMRange(ctx, 0, 200, []string{"Test=This"}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(100)) - // Test Count - mrangeOpt := &redis.TSMRangeOptions{Count: 10} - result, err = client.TSMRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(10)) - // Test Aggregation and BucketDuration - for i := 0; i < 100; i++ { - _, err := client.TSAdd(ctx, "a", i+200, float64(i%7)).Result() - Expect(err).NotTo(HaveOccurred()) - } - mrangeOpt = &redis.TSMRangeOptions{Aggregator: redis.Avg, BucketDuration: 10} - result, err = client.TSMRangeWithArgs(ctx, 0, 500, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(20)) - // Test WithLabels - Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{})) - mrangeOpt = &redis.TSMRangeOptions{WithLabels: true} - result, err = client.TSMRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"Test": "This", "team": "ny"})) - // Test SelectedLabels - mrangeOpt = &redis.TSMRangeOptions{SelectedLabels: []interface{}{"team"}} - result, err = client.TSMRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"team": "ny"})) - Expect(result["b"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"team": "sf"})) - // Test FilterBy - fts := make([]int, 0) - for i := 10; i < 20; i++ { - fts = append(fts, i) - } - mrangeOpt = &redis.TSMRangeOptions{FilterByTS: fts, FilterByValue: []int{1, 2}} - result, err = client.TSMRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(15), 1.0}, []interface{}{int64(16), 2.0}})) - // Test GroupBy - mrangeOpt = &redis.TSMRangeOptions{GroupByLabel: "Test", Reducer: "sum"} - result, err = client.TSMRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["Test=This"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 0.0}, []interface{}{int64(1), 2.0}, []interface{}{int64(2), 4.0}, []interface{}{int64(3), 6.0}})) - - mrangeOpt = &redis.TSMRangeOptions{GroupByLabel: "Test", Reducer: "max"} - result, err = client.TSMRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["Test=This"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 0.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(3), 3.0}})) - - mrangeOpt = &redis.TSMRangeOptions{GroupByLabel: "team", Reducer: "min"} - result, err = client.TSMRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - Expect(result["team=ny"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 0.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(3), 3.0}})) - Expect(result["team=sf"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 0.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(3), 3.0}})) - // Test Align - mrangeOpt = &redis.TSMRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "-"} - result, err = client.TSMRangeWithArgs(ctx, 0, 10, []string{"team=ny"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 10.0}, []interface{}{int64(10), 1.0}})) - - mrangeOpt = &redis.TSMRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: 5} - result, err = client.TSMRangeWithArgs(ctx, 0, 10, []string{"team=ny"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 5.0}, []interface{}{int64(5), 6.0}})) - }) - - It("should TSMRangeWithArgs Latest", Label("timeseries", "tsmrangeWithArgs", "tsmrangelatest", "NonRedisEnterprise"), func() { - resultCreate, err := client.TSCreate(ctx, "a").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - opt := &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "b", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - - resultCreate, err = client.TSCreate(ctx, "c").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - opt = &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "d", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - - resultCreateRule, err := client.TSCreateRule(ctx, "a", "b", redis.Sum, 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreateRule).To(BeEquivalentTo("OK")) - resultCreateRule, err = client.TSCreateRule(ctx, "c", "d", redis.Sum, 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreateRule).To(BeEquivalentTo("OK")) - - _, err = client.TSAdd(ctx, "a", 1, 1).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "a", 2, 3).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "a", 11, 7).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "a", 13, 1).Result() - Expect(err).NotTo(HaveOccurred()) - - _, err = client.TSAdd(ctx, "c", 1, 1).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 2, 3).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 11, 7).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 13, 1).Result() - Expect(err).NotTo(HaveOccurred()) - mrangeOpt := &redis.TSMRangeOptions{Latest: true} - result, err := client.TSMRangeWithArgs(ctx, 0, 10, []string{"is_compaction=true"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["b"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 4.0}, []interface{}{int64(10), 8.0}})) - Expect(result["d"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 4.0}, []interface{}{int64(10), 8.0}})) - }) - It("should TSMRevRange and TSMRevRangeWithArgs", Label("timeseries", "tsmrevrange", "tsmrevrangeWithArgs"), func() { - createOpt := &redis.TSOptions{Labels: map[string]string{"Test": "This", "team": "ny"}} - resultCreate, err := client.TSCreateWithArgs(ctx, "a", createOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - createOpt = &redis.TSOptions{Labels: map[string]string{"Test": "This", "Taste": "That", "team": "sf"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "b", createOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - - for i := 0; i < 100; i++ { - _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "b", i, float64(i%11)).Result() - Expect(err).NotTo(HaveOccurred()) - } - result, err := client.TSMRevRange(ctx, 0, 200, []string{"Test=This"}).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(100)) - // Test Count - mrangeOpt := &redis.TSMRevRangeOptions{Count: 10} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(10)) - // Test Aggregation and BucketDuration - for i := 0; i < 100; i++ { - _, err := client.TSAdd(ctx, "a", i+200, float64(i%7)).Result() - Expect(err).NotTo(HaveOccurred()) - } - mrangeOpt = &redis.TSMRevRangeOptions{Aggregator: redis.Avg, BucketDuration: 10} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 500, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(20)) - Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{})) - // Test WithLabels - Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{})) - mrangeOpt = &redis.TSMRevRangeOptions{WithLabels: true} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"Test": "This", "team": "ny"})) - // Test SelectedLabels - mrangeOpt = &redis.TSMRevRangeOptions{SelectedLabels: []interface{}{"team"}} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"team": "ny"})) - Expect(result["b"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"team": "sf"})) - // Test FilterBy - fts := make([]int, 0) - for i := 10; i < 20; i++ { - fts = append(fts, i) - } - mrangeOpt = &redis.TSMRevRangeOptions{FilterByTS: fts, FilterByValue: []int{1, 2}} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(16), 2.0}, []interface{}{int64(15), 1.0}})) - // Test GroupBy - mrangeOpt = &redis.TSMRevRangeOptions{GroupByLabel: "Test", Reducer: "sum"} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["Test=This"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), 6.0}, []interface{}{int64(2), 4.0}, []interface{}{int64(1), 2.0}, []interface{}{int64(0), 0.0}})) - - mrangeOpt = &redis.TSMRevRangeOptions{GroupByLabel: "Test", Reducer: "max"} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["Test=This"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), 3.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(0), 0.0}})) - - mrangeOpt = &redis.TSMRevRangeOptions{GroupByLabel: "team", Reducer: "min"} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(2)) - Expect(result["team=ny"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), 3.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(0), 0.0}})) - Expect(result["team=sf"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), 3.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(0), 0.0}})) - // Test Align - mrangeOpt = &redis.TSMRevRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "-"} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 10, []string{"team=ny"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), 1.0}, []interface{}{int64(0), 10.0}})) - - mrangeOpt = &redis.TSMRevRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: 1} - result, err = client.TSMRevRangeWithArgs(ctx, 0, 10, []string{"team=ny"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(1), 10.0}, []interface{}{int64(0), 1.0}})) - }) - - It("should TSMRevRangeWithArgs Latest", Label("timeseries", "tsmrevrangeWithArgs", "tsmrevrangelatest", "NonRedisEnterprise"), func() { - resultCreate, err := client.TSCreate(ctx, "a").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - opt := &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "b", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - - resultCreate, err = client.TSCreate(ctx, "c").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - opt = &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} - resultCreate, err = client.TSCreateWithArgs(ctx, "d", opt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreate).To(BeEquivalentTo("OK")) - - resultCreateRule, err := client.TSCreateRule(ctx, "a", "b", redis.Sum, 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreateRule).To(BeEquivalentTo("OK")) - resultCreateRule, err = client.TSCreateRule(ctx, "c", "d", redis.Sum, 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultCreateRule).To(BeEquivalentTo("OK")) - - _, err = client.TSAdd(ctx, "a", 1, 1).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "a", 2, 3).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "a", 11, 7).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "a", 13, 1).Result() - Expect(err).NotTo(HaveOccurred()) - - _, err = client.TSAdd(ctx, "c", 1, 1).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 2, 3).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 11, 7).Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.TSAdd(ctx, "c", 13, 1).Result() - Expect(err).NotTo(HaveOccurred()) - mrangeOpt := &redis.TSMRevRangeOptions{Latest: true} - result, err := client.TSMRevRangeWithArgs(ctx, 0, 10, []string{"is_compaction=true"}, mrangeOpt).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result["b"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), 8.0}, []interface{}{int64(0), 4.0}})) - Expect(result["d"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), 8.0}, []interface{}{int64(0), 4.0}})) - }) + + setupRedisClient := func(protocolVersion int) *redis.Client { + return redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + DB: 0, + Protocol: protocolVersion, + UnstableResp3: true, + }) + } + + protocols := []int{2, 3} + for _, protocol := range protocols { + protocol := protocol // capture loop variable for each context + + Context(fmt.Sprintf("with protocol version %d", protocol), func() { + var client *redis.Client + + BeforeEach(func() { + client = setupRedisClient(protocol) + Expect(client.FlushAll(ctx).Err()).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + if client != nil { + client.FlushDB(ctx) + client.Close() + } + }) + + It("should TSCreate and TSCreateWithArgs", Label("timeseries", "tscreate", "tscreateWithArgs", "NonRedisEnterprise"), func() { + result, err := client.TSCreate(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + // Test TSCreateWithArgs + opt := &redis.TSOptions{Retention: 5} + result, err = client.TSCreateWithArgs(ctx, "2", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + opt = &redis.TSOptions{Labels: map[string]string{"Redis": "Labs"}} + result, err = client.TSCreateWithArgs(ctx, "3", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + opt = &redis.TSOptions{Labels: map[string]string{"Time": "Series"}, Retention: 20} + result, err = client.TSCreateWithArgs(ctx, "4", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + resultInfo, err := client.TSInfo(ctx, "4").Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(resultInfo["labels"].([]interface{})[0]).To(BeEquivalentTo([]interface{}{"Time", "Series"})) + } else { + Expect(resultInfo["labels"].(map[interface{}]interface{})["Time"]).To(BeEquivalentTo("Series")) + } + // Test chunk size + opt = &redis.TSOptions{ChunkSize: 128} + result, err = client.TSCreateWithArgs(ctx, "ts-cs-1", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + resultInfo, err = client.TSInfo(ctx, "ts-cs-1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo["chunkSize"]).To(BeEquivalentTo(128)) + // Test duplicate policy + duplicate_policies := []string{"BLOCK", "LAST", "FIRST", "MIN", "MAX"} + for _, dup := range duplicate_policies { + keyName := "ts-dup-" + dup + opt = &redis.TSOptions{DuplicatePolicy: dup} + result, err = client.TSCreateWithArgs(ctx, keyName, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + resultInfo, err = client.TSInfo(ctx, keyName).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(strings.ToUpper(resultInfo["duplicatePolicy"].(string))).To(BeEquivalentTo(dup)) + } + // Test insertion filters + opt = &redis.TSOptions{IgnoreMaxTimeDiff: 5, DuplicatePolicy: "LAST", IgnoreMaxValDiff: 10.0} + result, err = client.TSCreateWithArgs(ctx, "ts-if-1", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + resultAdd, err := client.TSAdd(ctx, "ts-if-1", 1000, 1.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1000)) + resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1010, 11.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1010)) + resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1013, 10.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1010)) + resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1020, 11.5).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1020)) + resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1021, 22.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1021)) + + rangePoints, err := client.TSRange(ctx, "ts-if-1", 1000, 1021).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rangePoints)).To(BeEquivalentTo(4)) + Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{ + {Timestamp: 1000, Value: 1.0}, + {Timestamp: 1010, Value: 11.0}, + {Timestamp: 1020, Value: 11.5}, + {Timestamp: 1021, Value: 22.0}})) + // Test insertion filters with other duplicate policy + opt = &redis.TSOptions{IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0} + result, err = client.TSCreateWithArgs(ctx, "ts-if-2", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + resultAdd1, err := client.TSAdd(ctx, "ts-if-1", 1000, 1.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd1).To(BeEquivalentTo(1000)) + resultAdd1, err = client.TSAdd(ctx, "ts-if-1", 1010, 11.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd1).To(BeEquivalentTo(1010)) + resultAdd1, err = client.TSAdd(ctx, "ts-if-1", 1013, 10.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd1).To(BeEquivalentTo(1013)) + + rangePoints, err = client.TSRange(ctx, "ts-if-1", 1000, 1013).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rangePoints)).To(BeEquivalentTo(3)) + Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{ + {Timestamp: 1000, Value: 1.0}, + {Timestamp: 1010, Value: 11.0}, + {Timestamp: 1013, Value: 10.0}})) + }) + It("should TSAdd and TSAddWithArgs", Label("timeseries", "tsadd", "tsaddWithArgs", "NonRedisEnterprise"), func() { + result, err := client.TSAdd(ctx, "1", 1, 1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + // Test TSAddWithArgs + opt := &redis.TSOptions{Retention: 10} + result, err = client.TSAddWithArgs(ctx, "2", 2, 3, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(2)) + opt = &redis.TSOptions{Labels: map[string]string{"Redis": "Labs"}} + result, err = client.TSAddWithArgs(ctx, "3", 3, 2, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(3)) + opt = &redis.TSOptions{Labels: map[string]string{"Redis": "Labs", "Time": "Series"}, Retention: 10} + result, err = client.TSAddWithArgs(ctx, "4", 4, 2, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(4)) + resultInfo, err := client.TSInfo(ctx, "4").Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(resultInfo["labels"].([]interface{})).To(ContainElement([]interface{}{"Time", "Series"})) + } else { + Expect(resultInfo["labels"].(map[interface{}]interface{})["Time"]).To(BeEquivalentTo("Series")) + } + // Test chunk size + opt = &redis.TSOptions{ChunkSize: 128} + result, err = client.TSAddWithArgs(ctx, "ts-cs-1", 1, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + resultInfo, err = client.TSInfo(ctx, "ts-cs-1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo["chunkSize"]).To(BeEquivalentTo(128)) + // Test duplicate policy + // LAST + opt = &redis.TSOptions{DuplicatePolicy: "LAST"} + result, err = client.TSAddWithArgs(ctx, "tsal-1", 1, 5, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + result, err = client.TSAddWithArgs(ctx, "tsal-1", 1, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + resultGet, err := client.TSGet(ctx, "tsal-1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet.Value).To(BeEquivalentTo(10)) + // FIRST + opt = &redis.TSOptions{DuplicatePolicy: "FIRST"} + result, err = client.TSAddWithArgs(ctx, "tsaf-1", 1, 5, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + result, err = client.TSAddWithArgs(ctx, "tsaf-1", 1, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + resultGet, err = client.TSGet(ctx, "tsaf-1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet.Value).To(BeEquivalentTo(5)) + // MAX + opt = &redis.TSOptions{DuplicatePolicy: "MAX"} + result, err = client.TSAddWithArgs(ctx, "tsam-1", 1, 5, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + result, err = client.TSAddWithArgs(ctx, "tsam-1", 1, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + resultGet, err = client.TSGet(ctx, "tsam-1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet.Value).To(BeEquivalentTo(10)) + // MIN + opt = &redis.TSOptions{DuplicatePolicy: "MIN"} + result, err = client.TSAddWithArgs(ctx, "tsami-1", 1, 5, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + result, err = client.TSAddWithArgs(ctx, "tsami-1", 1, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1)) + resultGet, err = client.TSGet(ctx, "tsami-1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet.Value).To(BeEquivalentTo(5)) + // Insertion filters + opt = &redis.TSOptions{IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0, DuplicatePolicy: "LAST"} + result, err = client.TSAddWithArgs(ctx, "ts-if-1", 1000, 1.0, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1000)) + + result, err = client.TSAddWithArgs(ctx, "ts-if-1", 1004, 3.0, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(1000)) + + rangePoints, err := client.TSRange(ctx, "ts-if-1", 1000, 1004).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rangePoints)).To(BeEquivalentTo(1)) + Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: 1.0}})) + }) + + It("should TSAlter", Label("timeseries", "tsalter", "NonRedisEnterprise"), func() { + result, err := client.TSCreate(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + resultInfo, err := client.TSInfo(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(0)) + + opt := &redis.TSAlterOptions{Retention: 10} + resultAlter, err := client.TSAlter(ctx, "1", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAlter).To(BeEquivalentTo("OK")) + + resultInfo, err = client.TSInfo(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10)) + + resultInfo, err = client.TSInfo(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(resultInfo["labels"]).To(BeEquivalentTo([]interface{}{})) + } else { + Expect(resultInfo["labels"]).To(BeEquivalentTo(map[interface{}]interface{}{})) + } + + opt = &redis.TSAlterOptions{Labels: map[string]string{"Time": "Series"}} + resultAlter, err = client.TSAlter(ctx, "1", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAlter).To(BeEquivalentTo("OK")) + + resultInfo, err = client.TSInfo(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(resultInfo["labels"].([]interface{})[0]).To(BeEquivalentTo([]interface{}{"Time", "Series"})) + Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10)) + Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo(redis.Nil)) + } else { + Expect(resultInfo["labels"].(map[interface{}]interface{})["Time"]).To(BeEquivalentTo("Series")) + Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10)) + Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo(redis.Nil)) + } + opt = &redis.TSAlterOptions{DuplicatePolicy: "min"} + resultAlter, err = client.TSAlter(ctx, "1", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAlter).To(BeEquivalentTo("OK")) + + resultInfo, err = client.TSInfo(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo("min")) + // Test insertion filters + resultAdd, err := client.TSAdd(ctx, "ts-if-1", 1000, 1.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1000)) + resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1010, 11.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1010)) + resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1013, 10.0).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1013)) + + alterOpt := &redis.TSAlterOptions{IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0, DuplicatePolicy: "LAST"} + resultAlter, err = client.TSAlter(ctx, "ts-if-1", alterOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAlter).To(BeEquivalentTo("OK")) + + resultAdd, err = client.TSAdd(ctx, "ts-if-1", 1015, 11.5).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(1013)) + + rangePoints, err := client.TSRange(ctx, "ts-if-1", 1000, 1013).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rangePoints)).To(BeEquivalentTo(3)) + Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{ + {Timestamp: 1000, Value: 1.0}, + {Timestamp: 1010, Value: 11.0}, + {Timestamp: 1013, Value: 10.0}})) + }) + + It("should TSCreateRule and TSDeleteRule", Label("timeseries", "tscreaterule", "tsdeleterule"), func() { + result, err := client.TSCreate(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + result, err = client.TSCreate(ctx, "2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + result, err = client.TSCreateRule(ctx, "1", "2", redis.Avg, 100).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo("OK")) + for i := 0; i < 50; i++ { + resultAdd, err := client.TSAdd(ctx, "1", 100+i*2, 1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(100 + i*2)) + resultAdd, err = client.TSAdd(ctx, "1", 100+i*2+1, 2).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(100 + i*2 + 1)) + + } + resultAdd, err := client.TSAdd(ctx, "1", 100*2, 1.5).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeEquivalentTo(100 * 2)) + resultGet, err := client.TSGet(ctx, "2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet.Value).To(BeEquivalentTo(1.5)) + Expect(resultGet.Timestamp).To(BeEquivalentTo(100)) + + resultDeleteRule, err := client.TSDeleteRule(ctx, "1", "2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultDeleteRule).To(BeEquivalentTo("OK")) + resultInfo, err := client.TSInfo(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(resultInfo["rules"]).To(BeEquivalentTo([]interface{}{})) + } else { + Expect(resultInfo["rules"]).To(BeEquivalentTo(map[interface{}]interface{}{})) + } + }) + + It("should TSIncrBy, TSIncrByWithArgs, TSDecrBy and TSDecrByWithArgs", Label("timeseries", "tsincrby", "tsdecrby", "tsincrbyWithArgs", "tsdecrbyWithArgs", "NonRedisEnterprise"), func() { + for i := 0; i < 100; i++ { + _, err := client.TSIncrBy(ctx, "1", 1).Result() + Expect(err).NotTo(HaveOccurred()) + } + result, err := client.TSGet(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Value).To(BeEquivalentTo(100)) + + for i := 0; i < 100; i++ { + _, err := client.TSDecrBy(ctx, "1", 1).Result() + Expect(err).NotTo(HaveOccurred()) + } + result, err = client.TSGet(ctx, "1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Value).To(BeEquivalentTo(0)) + + opt := &redis.TSIncrDecrOptions{Timestamp: 5} + _, err = client.TSIncrByWithArgs(ctx, "2", 1.5, opt).Result() + Expect(err).NotTo(HaveOccurred()) + + result, err = client.TSGet(ctx, "2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Timestamp).To(BeEquivalentTo(5)) + Expect(result.Value).To(BeEquivalentTo(1.5)) + + opt = &redis.TSIncrDecrOptions{Timestamp: 7} + _, err = client.TSIncrByWithArgs(ctx, "2", 2.25, opt).Result() + Expect(err).NotTo(HaveOccurred()) + + result, err = client.TSGet(ctx, "2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Timestamp).To(BeEquivalentTo(7)) + Expect(result.Value).To(BeEquivalentTo(3.75)) + + opt = &redis.TSIncrDecrOptions{Timestamp: 15} + _, err = client.TSDecrByWithArgs(ctx, "2", 1.5, opt).Result() + Expect(err).NotTo(HaveOccurred()) + + result, err = client.TSGet(ctx, "2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Timestamp).To(BeEquivalentTo(15)) + Expect(result.Value).To(BeEquivalentTo(2.25)) + + // Test chunk size INCRBY + opt = &redis.TSIncrDecrOptions{ChunkSize: 128} + _, err = client.TSIncrByWithArgs(ctx, "3", 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + + resultInfo, err := client.TSInfo(ctx, "3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo["chunkSize"]).To(BeEquivalentTo(128)) + + // Test chunk size DECRBY + opt = &redis.TSIncrDecrOptions{ChunkSize: 128} + _, err = client.TSDecrByWithArgs(ctx, "4", 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + + resultInfo, err = client.TSInfo(ctx, "4").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo["chunkSize"]).To(BeEquivalentTo(128)) + + // Test insertion filters INCRBY + opt = &redis.TSIncrDecrOptions{Timestamp: 1000, IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0, DuplicatePolicy: "LAST"} + res, err := client.TSIncrByWithArgs(ctx, "ts-if-1", 1.0, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(BeEquivalentTo(1000)) + + res, err = client.TSIncrByWithArgs(ctx, "ts-if-1", 3.0, &redis.TSIncrDecrOptions{Timestamp: 1000}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(BeEquivalentTo(1000)) + + rangePoints, err := client.TSRange(ctx, "ts-if-1", 1000, 1004).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rangePoints)).To(BeEquivalentTo(1)) + Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: 1.0}})) + + res, err = client.TSIncrByWithArgs(ctx, "ts-if-1", 10.1, &redis.TSIncrDecrOptions{Timestamp: 1000}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(BeEquivalentTo(1000)) + + rangePoints, err = client.TSRange(ctx, "ts-if-1", 1000, 1004).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rangePoints)).To(BeEquivalentTo(1)) + Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: 11.1}})) + + // Test insertion filters DECRBY + opt = &redis.TSIncrDecrOptions{Timestamp: 1000, IgnoreMaxTimeDiff: 5, IgnoreMaxValDiff: 10.0, DuplicatePolicy: "LAST"} + res, err = client.TSDecrByWithArgs(ctx, "ts-if-2", 1.0, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(BeEquivalentTo(1000)) + + res, err = client.TSDecrByWithArgs(ctx, "ts-if-2", 3.0, &redis.TSIncrDecrOptions{Timestamp: 1000}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(BeEquivalentTo(1000)) + + rangePoints, err = client.TSRange(ctx, "ts-if-2", 1000, 1004).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rangePoints)).To(BeEquivalentTo(1)) + Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: -1.0}})) + + res, err = client.TSDecrByWithArgs(ctx, "ts-if-2", 10.1, &redis.TSIncrDecrOptions{Timestamp: 1000}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(BeEquivalentTo(1000)) + + rangePoints, err = client.TSRange(ctx, "ts-if-2", 1000, 1004).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rangePoints)).To(BeEquivalentTo(1)) + Expect(rangePoints).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1000, Value: -11.1}})) + }) + + It("should TSGet", Label("timeseries", "tsget"), func() { + opt := &redis.TSOptions{DuplicatePolicy: "max"} + resultGet, err := client.TSAddWithArgs(ctx, "foo", 2265985, 151, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet).To(BeEquivalentTo(2265985)) + result, err := client.TSGet(ctx, "foo").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Timestamp).To(BeEquivalentTo(2265985)) + Expect(result.Value).To(BeEquivalentTo(151)) + }) + + It("should TSGet Latest", Label("timeseries", "tsgetlatest", "NonRedisEnterprise"), func() { + resultGet, err := client.TSCreate(ctx, "tsgl-1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet).To(BeEquivalentTo("OK")) + resultGet, err = client.TSCreate(ctx, "tsgl-2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet).To(BeEquivalentTo("OK")) + + resultGet, err = client.TSCreateRule(ctx, "tsgl-1", "tsgl-2", redis.Sum, 10).Result() + Expect(err).NotTo(HaveOccurred()) + + Expect(resultGet).To(BeEquivalentTo("OK")) + _, err = client.TSAdd(ctx, "tsgl-1", 1, 1).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "tsgl-1", 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "tsgl-1", 11, 7).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "tsgl-1", 13, 1).Result() + Expect(err).NotTo(HaveOccurred()) + result, errGet := client.TSGet(ctx, "tsgl-2").Result() + Expect(errGet).NotTo(HaveOccurred()) + Expect(result.Timestamp).To(BeEquivalentTo(0)) + Expect(result.Value).To(BeEquivalentTo(4)) + result, errGet = client.TSGetWithArgs(ctx, "tsgl-2", &redis.TSGetOptions{Latest: true}).Result() + Expect(errGet).NotTo(HaveOccurred()) + Expect(result.Timestamp).To(BeEquivalentTo(10)) + Expect(result.Value).To(BeEquivalentTo(8)) + }) + + It("should TSInfo", Label("timeseries", "tsinfo"), func() { + resultGet, err := client.TSAdd(ctx, "foo", 2265985, 151).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet).To(BeEquivalentTo(2265985)) + result, err := client.TSInfo(ctx, "foo").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result["firstTimestamp"]).To(BeEquivalentTo(2265985)) + }) + + It("should TSMAdd", Label("timeseries", "tsmadd"), func() { + resultGet, err := client.TSCreate(ctx, "a").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultGet).To(BeEquivalentTo("OK")) + ktvSlices := make([][]interface{}, 3) + for i := 0; i < 3; i++ { + ktvSlices[i] = make([]interface{}, 3) + ktvSlices[i][0] = "a" + for j := 1; j < 3; j++ { + ktvSlices[i][j] = (i + j) * j + } + } + result, err := client.TSMAdd(ctx, ktvSlices).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo([]int64{1, 2, 3})) + }) + + It("should TSMGet and TSMGetWithArgs", Label("timeseries", "tsmget", "tsmgetWithArgs", "NonRedisEnterprise"), func() { + opt := &redis.TSOptions{Labels: map[string]string{"Test": "This"}} + resultCreate, err := client.TSCreateWithArgs(ctx, "a", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + opt = &redis.TSOptions{Labels: map[string]string{"Test": "This", "Taste": "That"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "b", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + _, err = client.TSAdd(ctx, "a", "*", 15).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "b", "*", 25).Result() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.TSMGet(ctx, []string{"Test=This"}).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][1].([]interface{})[1]).To(BeEquivalentTo("15")) + Expect(result["b"][1].([]interface{})[1]).To(BeEquivalentTo("25")) + } else { + Expect(result["a"][1].([]interface{})[1]).To(BeEquivalentTo(15)) + Expect(result["b"][1].([]interface{})[1]).To(BeEquivalentTo(25)) + } + mgetOpt := &redis.TSMGetOptions{WithLabels: true} + result, err = client.TSMGetWithArgs(ctx, []string{"Test=This"}, mgetOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["b"][0]).To(ConsistOf([]interface{}{"Test", "This"}, []interface{}{"Taste", "That"})) + } else { + Expect(result["b"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"Test": "This", "Taste": "That"})) + } + + resultCreate, err = client.TSCreate(ctx, "c").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + opt = &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "d", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + resultCreateRule, err := client.TSCreateRule(ctx, "c", "d", redis.Sum, 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreateRule).To(BeEquivalentTo("OK")) + _, err = client.TSAdd(ctx, "c", 1, 1).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 11, 7).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 13, 1).Result() + Expect(err).NotTo(HaveOccurred()) + result, err = client.TSMGet(ctx, []string{"is_compaction=true"}).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["d"][1]).To(BeEquivalentTo([]interface{}{int64(0), "4"})) + } else { + Expect(result["d"][1]).To(BeEquivalentTo([]interface{}{int64(0), 4.0})) + } + mgetOpt = &redis.TSMGetOptions{Latest: true} + result, err = client.TSMGetWithArgs(ctx, []string{"is_compaction=true"}, mgetOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["d"][1]).To(BeEquivalentTo([]interface{}{int64(10), "8"})) + } else { + Expect(result["d"][1]).To(BeEquivalentTo([]interface{}{int64(10), 8.0})) + } + }) + + It("should TSQueryIndex", Label("timeseries", "tsqueryindex"), func() { + opt := &redis.TSOptions{Labels: map[string]string{"Test": "This"}} + resultCreate, err := client.TSCreateWithArgs(ctx, "a", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + opt = &redis.TSOptions{Labels: map[string]string{"Test": "This", "Taste": "That"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "b", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + result, err := client.TSQueryIndex(ctx, []string{"Test=This"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + result, err = client.TSQueryIndex(ctx, []string{"Taste=That"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(1)) + }) + + It("should TSDel and TSRange", Label("timeseries", "tsdel", "tsrange"), func() { + for i := 0; i < 100; i++ { + _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() + Expect(err).NotTo(HaveOccurred()) + } + resultDelete, err := client.TSDel(ctx, "a", 0, 21).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultDelete).To(BeEquivalentTo(22)) + + resultRange, err := client.TSRange(ctx, "a", 0, 21).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange).To(BeEquivalentTo([]redis.TSTimestampValue{})) + + resultRange, err = client.TSRange(ctx, "a", 22, 22).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 22, Value: 1})) + }) + + It("should TSRange, TSRangeWithArgs", Label("timeseries", "tsrange", "tsrangeWithArgs", "NonRedisEnterprise"), func() { + for i := 0; i < 100; i++ { + _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() + Expect(err).NotTo(HaveOccurred()) + + } + result, err := client.TSRange(ctx, "a", 0, 200).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(100)) + for i := 0; i < 100; i++ { + client.TSAdd(ctx, "a", i+200, float64(i%7)) + } + result, err = client.TSRange(ctx, "a", 0, 500).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(200)) + fts := make([]int, 0) + for i := 10; i < 20; i++ { + fts = append(fts, i) + } + opt := &redis.TSRangeOptions{FilterByTS: fts, FilterByValue: []int{1, 2}} + result, err = client.TSRangeWithArgs(ctx, "a", 0, 500, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + opt = &redis.TSRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "+"} + result, err = client.TSRangeWithArgs(ctx, "a", 0, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 0, Value: 10}, {Timestamp: 10, Value: 1}})) + opt = &redis.TSRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "5"} + result, err = client.TSRangeWithArgs(ctx, "a", 0, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 0, Value: 5}, {Timestamp: 5, Value: 6}})) + opt = &redis.TSRangeOptions{Aggregator: redis.Twa, BucketDuration: 10} + result, err = client.TSRangeWithArgs(ctx, "a", 0, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 0, Value: 2.55}, {Timestamp: 10, Value: 3}})) + // Test Range Latest + resultCreate, err := client.TSCreate(ctx, "t1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + resultCreate, err = client.TSCreate(ctx, "t2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + resultRule, err := client.TSCreateRule(ctx, "t1", "t2", redis.Sum, 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRule).To(BeEquivalentTo("OK")) + _, errAdd := client.TSAdd(ctx, "t1", 1, 1).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t1", 2, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t1", 11, 7).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t1", 13, 1).Result() + Expect(errAdd).NotTo(HaveOccurred()) + resultRange, err := client.TSRange(ctx, "t1", 0, 20).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 1, Value: 1})) + + opt = &redis.TSRangeOptions{Latest: true} + resultRange, err = client.TSRangeWithArgs(ctx, "t2", 0, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 0, Value: 4})) + // Test Bucket Timestamp + resultCreate, err = client.TSCreate(ctx, "t3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + _, errAdd = client.TSAdd(ctx, "t3", 15, 1).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t3", 17, 4).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t3", 51, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t3", 73, 5).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t3", 75, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + + opt = &redis.TSRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10} + resultRange, err = client.TSRangeWithArgs(ctx, "t3", 0, 100, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 10, Value: 4})) + Expect(len(resultRange)).To(BeEquivalentTo(3)) + + opt = &redis.TSRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10, BucketTimestamp: "+"} + resultRange, err = client.TSRangeWithArgs(ctx, "t3", 0, 100, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 20, Value: 4})) + Expect(len(resultRange)).To(BeEquivalentTo(3)) + // Test Empty + _, errAdd = client.TSAdd(ctx, "t4", 15, 1).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t4", 17, 4).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t4", 51, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t4", 73, 5).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t4", 75, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + + opt = &redis.TSRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10} + resultRange, err = client.TSRangeWithArgs(ctx, "t4", 0, 100, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 10, Value: 4})) + Expect(len(resultRange)).To(BeEquivalentTo(3)) + + opt = &redis.TSRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10, Empty: true} + resultRange, err = client.TSRangeWithArgs(ctx, "t4", 0, 100, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 10, Value: 4})) + Expect(len(resultRange)).To(BeEquivalentTo(7)) + }) + + It("should TSRevRange, TSRevRangeWithArgs", Label("timeseries", "tsrevrange", "tsrevrangeWithArgs", "NonRedisEnterprise"), func() { + for i := 0; i < 100; i++ { + _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() + Expect(err).NotTo(HaveOccurred()) + + } + result, err := client.TSRange(ctx, "a", 0, 200).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(100)) + for i := 0; i < 100; i++ { + client.TSAdd(ctx, "a", i+200, float64(i%7)) + } + result, err = client.TSRange(ctx, "a", 0, 500).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(200)) + + opt := &redis.TSRevRangeOptions{Aggregator: redis.Avg, BucketDuration: 10} + result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 500, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(20)) + + opt = &redis.TSRevRangeOptions{Count: 10} + result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 500, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(10)) + + fts := make([]int, 0) + for i := 10; i < 20; i++ { + fts = append(fts, i) + } + opt = &redis.TSRevRangeOptions{FilterByTS: fts, FilterByValue: []int{1, 2}} + result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 500, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + + opt = &redis.TSRevRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "+"} + result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 10, Value: 1}, {Timestamp: 0, Value: 10}})) + + opt = &redis.TSRevRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "1"} + result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 1, Value: 10}, {Timestamp: 0, Value: 1}})) + + opt = &redis.TSRevRangeOptions{Aggregator: redis.Twa, BucketDuration: 10} + result, err = client.TSRevRangeWithArgs(ctx, "a", 0, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo([]redis.TSTimestampValue{{Timestamp: 10, Value: 3}, {Timestamp: 0, Value: 2.55}})) + // Test Range Latest + resultCreate, err := client.TSCreate(ctx, "t1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + resultCreate, err = client.TSCreate(ctx, "t2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + resultRule, err := client.TSCreateRule(ctx, "t1", "t2", redis.Sum, 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRule).To(BeEquivalentTo("OK")) + _, errAdd := client.TSAdd(ctx, "t1", 1, 1).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t1", 2, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t1", 11, 7).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t1", 13, 1).Result() + Expect(errAdd).NotTo(HaveOccurred()) + resultRange, err := client.TSRange(ctx, "t2", 0, 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 0, Value: 4})) + opt = &redis.TSRevRangeOptions{Latest: true} + resultRange, err = client.TSRevRangeWithArgs(ctx, "t2", 0, 10, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 10, Value: 8})) + resultRange, err = client.TSRevRangeWithArgs(ctx, "t2", 0, 9, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 0, Value: 4})) + // Test Bucket Timestamp + resultCreate, err = client.TSCreate(ctx, "t3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + _, errAdd = client.TSAdd(ctx, "t3", 15, 1).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t3", 17, 4).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t3", 51, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t3", 73, 5).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t3", 75, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + + opt = &redis.TSRevRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10} + resultRange, err = client.TSRevRangeWithArgs(ctx, "t3", 0, 100, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 70, Value: 5})) + Expect(len(resultRange)).To(BeEquivalentTo(3)) + + opt = &redis.TSRevRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10, BucketTimestamp: "+"} + resultRange, err = client.TSRevRangeWithArgs(ctx, "t3", 0, 100, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 80, Value: 5})) + Expect(len(resultRange)).To(BeEquivalentTo(3)) + // Test Empty + _, errAdd = client.TSAdd(ctx, "t4", 15, 1).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t4", 17, 4).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t4", 51, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t4", 73, 5).Result() + Expect(errAdd).NotTo(HaveOccurred()) + _, errAdd = client.TSAdd(ctx, "t4", 75, 3).Result() + Expect(errAdd).NotTo(HaveOccurred()) + + opt = &redis.TSRevRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10} + resultRange, err = client.TSRevRangeWithArgs(ctx, "t4", 0, 100, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 70, Value: 5})) + Expect(len(resultRange)).To(BeEquivalentTo(3)) + + opt = &redis.TSRevRangeOptions{Aggregator: redis.Max, Align: 0, BucketDuration: 10, Empty: true} + resultRange, err = client.TSRevRangeWithArgs(ctx, "t4", 0, 100, opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultRange[0]).To(BeEquivalentTo(redis.TSTimestampValue{Timestamp: 70, Value: 5})) + Expect(len(resultRange)).To(BeEquivalentTo(7)) + }) + + It("should TSMRange and TSMRangeWithArgs", Label("timeseries", "tsmrange", "tsmrangeWithArgs"), func() { + createOpt := &redis.TSOptions{Labels: map[string]string{"Test": "This", "team": "ny"}} + resultCreate, err := client.TSCreateWithArgs(ctx, "a", createOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + createOpt = &redis.TSOptions{Labels: map[string]string{"Test": "This", "Taste": "That", "team": "sf"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "b", createOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + + for i := 0; i < 100; i++ { + _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "b", i, float64(i%11)).Result() + Expect(err).NotTo(HaveOccurred()) + } + + result, err := client.TSMRange(ctx, 0, 200, []string{"Test=This"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + if client.Options().Protocol == 2 { + Expect(len(result["a"][1].([]interface{}))).To(BeEquivalentTo(100)) + } else { + Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(100)) + } + // Test Count + mrangeOpt := &redis.TSMRangeOptions{Count: 10} + result, err = client.TSMRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(len(result["a"][1].([]interface{}))).To(BeEquivalentTo(10)) + } else { + Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(10)) + } + // Test Aggregation and BucketDuration + for i := 0; i < 100; i++ { + _, err := client.TSAdd(ctx, "a", i+200, float64(i%7)).Result() + Expect(err).NotTo(HaveOccurred()) + } + mrangeOpt = &redis.TSMRangeOptions{Aggregator: redis.Avg, BucketDuration: 10} + result, err = client.TSMRangeWithArgs(ctx, 0, 500, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + if client.Options().Protocol == 2 { + Expect(len(result["a"][1].([]interface{}))).To(BeEquivalentTo(20)) + } else { + Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(20)) + } + // Test WithLabels + if client.Options().Protocol == 2 { + Expect(result["a"][0]).To(BeEquivalentTo([]interface{}{})) + } else { + Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{})) + } + mrangeOpt = &redis.TSMRangeOptions{WithLabels: true} + result, err = client.TSMRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][0]).To(ConsistOf([]interface{}{[]interface{}{"Test", "This"}, []interface{}{"team", "ny"}})) + } else { + Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"Test": "This", "team": "ny"})) + } + // Test SelectedLabels + mrangeOpt = &redis.TSMRangeOptions{SelectedLabels: []interface{}{"team"}} + result, err = client.TSMRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][0].([]interface{})[0]).To(BeEquivalentTo([]interface{}{"team", "ny"})) + Expect(result["b"][0].([]interface{})[0]).To(BeEquivalentTo([]interface{}{"team", "sf"})) + } else { + Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"team": "ny"})) + Expect(result["b"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"team": "sf"})) + } + // Test FilterBy + fts := make([]int, 0) + for i := 10; i < 20; i++ { + fts = append(fts, i) + } + mrangeOpt = &redis.TSMRangeOptions{FilterByTS: fts, FilterByValue: []int{1, 2}} + result, err = client.TSMRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][1].([]interface{})).To(BeEquivalentTo([]interface{}{[]interface{}{int64(15), "1"}, []interface{}{int64(16), "2"}})) + } else { + Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(15), 1.0}, []interface{}{int64(16), 2.0}})) + } + // Test GroupBy + mrangeOpt = &redis.TSMRangeOptions{GroupByLabel: "Test", Reducer: "sum"} + result, err = client.TSMRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["Test=This"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), "0"}, []interface{}{int64(1), "2"}, []interface{}{int64(2), "4"}, []interface{}{int64(3), "6"}})) + } else { + Expect(result["Test=This"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 0.0}, []interface{}{int64(1), 2.0}, []interface{}{int64(2), 4.0}, []interface{}{int64(3), 6.0}})) + } + mrangeOpt = &redis.TSMRangeOptions{GroupByLabel: "Test", Reducer: "max"} + result, err = client.TSMRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["Test=This"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), "0"}, []interface{}{int64(1), "1"}, []interface{}{int64(2), "2"}, []interface{}{int64(3), "3"}})) + } else { + Expect(result["Test=This"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 0.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(3), 3.0}})) + } + + mrangeOpt = &redis.TSMRangeOptions{GroupByLabel: "team", Reducer: "min"} + result, err = client.TSMRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + if client.Options().Protocol == 2 { + Expect(result["team=ny"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), "0"}, []interface{}{int64(1), "1"}, []interface{}{int64(2), "2"}, []interface{}{int64(3), "3"}})) + Expect(result["team=sf"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), "0"}, []interface{}{int64(1), "1"}, []interface{}{int64(2), "2"}, []interface{}{int64(3), "3"}})) + } else { + Expect(result["team=ny"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 0.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(3), 3.0}})) + Expect(result["team=sf"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 0.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(3), 3.0}})) + } + // Test Align + mrangeOpt = &redis.TSMRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "-"} + result, err = client.TSMRangeWithArgs(ctx, 0, 10, []string{"team=ny"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), "10"}, []interface{}{int64(10), "1"}})) + } else { + Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 10.0}, []interface{}{int64(10), 1.0}})) + } + + mrangeOpt = &redis.TSMRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: 5} + result, err = client.TSMRangeWithArgs(ctx, 0, 10, []string{"team=ny"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), "5"}, []interface{}{int64(5), "6"}})) + } else { + Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 5.0}, []interface{}{int64(5), 6.0}})) + } + }) + + It("should TSMRangeWithArgs Latest", Label("timeseries", "tsmrangeWithArgs", "tsmrangelatest", "NonRedisEnterprise"), func() { + resultCreate, err := client.TSCreate(ctx, "a").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + opt := &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "b", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + + resultCreate, err = client.TSCreate(ctx, "c").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + opt = &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "d", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + + resultCreateRule, err := client.TSCreateRule(ctx, "a", "b", redis.Sum, 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreateRule).To(BeEquivalentTo("OK")) + resultCreateRule, err = client.TSCreateRule(ctx, "c", "d", redis.Sum, 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreateRule).To(BeEquivalentTo("OK")) + + _, err = client.TSAdd(ctx, "a", 1, 1).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "a", 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "a", 11, 7).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "a", 13, 1).Result() + Expect(err).NotTo(HaveOccurred()) + + _, err = client.TSAdd(ctx, "c", 1, 1).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 11, 7).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 13, 1).Result() + Expect(err).NotTo(HaveOccurred()) + mrangeOpt := &redis.TSMRangeOptions{Latest: true} + result, err := client.TSMRangeWithArgs(ctx, 0, 10, []string{"is_compaction=true"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["b"][1]).To(ConsistOf([]interface{}{int64(0), "4"}, []interface{}{int64(10), "8"})) + Expect(result["d"][1]).To(ConsistOf([]interface{}{int64(0), "4"}, []interface{}{int64(10), "8"})) + } else { + Expect(result["b"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 4.0}, []interface{}{int64(10), 8.0}})) + Expect(result["d"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(0), 4.0}, []interface{}{int64(10), 8.0}})) + } + }) + It("should TSMRevRange and TSMRevRangeWithArgs", Label("timeseries", "tsmrevrange", "tsmrevrangeWithArgs"), func() { + createOpt := &redis.TSOptions{Labels: map[string]string{"Test": "This", "team": "ny"}} + resultCreate, err := client.TSCreateWithArgs(ctx, "a", createOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + createOpt = &redis.TSOptions{Labels: map[string]string{"Test": "This", "Taste": "That", "team": "sf"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "b", createOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + + for i := 0; i < 100; i++ { + _, err := client.TSAdd(ctx, "a", i, float64(i%7)).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "b", i, float64(i%11)).Result() + Expect(err).NotTo(HaveOccurred()) + } + result, err := client.TSMRevRange(ctx, 0, 200, []string{"Test=This"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + if client.Options().Protocol == 2 { + Expect(len(result["a"][1].([]interface{}))).To(BeEquivalentTo(100)) + } else { + Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(100)) + } + // Test Count + mrangeOpt := &redis.TSMRevRangeOptions{Count: 10} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(len(result["a"][1].([]interface{}))).To(BeEquivalentTo(10)) + } else { + Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(10)) + } + // Test Aggregation and BucketDuration + for i := 0; i < 100; i++ { + _, err := client.TSAdd(ctx, "a", i+200, float64(i%7)).Result() + Expect(err).NotTo(HaveOccurred()) + } + mrangeOpt = &redis.TSMRevRangeOptions{Aggregator: redis.Avg, BucketDuration: 10} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 500, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + if client.Options().Protocol == 2 { + Expect(len(result["a"][1].([]interface{}))).To(BeEquivalentTo(20)) + Expect(result["a"][0]).To(BeEquivalentTo([]interface{}{})) + } else { + Expect(len(result["a"][2].([]interface{}))).To(BeEquivalentTo(20)) + Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{})) + } + mrangeOpt = &redis.TSMRevRangeOptions{WithLabels: true} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][0]).To(ConsistOf([]interface{}{[]interface{}{"Test", "This"}, []interface{}{"team", "ny"}})) + } else { + Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"Test": "This", "team": "ny"})) + } + // Test SelectedLabels + mrangeOpt = &redis.TSMRevRangeOptions{SelectedLabels: []interface{}{"team"}} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][0].([]interface{})[0]).To(BeEquivalentTo([]interface{}{"team", "ny"})) + Expect(result["b"][0].([]interface{})[0]).To(BeEquivalentTo([]interface{}{"team", "sf"})) + } else { + Expect(result["a"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"team": "ny"})) + Expect(result["b"][0]).To(BeEquivalentTo(map[interface{}]interface{}{"team": "sf"})) + } + // Test FilterBy + fts := make([]int, 0) + for i := 10; i < 20; i++ { + fts = append(fts, i) + } + mrangeOpt = &redis.TSMRevRangeOptions{FilterByTS: fts, FilterByValue: []int{1, 2}} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 200, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][1].([]interface{})).To(ConsistOf([]interface{}{int64(16), "2"}, []interface{}{int64(15), "1"})) + } else { + Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(16), 2.0}, []interface{}{int64(15), 1.0}})) + } + // Test GroupBy + mrangeOpt = &redis.TSMRevRangeOptions{GroupByLabel: "Test", Reducer: "sum"} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["Test=This"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), "6"}, []interface{}{int64(2), "4"}, []interface{}{int64(1), "2"}, []interface{}{int64(0), "0"}})) + } else { + Expect(result["Test=This"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), 6.0}, []interface{}{int64(2), 4.0}, []interface{}{int64(1), 2.0}, []interface{}{int64(0), 0.0}})) + } + mrangeOpt = &redis.TSMRevRangeOptions{GroupByLabel: "Test", Reducer: "max"} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["Test=This"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), "3"}, []interface{}{int64(2), "2"}, []interface{}{int64(1), "1"}, []interface{}{int64(0), "0"}})) + } else { + Expect(result["Test=This"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), 3.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(0), 0.0}})) + } + mrangeOpt = &redis.TSMRevRangeOptions{GroupByLabel: "team", Reducer: "min"} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 3, []string{"Test=This"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(2)) + if client.Options().Protocol == 2 { + Expect(result["team=ny"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), "3"}, []interface{}{int64(2), "2"}, []interface{}{int64(1), "1"}, []interface{}{int64(0), "0"}})) + Expect(result["team=sf"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), "3"}, []interface{}{int64(2), "2"}, []interface{}{int64(1), "1"}, []interface{}{int64(0), "0"}})) + } else { + Expect(result["team=ny"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), 3.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(0), 0.0}})) + Expect(result["team=sf"][3]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(3), 3.0}, []interface{}{int64(2), 2.0}, []interface{}{int64(1), 1.0}, []interface{}{int64(0), 0.0}})) + } + // Test Align + mrangeOpt = &redis.TSMRevRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: "-"} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 10, []string{"team=ny"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), "1"}, []interface{}{int64(0), "10"}})) + } else { + Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), 1.0}, []interface{}{int64(0), 10.0}})) + } + mrangeOpt = &redis.TSMRevRangeOptions{Aggregator: redis.Count, BucketDuration: 10, Align: 1} + result, err = client.TSMRevRangeWithArgs(ctx, 0, 10, []string{"team=ny"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["a"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(1), "10"}, []interface{}{int64(0), "1"}})) + } else { + Expect(result["a"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(1), 10.0}, []interface{}{int64(0), 1.0}})) + } + }) + + It("should TSMRevRangeWithArgs Latest", Label("timeseries", "tsmrevrangeWithArgs", "tsmrevrangelatest", "NonRedisEnterprise"), func() { + resultCreate, err := client.TSCreate(ctx, "a").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + opt := &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "b", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + + resultCreate, err = client.TSCreate(ctx, "c").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + opt = &redis.TSOptions{Labels: map[string]string{"is_compaction": "true"}} + resultCreate, err = client.TSCreateWithArgs(ctx, "d", opt).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreate).To(BeEquivalentTo("OK")) + + resultCreateRule, err := client.TSCreateRule(ctx, "a", "b", redis.Sum, 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreateRule).To(BeEquivalentTo("OK")) + resultCreateRule, err = client.TSCreateRule(ctx, "c", "d", redis.Sum, 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultCreateRule).To(BeEquivalentTo("OK")) + + _, err = client.TSAdd(ctx, "a", 1, 1).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "a", 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "a", 11, 7).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "a", 13, 1).Result() + Expect(err).NotTo(HaveOccurred()) + + _, err = client.TSAdd(ctx, "c", 1, 1).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 11, 7).Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.TSAdd(ctx, "c", 13, 1).Result() + Expect(err).NotTo(HaveOccurred()) + mrangeOpt := &redis.TSMRevRangeOptions{Latest: true} + result, err := client.TSMRevRangeWithArgs(ctx, 0, 10, []string{"is_compaction=true"}, mrangeOpt).Result() + Expect(err).NotTo(HaveOccurred()) + if client.Options().Protocol == 2 { + Expect(result["b"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), "8"}, []interface{}{int64(0), "4"}})) + Expect(result["d"][1]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), "8"}, []interface{}{int64(0), "4"}})) + } else { + Expect(result["b"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), 8.0}, []interface{}{int64(0), 4.0}})) + Expect(result["d"][2]).To(BeEquivalentTo([]interface{}{[]interface{}{int64(10), 8.0}, []interface{}{int64(0), 4.0}})) + } + }) + }) + } }) From 8b1073d2d63a365909e584d57a4997a88f5ed185 Mon Sep 17 00:00:00 2001 From: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:15:19 +0200 Subject: [PATCH 04/11] Support Probabilistic commands with RESP 2 protocol (#3176) * Support bloom resp 2 * Support Resp2 for BF.Info * simplify BFInfoCmd field assignment using map-based key-to-field references --- probabilistic.go | 72 ++- probabilistic_test.go | 1437 +++++++++++++++++++++-------------------- 2 files changed, 780 insertions(+), 729 deletions(-) diff --git a/probabilistic.go b/probabilistic.go index 5d5cd1a62..02ca263cb 100644 --- a/probabilistic.go +++ b/probabilistic.go @@ -319,37 +319,69 @@ func (cmd *BFInfoCmd) Result() (BFInfo, error) { } func (cmd *BFInfoCmd) readReply(rd *proto.Reader) (err error) { - n, err := rd.ReadMapLen() + result := BFInfo{} + + // Create a mapping from key names to pointers of struct fields + respMapping := map[string]*int64{ + "Capacity": &result.Capacity, + "CAPACITY": &result.Capacity, + "Size": &result.Size, + "SIZE": &result.Size, + "Number of filters": &result.Filters, + "FILTERS": &result.Filters, + "Number of items inserted": &result.ItemsInserted, + "ITEMS": &result.ItemsInserted, + "Expansion rate": &result.ExpansionRate, + "EXPANSION": &result.ExpansionRate, + } + + // Helper function to read and assign a value based on the key + readAndAssignValue := func(key string) error { + fieldPtr, exists := respMapping[key] + if !exists { + return fmt.Errorf("redis: BLOOM.INFO unexpected key %s", key) + } + + // Read the integer and assign to the field via pointer dereferencing + val, err := rd.ReadInt() + if err != nil { + return err + } + *fieldPtr = val + return nil + } + + readType, err := rd.PeekReplyType() if err != nil { return err } - var key string - var result BFInfo - for f := 0; f < n; f++ { - key, err = rd.ReadString() + if len(cmd.args) > 2 && readType == proto.RespArray { + n, err := rd.ReadArrayLen() if err != nil { return err } - - switch key { - case "Capacity": - result.Capacity, err = rd.ReadInt() - case "Size": - result.Size, err = rd.ReadInt() - case "Number of filters": - result.Filters, err = rd.ReadInt() - case "Number of items inserted": - result.ItemsInserted, err = rd.ReadInt() - case "Expansion rate": - result.ExpansionRate, err = rd.ReadInt() - default: - return fmt.Errorf("redis: BLOOM.INFO unexpected key %s", key) + if key, ok := cmd.args[2].(string); ok && n == 1 { + if err := readAndAssignValue(key); err != nil { + return err + } + } else { + return fmt.Errorf("redis: BLOOM.INFO invalid argument key type") } - + } else { + n, err := rd.ReadMapLen() if err != nil { return err } + for i := 0; i < n; i++ { + key, err := rd.ReadString() + if err != nil { + return err + } + if err := readAndAssignValue(key); err != nil { + return err + } + } } cmd.val = result diff --git a/probabilistic_test.go b/probabilistic_test.go index 0610c515e..a0a050e23 100644 --- a/probabilistic_test.go +++ b/probabilistic_test.go @@ -13,721 +13,740 @@ import ( var _ = Describe("Probabilistic commands", Label("probabilistic"), func() { ctx := context.TODO() - var client *redis.Client - BeforeEach(func() { - client = redis.NewClient(&redis.Options{Addr: ":6379"}) - Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - Expect(client.Close()).NotTo(HaveOccurred()) - }) - - Describe("bloom", Label("bloom"), func() { - It("should BFAdd", Label("bloom", "bfadd"), func() { - resultAdd, err := client.BFAdd(ctx, "testbf1", 1).Result() - - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd).To(BeTrue()) - - resultInfo, err := client.BFInfo(ctx, "testbf1").Result() - - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo).To(BeAssignableToTypeOf(redis.BFInfo{})) - Expect(resultInfo.ItemsInserted).To(BeEquivalentTo(int64(1))) - }) - - It("should BFCard", Label("bloom", "bfcard"), func() { - // This is a probabilistic data structure, and it's not always guaranteed that we will get back - // the exact number of inserted items, during hash collisions - // But with such a low number of items (only 3), - // the probability of a collision is very low, so we can expect to get back the exact number of items - _, err := client.BFAdd(ctx, "testbf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.BFAdd(ctx, "testbf1", "item2").Result() - Expect(err).NotTo(HaveOccurred()) - _, err = client.BFAdd(ctx, "testbf1", 3).Result() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.BFCard(ctx, "testbf1").Result() - - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeEquivalentTo(int64(3))) - }) - - It("should BFExists", Label("bloom", "bfexists"), func() { - exists, err := client.BFExists(ctx, "testbf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(exists).To(BeFalse()) - - _, err = client.BFAdd(ctx, "testbf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - - exists, err = client.BFExists(ctx, "testbf1", "item1").Result() - - Expect(err).NotTo(HaveOccurred()) - Expect(exists).To(BeTrue()) - }) - - It("should BFInfo and BFReserve", Label("bloom", "bfinfo", "bfreserve"), func() { - err := client.BFReserve(ctx, "testbf1", 0.001, 2000).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.BFInfo(ctx, "testbf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeAssignableToTypeOf(redis.BFInfo{})) - Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) - }) - - It("should BFInfoCapacity, BFInfoSize, BFInfoFilters, BFInfoItems, BFInfoExpansion, ", Label("bloom", "bfinfocapacity", "bfinfosize", "bfinfofilters", "bfinfoitems", "bfinfoexpansion"), func() { - err := client.BFReserve(ctx, "testbf1", 0.001, 2000).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.BFInfoCapacity(ctx, "testbf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) - - result, err = client.BFInfoItems(ctx, "testbf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.ItemsInserted).To(BeEquivalentTo(int64(0))) - - result, err = client.BFInfoSize(ctx, "testbf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Size).To(BeEquivalentTo(int64(4056))) - - err = client.BFReserveExpansion(ctx, "testbf2", 0.001, 2000, 3).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err = client.BFInfoFilters(ctx, "testbf2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.Filters).To(BeEquivalentTo(int64(1))) - - result, err = client.BFInfoExpansion(ctx, "testbf2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result.ExpansionRate).To(BeEquivalentTo(int64(3))) - }) - - It("should BFInsert", Label("bloom", "bfinsert"), func() { - options := &redis.BFInsertOptions{ - Capacity: 2000, - Error: 0.001, - Expansion: 3, - NonScaling: false, - NoCreate: true, - } - - resultInsert, err := client.BFInsert(ctx, "testbf1", options, "item1").Result() - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError("ERR not found")) - - options = &redis.BFInsertOptions{ - Capacity: 2000, - Error: 0.001, - Expansion: 3, - NonScaling: false, - NoCreate: false, - } - - resultInsert, err = client.BFInsert(ctx, "testbf1", options, "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultInsert)).To(BeEquivalentTo(1)) - - exists, err := client.BFExists(ctx, "testbf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(exists).To(BeTrue()) - - result, err := client.BFInfo(ctx, "testbf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeAssignableToTypeOf(redis.BFInfo{})) - Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) - Expect(result.ExpansionRate).To(BeEquivalentTo(int64(3))) - }) - - It("should BFMAdd", Label("bloom", "bfmadd"), func() { - resultAdd, err := client.BFMAdd(ctx, "testbf1", "item1", "item2", "item3").Result() - - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultAdd)).To(Equal(3)) - - resultInfo, err := client.BFInfo(ctx, "testbf1").Result() - - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo).To(BeAssignableToTypeOf(redis.BFInfo{})) - Expect(resultInfo.ItemsInserted).To(BeEquivalentTo(int64(3))) - resultAdd2, err := client.BFMAdd(ctx, "testbf1", "item1", "item2", "item4").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultAdd2[0]).To(BeFalse()) - Expect(resultAdd2[1]).To(BeFalse()) - Expect(resultAdd2[2]).To(BeTrue()) - }) - - It("should BFMExists", Label("bloom", "bfmexists"), func() { - exist, err := client.BFMExists(ctx, "testbf1", "item1", "item2", "item3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(exist)).To(Equal(3)) - Expect(exist[0]).To(BeFalse()) - Expect(exist[1]).To(BeFalse()) - Expect(exist[2]).To(BeFalse()) - - _, err = client.BFMAdd(ctx, "testbf1", "item1", "item2", "item3").Result() - Expect(err).NotTo(HaveOccurred()) - - exist, err = client.BFMExists(ctx, "testbf1", "item1", "item2", "item3", "item4").Result() - - Expect(err).NotTo(HaveOccurred()) - Expect(len(exist)).To(Equal(4)) - Expect(exist[0]).To(BeTrue()) - Expect(exist[1]).To(BeTrue()) - Expect(exist[2]).To(BeTrue()) - Expect(exist[3]).To(BeFalse()) - }) - - It("should BFReserveExpansion", Label("bloom", "bfreserveexpansion"), func() { - err := client.BFReserveExpansion(ctx, "testbf1", 0.001, 2000, 3).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.BFInfo(ctx, "testbf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeAssignableToTypeOf(redis.BFInfo{})) - Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) - Expect(result.ExpansionRate).To(BeEquivalentTo(int64(3))) - }) - - It("should BFReserveNonScaling", Label("bloom", "bfreservenonscaling"), func() { - err := client.BFReserveNonScaling(ctx, "testbfns1", 0.001, 1000).Err() - Expect(err).NotTo(HaveOccurred()) - - _, err = client.BFInfo(ctx, "testbfns1").Result() - Expect(err).To(HaveOccurred()) - }) - - It("should BFScanDump and BFLoadChunk", Label("bloom", "bfscandump", "bfloadchunk"), func() { - err := client.BFReserve(ctx, "testbfsd1", 0.001, 3000).Err() - Expect(err).NotTo(HaveOccurred()) - for i := 0; i < 1000; i++ { - client.BFAdd(ctx, "testbfsd1", i) - } - infBefore := client.BFInfoSize(ctx, "testbfsd1") - fd := []redis.ScanDump{} - sd, err := client.BFScanDump(ctx, "testbfsd1", 0).Result() - for { - if sd.Iter == 0 { - break - } - Expect(err).NotTo(HaveOccurred()) - fd = append(fd, sd) - sd, err = client.BFScanDump(ctx, "testbfsd1", sd.Iter).Result() - } - client.Del(ctx, "testbfsd1") - for _, e := range fd { - client.BFLoadChunk(ctx, "testbfsd1", e.Iter, e.Data) - } - infAfter := client.BFInfoSize(ctx, "testbfsd1") - Expect(infBefore).To(BeEquivalentTo(infAfter)) - }) - - It("should BFReserveWithArgs", Label("bloom", "bfreserveargs"), func() { - options := &redis.BFReserveOptions{ - Capacity: 2000, - Error: 0.001, - Expansion: 3, - NonScaling: false, - } - err := client.BFReserveWithArgs(ctx, "testbf", options).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.BFInfo(ctx, "testbf").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeAssignableToTypeOf(redis.BFInfo{})) - Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) - Expect(result.ExpansionRate).To(BeEquivalentTo(int64(3))) - }) - }) - - Describe("cuckoo", Label("cuckoo"), func() { - It("should CFAdd", Label("cuckoo", "cfadd"), func() { - add, err := client.CFAdd(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(add).To(BeTrue()) - - exists, err := client.CFExists(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(exists).To(BeTrue()) - - info, err := client.CFInfo(ctx, "testcf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(info).To(BeAssignableToTypeOf(redis.CFInfo{})) - Expect(info.NumItemsInserted).To(BeEquivalentTo(int64(1))) + setupRedisClient := func(protocolVersion int) *redis.Client { + return redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + DB: 0, + Protocol: protocolVersion, }) + } - It("should CFAddNX", Label("cuckoo", "cfaddnx"), func() { - add, err := client.CFAddNX(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(add).To(BeTrue()) + protocols := []int{2, 3} + for _, protocol := range protocols { + protocol := protocol // capture loop variable for each context - exists, err := client.CFExists(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(exists).To(BeTrue()) + Context(fmt.Sprintf("with protocol version %d", protocol), func() { + var client *redis.Client - result, err := client.CFAddNX(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeFalse()) + BeforeEach(func() { + client = setupRedisClient(protocol) + Expect(client.FlushAll(ctx).Err()).NotTo(HaveOccurred()) + }) - info, err := client.CFInfo(ctx, "testcf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(info).To(BeAssignableToTypeOf(redis.CFInfo{})) - Expect(info.NumItemsInserted).To(BeEquivalentTo(int64(1))) - }) - - It("should CFCount", Label("cuckoo", "cfcount"), func() { - err := client.CFAdd(ctx, "testcf1", "item1").Err() - cnt, err := client.CFCount(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(cnt).To(BeEquivalentTo(int64(1))) - - err = client.CFAdd(ctx, "testcf1", "item1").Err() - Expect(err).NotTo(HaveOccurred()) - - cnt, err = client.CFCount(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(cnt).To(BeEquivalentTo(int64(2))) - }) - - It("should CFDel and CFExists", Label("cuckoo", "cfdel", "cfexists"), func() { - err := client.CFAdd(ctx, "testcf1", "item1").Err() - Expect(err).NotTo(HaveOccurred()) - - exists, err := client.CFExists(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(exists).To(BeTrue()) - - del, err := client.CFDel(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(del).To(BeTrue()) - - exists, err = client.CFExists(ctx, "testcf1", "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(exists).To(BeFalse()) - }) - - It("should CFInfo and CFReserve", Label("cuckoo", "cfinfo", "cfreserve"), func() { - err := client.CFReserve(ctx, "testcf1", 1000).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.CFReserveExpansion(ctx, "testcfe1", 1000, 1).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.CFReserveBucketSize(ctx, "testcfbs1", 1000, 4).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.CFReserveMaxIterations(ctx, "testcfmi1", 1000, 10).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.CFInfo(ctx, "testcf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeAssignableToTypeOf(redis.CFInfo{})) - }) - - It("should CFScanDump and CFLoadChunk", Label("bloom", "cfscandump", "cfloadchunk"), func() { - err := client.CFReserve(ctx, "testcfsd1", 1000).Err() - Expect(err).NotTo(HaveOccurred()) - for i := 0; i < 1000; i++ { - Item := fmt.Sprintf("item%d", i) - client.CFAdd(ctx, "testcfsd1", Item) - } - infBefore := client.CFInfo(ctx, "testcfsd1") - fd := []redis.ScanDump{} - sd, err := client.CFScanDump(ctx, "testcfsd1", 0).Result() - for { - if sd.Iter == 0 { - break + AfterEach(func() { + if client != nil { + client.FlushDB(ctx) + client.Close() } - Expect(err).NotTo(HaveOccurred()) - fd = append(fd, sd) - sd, err = client.CFScanDump(ctx, "testcfsd1", sd.Iter).Result() - } - client.Del(ctx, "testcfsd1") - for _, e := range fd { - client.CFLoadChunk(ctx, "testcfsd1", e.Iter, e.Data) - } - infAfter := client.CFInfo(ctx, "testcfsd1") - Expect(infBefore).To(BeEquivalentTo(infAfter)) - }) - - It("should CFInfo and CFReserveWithArgs", Label("cuckoo", "cfinfo", "cfreserveargs"), func() { - args := &redis.CFReserveOptions{ - Capacity: 2048, - BucketSize: 3, - MaxIterations: 15, - Expansion: 2, - } - - err := client.CFReserveWithArgs(ctx, "testcf1", args).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.CFInfo(ctx, "testcf1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(BeAssignableToTypeOf(redis.CFInfo{})) - Expect(result.BucketSize).To(BeEquivalentTo(int64(3))) - Expect(result.MaxIteration).To(BeEquivalentTo(int64(15))) - Expect(result.ExpansionRate).To(BeEquivalentTo(int64(2))) - }) - - It("should CFInsert", Label("cuckoo", "cfinsert"), func() { - args := &redis.CFInsertOptions{ - Capacity: 3000, - NoCreate: true, - } - - result, err := client.CFInsert(ctx, "testcf1", args, "item1", "item2", "item3").Result() - Expect(err).To(HaveOccurred()) - - args = &redis.CFInsertOptions{ - Capacity: 3000, - NoCreate: false, - } - - result, err = client.CFInsert(ctx, "testcf1", args, "item1", "item2", "item3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(3)) - }) - - It("should CFInsertNX", Label("cuckoo", "cfinsertnx"), func() { - args := &redis.CFInsertOptions{ - Capacity: 3000, - NoCreate: true, - } - - result, err := client.CFInsertNX(ctx, "testcf1", args, "item1", "item2", "item2").Result() - Expect(err).To(HaveOccurred()) - - args = &redis.CFInsertOptions{ - Capacity: 3000, - NoCreate: false, - } - - result, err = client.CFInsertNX(ctx, "testcf2", args, "item1", "item2", "item2").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(3)) - Expect(result[0]).To(BeEquivalentTo(int64(1))) - Expect(result[1]).To(BeEquivalentTo(int64(1))) - Expect(result[2]).To(BeEquivalentTo(int64(0))) - }) - - It("should CFMexists", Label("cuckoo", "cfmexists"), func() { - err := client.CFInsert(ctx, "testcf1", nil, "item1", "item2", "item3").Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.CFMExists(ctx, "testcf1", "item1", "item2", "item3", "item4").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(4)) - Expect(result[0]).To(BeTrue()) - Expect(result[1]).To(BeTrue()) - Expect(result[2]).To(BeTrue()) - Expect(result[3]).To(BeFalse()) - }) - }) - - Describe("CMS", Label("cms"), func() { - It("should CMSIncrBy", Label("cms", "cmsincrby"), func() { - err := client.CMSInitByDim(ctx, "testcms1", 5, 10).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.CMSIncrBy(ctx, "testcms1", "item1", 1, "item2", 2, "item3", 3).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(3)) - Expect(result[0]).To(BeEquivalentTo(int64(1))) - Expect(result[1]).To(BeEquivalentTo(int64(2))) - Expect(result[2]).To(BeEquivalentTo(int64(3))) - }) - - It("should CMSInitByDim and CMSInfo", Label("cms", "cmsinitbydim", "cmsinfo"), func() { - err := client.CMSInitByDim(ctx, "testcms1", 5, 10).Err() - Expect(err).NotTo(HaveOccurred()) - - info, err := client.CMSInfo(ctx, "testcms1").Result() - Expect(err).NotTo(HaveOccurred()) - - Expect(info).To(BeAssignableToTypeOf(redis.CMSInfo{})) - Expect(info.Width).To(BeEquivalentTo(int64(5))) - Expect(info.Depth).To(BeEquivalentTo(int64(10))) - }) - - It("should CMSInitByProb", Label("cms", "cmsinitbyprob"), func() { - err := client.CMSInitByProb(ctx, "testcms1", 0.002, 0.01).Err() - Expect(err).NotTo(HaveOccurred()) - - info, err := client.CMSInfo(ctx, "testcms1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(info).To(BeAssignableToTypeOf(redis.CMSInfo{})) - }) - - It("should CMSMerge, CMSMergeWithWeight and CMSQuery", Label("cms", "cmsmerge", "cmsquery", "NonRedisEnterprise"), func() { - err := client.CMSMerge(ctx, "destCms1", "testcms2", "testcms3").Err() - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError("CMS: key does not exist")) - - err = client.CMSInitByDim(ctx, "destCms1", 5, 10).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.CMSInitByDim(ctx, "destCms2", 5, 10).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.CMSInitByDim(ctx, "cms1", 2, 20).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.CMSInitByDim(ctx, "cms2", 3, 20).Err() - Expect(err).NotTo(HaveOccurred()) - - err = client.CMSMerge(ctx, "destCms1", "cms1", "cms2").Err() - Expect(err).To(MatchError("CMS: width/depth is not equal")) - - client.Del(ctx, "cms1", "cms2") - - err = client.CMSInitByDim(ctx, "cms1", 5, 10).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.CMSInitByDim(ctx, "cms2", 5, 10).Err() - Expect(err).NotTo(HaveOccurred()) - - client.CMSIncrBy(ctx, "cms1", "item1", 1, "item2", 2) - client.CMSIncrBy(ctx, "cms2", "item2", 2, "item3", 3) - - err = client.CMSMerge(ctx, "destCms1", "cms1", "cms2").Err() - Expect(err).NotTo(HaveOccurred()) - - result, err := client.CMSQuery(ctx, "destCms1", "item1", "item2", "item3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(3)) - Expect(result[0]).To(BeEquivalentTo(int64(1))) - Expect(result[1]).To(BeEquivalentTo(int64(4))) - Expect(result[2]).To(BeEquivalentTo(int64(3))) - - sourceSketches := map[string]int64{ - "cms1": 1, - "cms2": 2, - } - err = client.CMSMergeWithWeight(ctx, "destCms2", sourceSketches).Err() - Expect(err).NotTo(HaveOccurred()) - - result, err = client.CMSQuery(ctx, "destCms2", "item1", "item2", "item3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(BeEquivalentTo(3)) - Expect(result[0]).To(BeEquivalentTo(int64(1))) - Expect(result[1]).To(BeEquivalentTo(int64(6))) - Expect(result[2]).To(BeEquivalentTo(int64(6))) - }) - }) - - Describe("TopK", Label("topk"), func() { - It("should TopKReserve, TopKInfo, TopKAdd, TopKQuery, TopKCount, TopKIncrBy, TopKList, TopKListWithCount", Label("topk", "topkreserve", "topkinfo", "topkadd", "topkquery", "topkcount", "topkincrby", "topklist", "topklistwithcount"), func() { - err := client.TopKReserve(ctx, "topk1", 3).Err() - Expect(err).NotTo(HaveOccurred()) - - resultInfo, err := client.TopKInfo(ctx, "topk1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo.K).To(BeEquivalentTo(int64(3))) - - resultAdd, err := client.TopKAdd(ctx, "topk1", "item1", "item2", 3, "item1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultAdd)).To(BeEquivalentTo(int64(4))) - - resultQuery, err := client.TopKQuery(ctx, "topk1", "item1", "item2", 4, 3).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultQuery)).To(BeEquivalentTo(4)) - Expect(resultQuery[0]).To(BeTrue()) - Expect(resultQuery[1]).To(BeTrue()) - Expect(resultQuery[2]).To(BeFalse()) - Expect(resultQuery[3]).To(BeTrue()) - - resultCount, err := client.TopKCount(ctx, "topk1", "item1", "item2", "item3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultCount)).To(BeEquivalentTo(3)) - Expect(resultCount[0]).To(BeEquivalentTo(int64(2))) - Expect(resultCount[1]).To(BeEquivalentTo(int64(1))) - Expect(resultCount[2]).To(BeEquivalentTo(int64(0))) - - resultIncr, err := client.TopKIncrBy(ctx, "topk1", "item1", 5, "item2", 10).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultIncr)).To(BeEquivalentTo(2)) - - resultCount, err = client.TopKCount(ctx, "topk1", "item1", "item2", "item3").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultCount)).To(BeEquivalentTo(3)) - Expect(resultCount[0]).To(BeEquivalentTo(int64(7))) - Expect(resultCount[1]).To(BeEquivalentTo(int64(11))) - Expect(resultCount[2]).To(BeEquivalentTo(int64(0))) - - resultList, err := client.TopKList(ctx, "topk1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultList)).To(BeEquivalentTo(3)) - Expect(resultList).To(ContainElements("item2", "item1", "3")) - - resultListWithCount, err := client.TopKListWithCount(ctx, "topk1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(resultListWithCount)).To(BeEquivalentTo(3)) - Expect(resultListWithCount["3"]).To(BeEquivalentTo(int64(1))) - Expect(resultListWithCount["item1"]).To(BeEquivalentTo(int64(7))) - Expect(resultListWithCount["item2"]).To(BeEquivalentTo(int64(11))) - }) - - It("should TopKReserveWithOptions", Label("topk", "topkreservewithoptions"), func() { - err := client.TopKReserveWithOptions(ctx, "topk1", 3, 1500, 8, 0.5).Err() - Expect(err).NotTo(HaveOccurred()) - - resultInfo, err := client.TopKInfo(ctx, "topk1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(resultInfo.K).To(BeEquivalentTo(int64(3))) - Expect(resultInfo.Width).To(BeEquivalentTo(int64(1500))) - Expect(resultInfo.Depth).To(BeEquivalentTo(int64(8))) - Expect(resultInfo.Decay).To(BeEquivalentTo(0.5)) - }) - }) - - Describe("t-digest", Label("tdigest"), func() { - It("should TDigestAdd, TDigestCreate, TDigestInfo, TDigestByRank, TDigestByRevRank, TDigestCDF, TDigestMax, TDigestMin, TDigestQuantile, TDigestRank, TDigestRevRank, TDigestTrimmedMean, TDigestReset, ", Label("tdigest", "tdigestadd", "tdigestcreate", "tdigestinfo", "tdigestbyrank", "tdigestbyrevrank", "tdigestcdf", "tdigestmax", "tdigestmin", "tdigestquantile", "tdigestrank", "tdigestrevrank", "tdigesttrimmedmean", "tdigestreset"), func() { - err := client.TDigestCreate(ctx, "tdigest1").Err() - Expect(err).NotTo(HaveOccurred()) - - info, err := client.TDigestInfo(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(info.Observations).To(BeEquivalentTo(int64(0))) - - // Test with empty sketch - byRank, err := client.TDigestByRank(ctx, "tdigest1", 0, 1, 2, 3).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(byRank)).To(BeEquivalentTo(4)) - - byRevRank, err := client.TDigestByRevRank(ctx, "tdigest1", 0, 1, 2).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(byRevRank)).To(BeEquivalentTo(3)) - - cdf, err := client.TDigestCDF(ctx, "tdigest1", 15, 35, 70).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(cdf)).To(BeEquivalentTo(3)) - - max, err := client.TDigestMax(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(math.IsNaN(max)).To(BeTrue()) - - min, err := client.TDigestMin(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(math.IsNaN(min)).To(BeTrue()) - - quantile, err := client.TDigestQuantile(ctx, "tdigest1", 0.1, 0.2).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(quantile)).To(BeEquivalentTo(2)) - - rank, err := client.TDigestRank(ctx, "tdigest1", 10, 20).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rank)).To(BeEquivalentTo(2)) - - revRank, err := client.TDigestRevRank(ctx, "tdigest1", 10, 20).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(revRank)).To(BeEquivalentTo(2)) - - trimmedMean, err := client.TDigestTrimmedMean(ctx, "tdigest1", 0.1, 0.6).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(math.IsNaN(trimmedMean)).To(BeTrue()) - - // Add elements - err = client.TDigestAdd(ctx, "tdigest1", 10, 20, 30, 40, 50, 60, 70, 80, 90, 100).Err() - Expect(err).NotTo(HaveOccurred()) - - info, err = client.TDigestInfo(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(info.Observations).To(BeEquivalentTo(int64(10))) - - byRank, err = client.TDigestByRank(ctx, "tdigest1", 0, 1, 2).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(byRank)).To(BeEquivalentTo(3)) - Expect(byRank[0]).To(BeEquivalentTo(float64(10))) - Expect(byRank[1]).To(BeEquivalentTo(float64(20))) - Expect(byRank[2]).To(BeEquivalentTo(float64(30))) - - byRevRank, err = client.TDigestByRevRank(ctx, "tdigest1", 0, 1, 2).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(byRevRank)).To(BeEquivalentTo(3)) - Expect(byRevRank[0]).To(BeEquivalentTo(float64(100))) - Expect(byRevRank[1]).To(BeEquivalentTo(float64(90))) - Expect(byRevRank[2]).To(BeEquivalentTo(float64(80))) - - cdf, err = client.TDigestCDF(ctx, "tdigest1", 15, 35, 70).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(cdf)).To(BeEquivalentTo(3)) - Expect(cdf[0]).To(BeEquivalentTo(0.1)) - Expect(cdf[1]).To(BeEquivalentTo(0.3)) - Expect(cdf[2]).To(BeEquivalentTo(0.65)) - - max, err = client.TDigestMax(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(max).To(BeEquivalentTo(float64(100))) - - min, err = client.TDigestMin(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(min).To(BeEquivalentTo(float64(10))) - - quantile, err = client.TDigestQuantile(ctx, "tdigest1", 0.1, 0.2).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(quantile)).To(BeEquivalentTo(2)) - Expect(quantile[0]).To(BeEquivalentTo(float64(20))) - Expect(quantile[1]).To(BeEquivalentTo(float64(30))) - - rank, err = client.TDigestRank(ctx, "tdigest1", 10, 20).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(rank)).To(BeEquivalentTo(2)) - Expect(rank[0]).To(BeEquivalentTo(int64(0))) - Expect(rank[1]).To(BeEquivalentTo(int64(1))) - - revRank, err = client.TDigestRevRank(ctx, "tdigest1", 10, 20).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(len(revRank)).To(BeEquivalentTo(2)) - Expect(revRank[0]).To(BeEquivalentTo(int64(9))) - Expect(revRank[1]).To(BeEquivalentTo(int64(8))) - - trimmedMean, err = client.TDigestTrimmedMean(ctx, "tdigest1", 0.1, 0.6).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(trimmedMean).To(BeEquivalentTo(float64(40))) - - reset, err := client.TDigestReset(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(reset).To(BeEquivalentTo("OK")) - }) - - It("should TDigestCreateWithCompression", Label("tdigest", "tcreatewithcompression"), func() { - err := client.TDigestCreateWithCompression(ctx, "tdigest1", 2000).Err() - Expect(err).NotTo(HaveOccurred()) - - info, err := client.TDigestInfo(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(info.Compression).To(BeEquivalentTo(int64(2000))) - }) - - It("should TDigestMerge", Label("tdigest", "tmerge", "NonRedisEnterprise"), func() { - err := client.TDigestCreate(ctx, "tdigest1").Err() - Expect(err).NotTo(HaveOccurred()) - err = client.TDigestAdd(ctx, "tdigest1", 10, 20, 30, 40, 50, 60, 70, 80, 90, 100).Err() - Expect(err).NotTo(HaveOccurred()) - - err = client.TDigestCreate(ctx, "tdigest2").Err() - Expect(err).NotTo(HaveOccurred()) - err = client.TDigestAdd(ctx, "tdigest2", 15, 25, 35, 45, 55, 65, 75, 85, 95, 105).Err() - Expect(err).NotTo(HaveOccurred()) - - err = client.TDigestCreate(ctx, "tdigest3").Err() - Expect(err).NotTo(HaveOccurred()) - err = client.TDigestAdd(ctx, "tdigest3", 50, 60, 70, 80, 90, 100, 110, 120, 130, 140).Err() - Expect(err).NotTo(HaveOccurred()) - - options := &redis.TDigestMergeOptions{ - Compression: 1000, - Override: false, - } - err = client.TDigestMerge(ctx, "tdigest1", options, "tdigest2", "tdigest3").Err() - Expect(err).NotTo(HaveOccurred()) - - info, err := client.TDigestInfo(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(info.Observations).To(BeEquivalentTo(int64(30))) - Expect(info.Compression).To(BeEquivalentTo(int64(1000))) - - max, err := client.TDigestMax(ctx, "tdigest1").Result() - Expect(err).NotTo(HaveOccurred()) - Expect(max).To(BeEquivalentTo(float64(140))) + }) + + Describe("bloom", Label("bloom"), func() { + It("should BFAdd", Label("bloom", "bfadd"), func() { + resultAdd, err := client.BFAdd(ctx, "testbf1", 1).Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd).To(BeTrue()) + + resultInfo, err := client.BFInfo(ctx, "testbf1").Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo).To(BeAssignableToTypeOf(redis.BFInfo{})) + Expect(resultInfo.ItemsInserted).To(BeEquivalentTo(int64(1))) + }) + + It("should BFCard", Label("bloom", "bfcard"), func() { + // This is a probabilistic data structure, and it's not always guaranteed that we will get back + // the exact number of inserted items, during hash collisions + // But with such a low number of items (only 3), + // the probability of a collision is very low, so we can expect to get back the exact number of items + _, err := client.BFAdd(ctx, "testbf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.BFAdd(ctx, "testbf1", "item2").Result() + Expect(err).NotTo(HaveOccurred()) + _, err = client.BFAdd(ctx, "testbf1", 3).Result() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.BFCard(ctx, "testbf1").Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeEquivalentTo(int64(3))) + }) + + It("should BFExists", Label("bloom", "bfexists"), func() { + exists, err := client.BFExists(ctx, "testbf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeFalse()) + + _, err = client.BFAdd(ctx, "testbf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + + exists, err = client.BFExists(ctx, "testbf1", "item1").Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeTrue()) + }) + + It("should BFInfo and BFReserve", Label("bloom", "bfinfo", "bfreserve"), func() { + err := client.BFReserve(ctx, "testbf1", 0.001, 2000).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.BFInfo(ctx, "testbf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeAssignableToTypeOf(redis.BFInfo{})) + Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) + }) + + It("should BFInfoCapacity, BFInfoSize, BFInfoFilters, BFInfoItems, BFInfoExpansion, ", Label("bloom", "bfinfocapacity", "bfinfosize", "bfinfofilters", "bfinfoitems", "bfinfoexpansion"), func() { + err := client.BFReserve(ctx, "testbf1", 0.001, 2000).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.BFInfoCapacity(ctx, "testbf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) + + result, err = client.BFInfoItems(ctx, "testbf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.ItemsInserted).To(BeEquivalentTo(int64(0))) + + result, err = client.BFInfoSize(ctx, "testbf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Size).To(BeEquivalentTo(int64(4056))) + + err = client.BFReserveExpansion(ctx, "testbf2", 0.001, 2000, 3).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err = client.BFInfoFilters(ctx, "testbf2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.Filters).To(BeEquivalentTo(int64(1))) + + result, err = client.BFInfoExpansion(ctx, "testbf2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result.ExpansionRate).To(BeEquivalentTo(int64(3))) + }) + + It("should BFInsert", Label("bloom", "bfinsert"), func() { + options := &redis.BFInsertOptions{ + Capacity: 2000, + Error: 0.001, + Expansion: 3, + NonScaling: false, + NoCreate: true, + } + + _, err := client.BFInsert(ctx, "testbf1", options, "item1").Result() + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError("ERR not found")) + + options = &redis.BFInsertOptions{ + Capacity: 2000, + Error: 0.001, + Expansion: 3, + NonScaling: false, + NoCreate: false, + } + + resultInsert, err := client.BFInsert(ctx, "testbf1", options, "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultInsert)).To(BeEquivalentTo(1)) + + exists, err := client.BFExists(ctx, "testbf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeTrue()) + + result, err := client.BFInfo(ctx, "testbf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeAssignableToTypeOf(redis.BFInfo{})) + Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) + Expect(result.ExpansionRate).To(BeEquivalentTo(int64(3))) + }) + + It("should BFMAdd", Label("bloom", "bfmadd"), func() { + resultAdd, err := client.BFMAdd(ctx, "testbf1", "item1", "item2", "item3").Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultAdd)).To(Equal(3)) + + resultInfo, err := client.BFInfo(ctx, "testbf1").Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo).To(BeAssignableToTypeOf(redis.BFInfo{})) + Expect(resultInfo.ItemsInserted).To(BeEquivalentTo(int64(3))) + resultAdd2, err := client.BFMAdd(ctx, "testbf1", "item1", "item2", "item4").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultAdd2[0]).To(BeFalse()) + Expect(resultAdd2[1]).To(BeFalse()) + Expect(resultAdd2[2]).To(BeTrue()) + }) + + It("should BFMExists", Label("bloom", "bfmexists"), func() { + exist, err := client.BFMExists(ctx, "testbf1", "item1", "item2", "item3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(exist)).To(Equal(3)) + Expect(exist[0]).To(BeFalse()) + Expect(exist[1]).To(BeFalse()) + Expect(exist[2]).To(BeFalse()) + + _, err = client.BFMAdd(ctx, "testbf1", "item1", "item2", "item3").Result() + Expect(err).NotTo(HaveOccurred()) + + exist, err = client.BFMExists(ctx, "testbf1", "item1", "item2", "item3", "item4").Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(len(exist)).To(Equal(4)) + Expect(exist[0]).To(BeTrue()) + Expect(exist[1]).To(BeTrue()) + Expect(exist[2]).To(BeTrue()) + Expect(exist[3]).To(BeFalse()) + }) + + It("should BFReserveExpansion", Label("bloom", "bfreserveexpansion"), func() { + err := client.BFReserveExpansion(ctx, "testbf1", 0.001, 2000, 3).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.BFInfo(ctx, "testbf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeAssignableToTypeOf(redis.BFInfo{})) + Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) + Expect(result.ExpansionRate).To(BeEquivalentTo(int64(3))) + }) + + It("should BFReserveNonScaling", Label("bloom", "bfreservenonscaling"), func() { + err := client.BFReserveNonScaling(ctx, "testbfns1", 0.001, 1000).Err() + Expect(err).NotTo(HaveOccurred()) + + _, err = client.BFInfo(ctx, "testbfns1").Result() + Expect(err).To(HaveOccurred()) + }) + + It("should BFScanDump and BFLoadChunk", Label("bloom", "bfscandump", "bfloadchunk"), func() { + err := client.BFReserve(ctx, "testbfsd1", 0.001, 3000).Err() + Expect(err).NotTo(HaveOccurred()) + for i := 0; i < 1000; i++ { + client.BFAdd(ctx, "testbfsd1", i) + } + infBefore := client.BFInfoSize(ctx, "testbfsd1") + fd := []redis.ScanDump{} + sd, err := client.BFScanDump(ctx, "testbfsd1", 0).Result() + for { + if sd.Iter == 0 { + break + } + Expect(err).NotTo(HaveOccurred()) + fd = append(fd, sd) + sd, err = client.BFScanDump(ctx, "testbfsd1", sd.Iter).Result() + } + client.Del(ctx, "testbfsd1") + for _, e := range fd { + client.BFLoadChunk(ctx, "testbfsd1", e.Iter, e.Data) + } + infAfter := client.BFInfoSize(ctx, "testbfsd1") + Expect(infBefore).To(BeEquivalentTo(infAfter)) + }) + + It("should BFReserveWithArgs", Label("bloom", "bfreserveargs"), func() { + options := &redis.BFReserveOptions{ + Capacity: 2000, + Error: 0.001, + Expansion: 3, + NonScaling: false, + } + err := client.BFReserveWithArgs(ctx, "testbf", options).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.BFInfo(ctx, "testbf").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeAssignableToTypeOf(redis.BFInfo{})) + Expect(result.Capacity).To(BeEquivalentTo(int64(2000))) + Expect(result.ExpansionRate).To(BeEquivalentTo(int64(3))) + }) + }) + + Describe("cuckoo", Label("cuckoo"), func() { + It("should CFAdd", Label("cuckoo", "cfadd"), func() { + add, err := client.CFAdd(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(add).To(BeTrue()) + + exists, err := client.CFExists(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeTrue()) + + info, err := client.CFInfo(ctx, "testcf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(info).To(BeAssignableToTypeOf(redis.CFInfo{})) + Expect(info.NumItemsInserted).To(BeEquivalentTo(int64(1))) + }) + + It("should CFAddNX", Label("cuckoo", "cfaddnx"), func() { + add, err := client.CFAddNX(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(add).To(BeTrue()) + + exists, err := client.CFExists(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeTrue()) + + result, err := client.CFAddNX(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeFalse()) + + info, err := client.CFInfo(ctx, "testcf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(info).To(BeAssignableToTypeOf(redis.CFInfo{})) + Expect(info.NumItemsInserted).To(BeEquivalentTo(int64(1))) + }) + + It("should CFCount", Label("cuckoo", "cfcount"), func() { + err := client.CFAdd(ctx, "testcf1", "item1").Err() + cnt, err := client.CFCount(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(cnt).To(BeEquivalentTo(int64(1))) + + err = client.CFAdd(ctx, "testcf1", "item1").Err() + Expect(err).NotTo(HaveOccurred()) + + cnt, err = client.CFCount(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(cnt).To(BeEquivalentTo(int64(2))) + }) + + It("should CFDel and CFExists", Label("cuckoo", "cfdel", "cfexists"), func() { + err := client.CFAdd(ctx, "testcf1", "item1").Err() + Expect(err).NotTo(HaveOccurred()) + + exists, err := client.CFExists(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeTrue()) + + del, err := client.CFDel(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(del).To(BeTrue()) + + exists, err = client.CFExists(ctx, "testcf1", "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeFalse()) + }) + + It("should CFInfo and CFReserve", Label("cuckoo", "cfinfo", "cfreserve"), func() { + err := client.CFReserve(ctx, "testcf1", 1000).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.CFReserveExpansion(ctx, "testcfe1", 1000, 1).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.CFReserveBucketSize(ctx, "testcfbs1", 1000, 4).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.CFReserveMaxIterations(ctx, "testcfmi1", 1000, 10).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.CFInfo(ctx, "testcf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeAssignableToTypeOf(redis.CFInfo{})) + }) + + It("should CFScanDump and CFLoadChunk", Label("bloom", "cfscandump", "cfloadchunk"), func() { + err := client.CFReserve(ctx, "testcfsd1", 1000).Err() + Expect(err).NotTo(HaveOccurred()) + for i := 0; i < 1000; i++ { + Item := fmt.Sprintf("item%d", i) + client.CFAdd(ctx, "testcfsd1", Item) + } + infBefore := client.CFInfo(ctx, "testcfsd1") + fd := []redis.ScanDump{} + sd, err := client.CFScanDump(ctx, "testcfsd1", 0).Result() + for { + if sd.Iter == 0 { + break + } + Expect(err).NotTo(HaveOccurred()) + fd = append(fd, sd) + sd, err = client.CFScanDump(ctx, "testcfsd1", sd.Iter).Result() + } + client.Del(ctx, "testcfsd1") + for _, e := range fd { + client.CFLoadChunk(ctx, "testcfsd1", e.Iter, e.Data) + } + infAfter := client.CFInfo(ctx, "testcfsd1") + Expect(infBefore).To(BeEquivalentTo(infAfter)) + }) + + It("should CFInfo and CFReserveWithArgs", Label("cuckoo", "cfinfo", "cfreserveargs"), func() { + args := &redis.CFReserveOptions{ + Capacity: 2048, + BucketSize: 3, + MaxIterations: 15, + Expansion: 2, + } + + err := client.CFReserveWithArgs(ctx, "testcf1", args).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.CFInfo(ctx, "testcf1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(BeAssignableToTypeOf(redis.CFInfo{})) + Expect(result.BucketSize).To(BeEquivalentTo(int64(3))) + Expect(result.MaxIteration).To(BeEquivalentTo(int64(15))) + Expect(result.ExpansionRate).To(BeEquivalentTo(int64(2))) + }) + + It("should CFInsert", Label("cuckoo", "cfinsert"), func() { + args := &redis.CFInsertOptions{ + Capacity: 3000, + NoCreate: true, + } + + result, err := client.CFInsert(ctx, "testcf1", args, "item1", "item2", "item3").Result() + Expect(err).To(HaveOccurred()) + + args = &redis.CFInsertOptions{ + Capacity: 3000, + NoCreate: false, + } + + result, err = client.CFInsert(ctx, "testcf1", args, "item1", "item2", "item3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(3)) + }) + + It("should CFInsertNX", Label("cuckoo", "cfinsertnx"), func() { + args := &redis.CFInsertOptions{ + Capacity: 3000, + NoCreate: true, + } + + _, err := client.CFInsertNX(ctx, "testcf1", args, "item1", "item2", "item2").Result() + Expect(err).To(HaveOccurred()) + + args = &redis.CFInsertOptions{ + Capacity: 3000, + NoCreate: false, + } + + result, err := client.CFInsertNX(ctx, "testcf2", args, "item1", "item2", "item2").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(3)) + Expect(result[0]).To(BeEquivalentTo(int64(1))) + Expect(result[1]).To(BeEquivalentTo(int64(1))) + Expect(result[2]).To(BeEquivalentTo(int64(0))) + }) + + It("should CFMexists", Label("cuckoo", "cfmexists"), func() { + err := client.CFInsert(ctx, "testcf1", nil, "item1", "item2", "item3").Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.CFMExists(ctx, "testcf1", "item1", "item2", "item3", "item4").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(4)) + Expect(result[0]).To(BeTrue()) + Expect(result[1]).To(BeTrue()) + Expect(result[2]).To(BeTrue()) + Expect(result[3]).To(BeFalse()) + }) + }) + + Describe("CMS", Label("cms"), func() { + It("should CMSIncrBy", Label("cms", "cmsincrby"), func() { + err := client.CMSInitByDim(ctx, "testcms1", 5, 10).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.CMSIncrBy(ctx, "testcms1", "item1", 1, "item2", 2, "item3", 3).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(3)) + Expect(result[0]).To(BeEquivalentTo(int64(1))) + Expect(result[1]).To(BeEquivalentTo(int64(2))) + Expect(result[2]).To(BeEquivalentTo(int64(3))) + }) + + It("should CMSInitByDim and CMSInfo", Label("cms", "cmsinitbydim", "cmsinfo"), func() { + err := client.CMSInitByDim(ctx, "testcms1", 5, 10).Err() + Expect(err).NotTo(HaveOccurred()) + + info, err := client.CMSInfo(ctx, "testcms1").Result() + Expect(err).NotTo(HaveOccurred()) + + Expect(info).To(BeAssignableToTypeOf(redis.CMSInfo{})) + Expect(info.Width).To(BeEquivalentTo(int64(5))) + Expect(info.Depth).To(BeEquivalentTo(int64(10))) + }) + + It("should CMSInitByProb", Label("cms", "cmsinitbyprob"), func() { + err := client.CMSInitByProb(ctx, "testcms1", 0.002, 0.01).Err() + Expect(err).NotTo(HaveOccurred()) + + info, err := client.CMSInfo(ctx, "testcms1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(info).To(BeAssignableToTypeOf(redis.CMSInfo{})) + }) + + It("should CMSMerge, CMSMergeWithWeight and CMSQuery", Label("cms", "cmsmerge", "cmsquery", "NonRedisEnterprise"), func() { + err := client.CMSMerge(ctx, "destCms1", "testcms2", "testcms3").Err() + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError("CMS: key does not exist")) + + err = client.CMSInitByDim(ctx, "destCms1", 5, 10).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.CMSInitByDim(ctx, "destCms2", 5, 10).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.CMSInitByDim(ctx, "cms1", 2, 20).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.CMSInitByDim(ctx, "cms2", 3, 20).Err() + Expect(err).NotTo(HaveOccurred()) + + err = client.CMSMerge(ctx, "destCms1", "cms1", "cms2").Err() + Expect(err).To(MatchError("CMS: width/depth is not equal")) + + client.Del(ctx, "cms1", "cms2") + + err = client.CMSInitByDim(ctx, "cms1", 5, 10).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.CMSInitByDim(ctx, "cms2", 5, 10).Err() + Expect(err).NotTo(HaveOccurred()) + + client.CMSIncrBy(ctx, "cms1", "item1", 1, "item2", 2) + client.CMSIncrBy(ctx, "cms2", "item2", 2, "item3", 3) + + err = client.CMSMerge(ctx, "destCms1", "cms1", "cms2").Err() + Expect(err).NotTo(HaveOccurred()) + + result, err := client.CMSQuery(ctx, "destCms1", "item1", "item2", "item3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(3)) + Expect(result[0]).To(BeEquivalentTo(int64(1))) + Expect(result[1]).To(BeEquivalentTo(int64(4))) + Expect(result[2]).To(BeEquivalentTo(int64(3))) + + sourceSketches := map[string]int64{ + "cms1": 1, + "cms2": 2, + } + err = client.CMSMergeWithWeight(ctx, "destCms2", sourceSketches).Err() + Expect(err).NotTo(HaveOccurred()) + + result, err = client.CMSQuery(ctx, "destCms2", "item1", "item2", "item3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(BeEquivalentTo(3)) + Expect(result[0]).To(BeEquivalentTo(int64(1))) + Expect(result[1]).To(BeEquivalentTo(int64(6))) + Expect(result[2]).To(BeEquivalentTo(int64(6))) + }) + }) + + Describe("TopK", Label("topk"), func() { + It("should TopKReserve, TopKInfo, TopKAdd, TopKQuery, TopKCount, TopKIncrBy, TopKList, TopKListWithCount", Label("topk", "topkreserve", "topkinfo", "topkadd", "topkquery", "topkcount", "topkincrby", "topklist", "topklistwithcount"), func() { + err := client.TopKReserve(ctx, "topk1", 3).Err() + Expect(err).NotTo(HaveOccurred()) + + resultInfo, err := client.TopKInfo(ctx, "topk1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo.K).To(BeEquivalentTo(int64(3))) + + resultAdd, err := client.TopKAdd(ctx, "topk1", "item1", "item2", 3, "item1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultAdd)).To(BeEquivalentTo(int64(4))) + + resultQuery, err := client.TopKQuery(ctx, "topk1", "item1", "item2", 4, 3).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultQuery)).To(BeEquivalentTo(4)) + Expect(resultQuery[0]).To(BeTrue()) + Expect(resultQuery[1]).To(BeTrue()) + Expect(resultQuery[2]).To(BeFalse()) + Expect(resultQuery[3]).To(BeTrue()) + + resultCount, err := client.TopKCount(ctx, "topk1", "item1", "item2", "item3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultCount)).To(BeEquivalentTo(3)) + Expect(resultCount[0]).To(BeEquivalentTo(int64(2))) + Expect(resultCount[1]).To(BeEquivalentTo(int64(1))) + Expect(resultCount[2]).To(BeEquivalentTo(int64(0))) + + resultIncr, err := client.TopKIncrBy(ctx, "topk1", "item1", 5, "item2", 10).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultIncr)).To(BeEquivalentTo(2)) + + resultCount, err = client.TopKCount(ctx, "topk1", "item1", "item2", "item3").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultCount)).To(BeEquivalentTo(3)) + Expect(resultCount[0]).To(BeEquivalentTo(int64(7))) + Expect(resultCount[1]).To(BeEquivalentTo(int64(11))) + Expect(resultCount[2]).To(BeEquivalentTo(int64(0))) + + resultList, err := client.TopKList(ctx, "topk1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultList)).To(BeEquivalentTo(3)) + Expect(resultList).To(ContainElements("item2", "item1", "3")) + + resultListWithCount, err := client.TopKListWithCount(ctx, "topk1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultListWithCount)).To(BeEquivalentTo(3)) + Expect(resultListWithCount["3"]).To(BeEquivalentTo(int64(1))) + Expect(resultListWithCount["item1"]).To(BeEquivalentTo(int64(7))) + Expect(resultListWithCount["item2"]).To(BeEquivalentTo(int64(11))) + }) + + It("should TopKReserveWithOptions", Label("topk", "topkreservewithoptions"), func() { + err := client.TopKReserveWithOptions(ctx, "topk1", 3, 1500, 8, 0.5).Err() + Expect(err).NotTo(HaveOccurred()) + + resultInfo, err := client.TopKInfo(ctx, "topk1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(resultInfo.K).To(BeEquivalentTo(int64(3))) + Expect(resultInfo.Width).To(BeEquivalentTo(int64(1500))) + Expect(resultInfo.Depth).To(BeEquivalentTo(int64(8))) + Expect(resultInfo.Decay).To(BeEquivalentTo(0.5)) + }) + }) + + Describe("t-digest", Label("tdigest"), func() { + It("should TDigestAdd, TDigestCreate, TDigestInfo, TDigestByRank, TDigestByRevRank, TDigestCDF, TDigestMax, TDigestMin, TDigestQuantile, TDigestRank, TDigestRevRank, TDigestTrimmedMean, TDigestReset, ", Label("tdigest", "tdigestadd", "tdigestcreate", "tdigestinfo", "tdigestbyrank", "tdigestbyrevrank", "tdigestcdf", "tdigestmax", "tdigestmin", "tdigestquantile", "tdigestrank", "tdigestrevrank", "tdigesttrimmedmean", "tdigestreset"), func() { + err := client.TDigestCreate(ctx, "tdigest1").Err() + Expect(err).NotTo(HaveOccurred()) + + info, err := client.TDigestInfo(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(info.Observations).To(BeEquivalentTo(int64(0))) + + // Test with empty sketch + byRank, err := client.TDigestByRank(ctx, "tdigest1", 0, 1, 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(byRank)).To(BeEquivalentTo(4)) + + byRevRank, err := client.TDigestByRevRank(ctx, "tdigest1", 0, 1, 2).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(byRevRank)).To(BeEquivalentTo(3)) + + cdf, err := client.TDigestCDF(ctx, "tdigest1", 15, 35, 70).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(cdf)).To(BeEquivalentTo(3)) + + max, err := client.TDigestMax(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(math.IsNaN(max)).To(BeTrue()) + + min, err := client.TDigestMin(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(math.IsNaN(min)).To(BeTrue()) + + quantile, err := client.TDigestQuantile(ctx, "tdigest1", 0.1, 0.2).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(quantile)).To(BeEquivalentTo(2)) + + rank, err := client.TDigestRank(ctx, "tdigest1", 10, 20).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rank)).To(BeEquivalentTo(2)) + + revRank, err := client.TDigestRevRank(ctx, "tdigest1", 10, 20).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(revRank)).To(BeEquivalentTo(2)) + + trimmedMean, err := client.TDigestTrimmedMean(ctx, "tdigest1", 0.1, 0.6).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(math.IsNaN(trimmedMean)).To(BeTrue()) + + // Add elements + err = client.TDigestAdd(ctx, "tdigest1", 10, 20, 30, 40, 50, 60, 70, 80, 90, 100).Err() + Expect(err).NotTo(HaveOccurred()) + + info, err = client.TDigestInfo(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(info.Observations).To(BeEquivalentTo(int64(10))) + + byRank, err = client.TDigestByRank(ctx, "tdigest1", 0, 1, 2).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(byRank)).To(BeEquivalentTo(3)) + Expect(byRank[0]).To(BeEquivalentTo(float64(10))) + Expect(byRank[1]).To(BeEquivalentTo(float64(20))) + Expect(byRank[2]).To(BeEquivalentTo(float64(30))) + + byRevRank, err = client.TDigestByRevRank(ctx, "tdigest1", 0, 1, 2).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(byRevRank)).To(BeEquivalentTo(3)) + Expect(byRevRank[0]).To(BeEquivalentTo(float64(100))) + Expect(byRevRank[1]).To(BeEquivalentTo(float64(90))) + Expect(byRevRank[2]).To(BeEquivalentTo(float64(80))) + + cdf, err = client.TDigestCDF(ctx, "tdigest1", 15, 35, 70).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(cdf)).To(BeEquivalentTo(3)) + Expect(cdf[0]).To(BeEquivalentTo(0.1)) + Expect(cdf[1]).To(BeEquivalentTo(0.3)) + Expect(cdf[2]).To(BeEquivalentTo(0.65)) + + max, err = client.TDigestMax(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(max).To(BeEquivalentTo(float64(100))) + + min, err = client.TDigestMin(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(min).To(BeEquivalentTo(float64(10))) + + quantile, err = client.TDigestQuantile(ctx, "tdigest1", 0.1, 0.2).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(quantile)).To(BeEquivalentTo(2)) + Expect(quantile[0]).To(BeEquivalentTo(float64(20))) + Expect(quantile[1]).To(BeEquivalentTo(float64(30))) + + rank, err = client.TDigestRank(ctx, "tdigest1", 10, 20).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(rank)).To(BeEquivalentTo(2)) + Expect(rank[0]).To(BeEquivalentTo(int64(0))) + Expect(rank[1]).To(BeEquivalentTo(int64(1))) + + revRank, err = client.TDigestRevRank(ctx, "tdigest1", 10, 20).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(revRank)).To(BeEquivalentTo(2)) + Expect(revRank[0]).To(BeEquivalentTo(int64(9))) + Expect(revRank[1]).To(BeEquivalentTo(int64(8))) + + trimmedMean, err = client.TDigestTrimmedMean(ctx, "tdigest1", 0.1, 0.6).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(trimmedMean).To(BeEquivalentTo(float64(40))) + + reset, err := client.TDigestReset(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(reset).To(BeEquivalentTo("OK")) + }) + + It("should TDigestCreateWithCompression", Label("tdigest", "tcreatewithcompression"), func() { + err := client.TDigestCreateWithCompression(ctx, "tdigest1", 2000).Err() + Expect(err).NotTo(HaveOccurred()) + + info, err := client.TDigestInfo(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(info.Compression).To(BeEquivalentTo(int64(2000))) + }) + + It("should TDigestMerge", Label("tdigest", "tmerge", "NonRedisEnterprise"), func() { + err := client.TDigestCreate(ctx, "tdigest1").Err() + Expect(err).NotTo(HaveOccurred()) + err = client.TDigestAdd(ctx, "tdigest1", 10, 20, 30, 40, 50, 60, 70, 80, 90, 100).Err() + Expect(err).NotTo(HaveOccurred()) + + err = client.TDigestCreate(ctx, "tdigest2").Err() + Expect(err).NotTo(HaveOccurred()) + err = client.TDigestAdd(ctx, "tdigest2", 15, 25, 35, 45, 55, 65, 75, 85, 95, 105).Err() + Expect(err).NotTo(HaveOccurred()) + + err = client.TDigestCreate(ctx, "tdigest3").Err() + Expect(err).NotTo(HaveOccurred()) + err = client.TDigestAdd(ctx, "tdigest3", 50, 60, 70, 80, 90, 100, 110, 120, 130, 140).Err() + Expect(err).NotTo(HaveOccurred()) + + options := &redis.TDigestMergeOptions{ + Compression: 1000, + Override: false, + } + err = client.TDigestMerge(ctx, "tdigest1", options, "tdigest2", "tdigest3").Err() + Expect(err).NotTo(HaveOccurred()) + + info, err := client.TDigestInfo(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(info.Observations).To(BeEquivalentTo(int64(30))) + Expect(info.Compression).To(BeEquivalentTo(int64(1000))) + + max, err := client.TDigestMax(ctx, "tdigest1").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(max).To(BeEquivalentTo(float64(140))) + }) + }) }) - }) + } }) From 930d904205691ff06104fcc3ac108177077def35 Mon Sep 17 00:00:00 2001 From: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:20:59 +0200 Subject: [PATCH 05/11] Add guidance on unstable RESP3 support for RediSearch commands to README (#3177) * Add UnstableResp3 to docs * Add RawVal and RawResult to wordlist * Explain more about SetVal * Add UnstableResp to wordlist --- .github/wordlist.txt | 3 +++ README.md | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/wordlist.txt b/.github/wordlist.txt index c200c60b4..1fc34f733 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -54,6 +54,7 @@ stunnel SynDump TCP TLS +UnstableResp uri URI url @@ -62,3 +63,5 @@ RedisStack RedisGears RedisTimeseries RediSearch +RawResult +RawVal \ No newline at end of file diff --git a/README.md b/README.md index 37714a979..e71367659 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,21 @@ rdb := redis.NewClient(&redis.Options{ #### Unstable RESP3 Structures for RediSearch Commands When integrating Redis with application functionalities using RESP3, it's important to note that some response structures aren't final yet. This is especially true for more complex structures like search and query results. We recommend using RESP2 when using the search and query capabilities, but we plan to stabilize the RESP3-based API-s in the coming versions. You can find more guidance in the upcoming release notes. +To enable unstable RESP3, set the option in your client configuration: + +```go +redis.NewClient(&redis.Options{ + UnstableResp3: true, + }) +``` +**Note:** When UnstableResp3 mode is enabled, it's necessary to use RawResult() and RawVal() to retrieve a raw data. + Since, raw response is the only option for unstable search commands Val() and Result() calls wouldn't have any affect on them: + +```go +res1, err := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{}).RawResult() +val1 := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{}).RawVal() +``` + ## Contributing Please see [out contributing guidelines](CONTRIBUTING.md) to help us improve this library! From 080e051124d5e35a0d0c49e03f8547e86365c5d8 Mon Sep 17 00:00:00 2001 From: LINKIWI Date: Wed, 20 Nov 2024 03:38:06 -0800 Subject: [PATCH 06/11] Eliminate redundant dial mutex causing unbounded connection queue contention (#3088) * Eliminate redundant dial mutex causing unbounded connection queue contention * Dialer connection timeouts unit test --------- Co-authored-by: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com> --- redis.go | 2 -- redis_test.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/redis.go b/redis.go index c8b500809..2f576bdbe 100644 --- a/redis.go +++ b/redis.go @@ -176,8 +176,6 @@ func (hs *hooksMixin) withProcessPipelineHook( } func (hs *hooksMixin) dialHook(ctx context.Context, network, addr string) (net.Conn, error) { - hs.hooksMu.Lock() - defer hs.hooksMu.Unlock() return hs.current.dial(ctx, network, addr) } diff --git a/redis_test.go b/redis_test.go index ef2125452..b5cf2570f 100644 --- a/redis_test.go +++ b/redis_test.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net" + "sync" "testing" "time" @@ -633,3 +634,67 @@ var _ = Describe("Hook with MinIdleConns", func() { })) }) }) + +var _ = Describe("Dialer connection timeouts", func() { + var client *redis.Client + + const dialSimulatedDelay = 1 * time.Second + + BeforeEach(func() { + options := redisOptions() + options.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) { + // Simulated slow dialer. + // Note that the following sleep is deliberately not context-aware. + time.Sleep(dialSimulatedDelay) + return net.Dial("tcp", options.Addr) + } + options.MinIdleConns = 1 + client = redis.NewClient(options) + }) + + AfterEach(func() { + err := client.Close() + Expect(err).NotTo(HaveOccurred()) + }) + + It("does not contend on connection dial for concurrent commands", func() { + var wg sync.WaitGroup + + const concurrency = 10 + + durations := make(chan time.Duration, concurrency) + errs := make(chan error, concurrency) + + start := time.Now() + wg.Add(concurrency) + + for i := 0; i < concurrency; i++ { + go func() { + defer wg.Done() + + start := time.Now() + err := client.Ping(ctx).Err() + durations <- time.Since(start) + errs <- err + }() + } + + wg.Wait() + close(durations) + close(errs) + + // All commands should eventually succeed, after acquiring a connection. + for err := range errs { + Expect(err).NotTo(HaveOccurred()) + } + + // Each individual command should complete within the simulated dial duration bound. + for duration := range durations { + Expect(duration).To(BeNumerically("<", 2*dialSimulatedDelay)) + } + + // Due to concurrent execution, the entire test suite should also complete within + // the same dial duration bound applied for individual commands. + Expect(time.Since(start)).To(BeNumerically("<", 2*dialSimulatedDelay)) + }) +}) From f1ffb55c9aeea7dceab2bcf4970f168dbcf5a484 Mon Sep 17 00:00:00 2001 From: Justin <8886628+justinmir@users.noreply.github.com> Date: Wed, 20 Nov 2024 06:36:39 -0600 Subject: [PATCH 07/11] Only check latencies once every 10 seconds with `routeByLatency` (#2795) * Only check latencies once every 10 seconds with `routeByLatency` `routeByLatency` currently checks latencies any time a server returns a MOVED or READONLY reply. When a shard is down, the ClusterClient chooses to issue the request to a random server, which returns a MOVED reply. This causes a state refresh and a latency update on all servers. This can lead to significant ping load to clusters with a large number of clients. This introduces logic to ping only once every 10 seconds, only performing a latency update on a node during the `GC` function if the latency was set later than 10 seconds ago. Fixes https://github.com/redis/go-redis/issues/2782 * use UnixNano instead of Unix for better precision --------- Co-authored-by: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com> --- osscluster.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osscluster.go b/osscluster.go index ce258ff36..72e922a80 100644 --- a/osscluster.go +++ b/osscluster.go @@ -21,6 +21,10 @@ import ( "github.com/redis/go-redis/v9/internal/rand" ) +const ( + minLatencyMeasurementInterval = 10 * time.Second +) + var errClusterNoNodes = fmt.Errorf("redis: cluster has no nodes") // ClusterOptions are used to configure a cluster client and should be @@ -316,6 +320,10 @@ type clusterNode struct { latency uint32 // atomic generation uint32 // atomic failing uint32 // atomic + + // last time the latency measurement was performed for the node, stored in nanoseconds + // from epoch + lastLatencyMeasurement int64 // atomic } func newClusterNode(clOpt *ClusterOptions, addr string) *clusterNode { @@ -368,6 +376,7 @@ func (n *clusterNode) updateLatency() { latency = float64(dur) / float64(successes) } atomic.StoreUint32(&n.latency, uint32(latency+0.5)) + n.SetLastLatencyMeasurement(time.Now()) } func (n *clusterNode) Latency() time.Duration { @@ -397,6 +406,10 @@ func (n *clusterNode) Generation() uint32 { return atomic.LoadUint32(&n.generation) } +func (n *clusterNode) LastLatencyMeasurement() int64 { + return atomic.LoadInt64(&n.lastLatencyMeasurement) +} + func (n *clusterNode) SetGeneration(gen uint32) { for { v := atomic.LoadUint32(&n.generation) @@ -406,6 +419,15 @@ func (n *clusterNode) SetGeneration(gen uint32) { } } +func (n *clusterNode) SetLastLatencyMeasurement(t time.Time) { + for { + v := atomic.LoadInt64(&n.lastLatencyMeasurement) + if t.UnixNano() < v || atomic.CompareAndSwapInt64(&n.lastLatencyMeasurement, v, t.UnixNano()) { + break + } + } +} + //------------------------------------------------------------------------------ type clusterNodes struct { @@ -493,10 +515,11 @@ func (c *clusterNodes) GC(generation uint32) { c.mu.Lock() c.activeAddrs = c.activeAddrs[:0] + now := time.Now() for addr, node := range c.nodes { if node.Generation() >= generation { c.activeAddrs = append(c.activeAddrs, addr) - if c.opt.RouteByLatency { + if c.opt.RouteByLatency && node.LastLatencyMeasurement() < now.Add(-minLatencyMeasurementInterval).UnixNano() { go node.updateLatency() } continue From fc32d0a01d447fcdcfb90dd33a044f3661b9ccdf Mon Sep 17 00:00:00 2001 From: LINKIWI Date: Thu, 21 Nov 2024 04:38:11 -0800 Subject: [PATCH 08/11] Recognize byte slice for key argument in cluster client hash slot computation (#3049) Co-authored-by: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Co-authored-by: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com> --- command.go | 2 ++ osscluster_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/command.go b/command.go index 7ea7862d5..3cb9538a5 100644 --- a/command.go +++ b/command.go @@ -167,6 +167,8 @@ func (cmd *baseCmd) stringArg(pos int) string { switch v := arg.(type) { case string: return v + case []byte: + return string(v) default: // TODO: consider using appendArg return fmt.Sprint(v) diff --git a/osscluster_test.go b/osscluster_test.go index f7bd1683f..9c3eaba35 100644 --- a/osscluster_test.go +++ b/osscluster_test.go @@ -653,6 +653,32 @@ var _ = Describe("ClusterClient", func() { Expect(client.Close()).NotTo(HaveOccurred()) }) + It("determines hash slots correctly for generic commands", func() { + opt := redisClusterOptions() + opt.MaxRedirects = -1 + client := cluster.newClusterClient(ctx, opt) + + err := client.Do(ctx, "GET", "A").Err() + Expect(err).To(Equal(redis.Nil)) + + err = client.Do(ctx, []byte("GET"), []byte("A")).Err() + Expect(err).To(Equal(redis.Nil)) + + Eventually(func() error { + return client.SwapNodes(ctx, "A") + }, 30*time.Second).ShouldNot(HaveOccurred()) + + err = client.Do(ctx, "GET", "A").Err() + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("MOVED")) + + err = client.Do(ctx, []byte("GET"), []byte("A")).Err() + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("MOVED")) + + Expect(client.Close()).NotTo(HaveOccurred()) + }) + It("follows node redirection immediately", func() { // Configure retry backoffs far in excess of the expected duration of redirection opt := redisClusterOptions() From e63669e1706936ac794277340c51a51c5facca70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:38:38 +0200 Subject: [PATCH 09/11] chore(deps): bump rojopolis/spellcheck-github-actions (#3188) Bumps [rojopolis/spellcheck-github-actions](https://github.com/rojopolis/spellcheck-github-actions) from 0.40.0 to 0.45.0. - [Release notes](https://github.com/rojopolis/spellcheck-github-actions/releases) - [Changelog](https://github.com/rojopolis/spellcheck-github-actions/blob/master/CHANGELOG.md) - [Commits](https://github.com/rojopolis/spellcheck-github-actions/compare/0.40.0...0.45.0) --- updated-dependencies: - dependency-name: rojopolis/spellcheck-github-actions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com> --- .github/workflows/spellcheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index cc6d828c9..977f8c5c1 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -8,7 +8,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Check Spelling - uses: rojopolis/spellcheck-github-actions@0.40.0 + uses: rojopolis/spellcheck-github-actions@0.45.0 with: config_path: .github/spellcheck-settings.yml task_name: Markdown From 73cc5f7c215f6f2079608fdbf32420f28ee36347 Mon Sep 17 00:00:00 2001 From: Cgol9 Date: Thu, 5 Dec 2024 01:10:04 -0700 Subject: [PATCH 10/11] SortByWithCount FTSearchOptions fix (#3201) * SortByWithCount FTSearchOptions fix * FTSearch test fix * Another FTSearch test fix * Another FTSearch test fix --------- Co-authored-by: Christopher Golling --- search_commands.go | 2 +- search_test.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/search_commands.go b/search_commands.go index e4df0b6fc..ede084e4e 100644 --- a/search_commands.go +++ b/search_commands.go @@ -1775,7 +1775,7 @@ func FTSearchQuery(query string, options *FTSearchOptions) SearchQuery { } } if options.SortByWithCount { - queryArgs = append(queryArgs, "WITHCOUT") + queryArgs = append(queryArgs, "WITHCOUNT") } } if options.LimitOffset >= 0 && options.Limit > 0 { diff --git a/search_test.go b/search_test.go index 48b9aa39b..e267c8ae8 100644 --- a/search_test.go +++ b/search_test.go @@ -125,6 +125,10 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() { Expect(res2.Docs[1].ID).To(BeEquivalentTo("doc2")) Expect(res2.Docs[0].ID).To(BeEquivalentTo("doc3")) + res3, err := client.FTSearchWithArgs(ctx, "num", "foo", &redis.FTSearchOptions{NoContent: true, SortBy: []redis.FTSearchSortBy{sortBy2}, SortByWithCount: true}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res3.Total).To(BeEquivalentTo(int64(0))) + }) It("should FTCreate and FTSearch example", Label("search", "ftcreate", "ftsearch"), func() { From caa2592db731428e542670e8e6d3ccfa60603e8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:11:12 +0200 Subject: [PATCH 11/11] chore(deps): bump codecov/codecov-action from 4 to 5 (#3196) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ofekshenawa <104765379+ofekshenawa@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5007423a4..c1d04b820 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: run: make test - name: Upload to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: files: coverage.txt token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file