diff --git a/doctests/timeseries_tut_test.go b/doctests/timeseries_tut_test.go new file mode 100644 index 000000000..28e163539 --- /dev/null +++ b/doctests/timeseries_tut_test.go @@ -0,0 +1,1163 @@ +// EXAMPLE: time_series_tutorial +// HIDE_START +package example_commands_test + +import ( + "context" + "fmt" + "maps" + "math" + "slices" + "sort" + + "github.com/redis/go-redis/v9" +) + +// HIDE_END + +func ExampleClient_timeseries_create() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "thermometer:1", "thermometer:2", "thermometer:3") + // REMOVE_END + + // STEP_START create + res1, err := rdb.TSCreate(ctx, "thermometer:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.Type(ctx, "thermometer:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> TSDB-TYPE + + res3, err := rdb.TSInfo(ctx, "thermometer:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res3["totalSamples"]) // >>> 0 + // STEP_END + + // STEP_START create_retention + res4, err := rdb.TSAddWithArgs( + ctx, + "thermometer:2", + 1, + 10.8, + &redis.TSOptions{ + Retention: 100, + }, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> 1 + + res5, err := rdb.TSInfo(ctx, "thermometer:2").Result() + if err != nil { + panic(err) + } + + fmt.Println(res5["retentionTime"]) // >>> 100 + // STEP_END + + // STEP_START create_labels + res6, err := rdb.TSAddWithArgs( + ctx, + "thermometer:3", + 1, + 10.4, + &redis.TSOptions{ + Labels: map[string]string{ + "location": "UK", + "type": "Mercury", + }, + }, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res6) // >>> 1 + + res7, err := rdb.TSInfo(ctx, "thermometer:3").Result() + if err != nil { + panic(err) + } + + fmt.Println(res7["labels"]) + // >>> map[location:UK type:Mercury] + // STEP_END + + // Output: + // OK + // TSDB-TYPE + // 0 + // 1 + // 100 + // 1 + // map[location:UK type:Mercury] +} + +func ExampleClient_timeseries_add() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "thermometer:1", "thermometer:2") + rdb.TSCreate(ctx, "thermometer:1") + rdb.TSCreate(ctx, "thermometer:2") + // REMOVE_END + + // STEP_START madd + res1, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"thermometer:1", 1, 9.2}, + {"thermometer:1", 2, 9.9}, + {"thermometer:2", 2, 10.3}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> [1 2 2] + // STEP_END + + // STEP_START get + // The last recorded temperature for thermometer:2 + // was 10.3 at time 2. + res2, err := rdb.TSGet(ctx, "thermometer:2").Result() + if err != nil { + panic(err) + } + + fmt.Println(res2) + // >>> {2 10.3} + // STEP_END + + // Output: + // [1 2 2] + // {2 10.3} +} + +func ExampleClient_timeseries_range() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "rg:1") + // REMOVE_END + + // STEP_START range + // Add 5 data points to a time series named "rg:1". + res1, err := rdb.TSCreate(ctx, "rg:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"rg:1", 0, 18}, + {"rg:1", 1, 14}, + {"rg:1", 2, 22}, + {"rg:1", 3, 18}, + {"rg:1", 4, 24}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [0 1 2 3 4] + + // Retrieve all the data points in ascending order. + // Note: use 0 and `math.MaxInt64` instead of - and + + // to denote the minimum and maximum possible timestamps. + res3, err := rdb.TSRange(ctx, "rg:1", 0, math.MaxInt64).Result() + if err != nil { + panic(err) + } + + fmt.Println(res3) + // >>> [{0 18} {1 14} {2 22} {3 18} {4 24}] + + // Retrieve data points up to time 1 (inclusive). + res4, err := rdb.TSRange(ctx, "rg:1", 0, 1).Result() + if err != nil { + panic(err) + } + + fmt.Println(res4) + // >>> [{0 18} {1 14}] + + // Retrieve data points from time 3 onwards. + res5, err := rdb.TSRange(ctx, "rg:1", 3, math.MaxInt64).Result() + if err != nil { + panic(err) + } + + fmt.Println(res5) + // >>> [{3 18} {4 24}] + + // Retrieve all the data points in descending order. + res6, err := rdb.TSRevRange(ctx, "rg:1", 0, math.MaxInt64).Result() + if err != nil { + panic(err) + } + + fmt.Println(res6) + // >>> [{4 24} {3 18} {2 22} {1 14} {0 18}] + + // Retrieve data points up to time 1 (inclusive), but return them + // in descending order. + res7, err := rdb.TSRevRange(ctx, "rg:1", 0, 1).Result() + if err != nil { + panic(err) + } + + fmt.Println(res7) + // >>> [{1 14} {0 18}] + // STEP_END + + // STEP_START range_filter + res8, err := rdb.TSRangeWithArgs( + ctx, + "rg:1", + 0, + math.MaxInt64, + &redis.TSRangeOptions{ + FilterByTS: []int{0, 2, 4}, + }, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res8) // >>> [{0 18} {2 22} {4 24}] + + res9, err := rdb.TSRevRangeWithArgs( + ctx, + "rg:1", + 0, + math.MaxInt64, + &redis.TSRevRangeOptions{ + FilterByTS: []int{0, 2, 4}, + FilterByValue: []int{20, 25}, + }, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res9) // >>> [{4 24} {2 22}] + + res10, err := rdb.TSRevRangeWithArgs( + ctx, + "rg:1", + 0, + math.MaxInt64, + &redis.TSRevRangeOptions{ + FilterByTS: []int{0, 2, 4}, + FilterByValue: []int{22, 22}, + }, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res10) // >>> [{2 22}] + // STEP_END + + // Output: + // OK + // [0 1 2 3 4] + // [{0 18} {1 14} {2 22} {3 18} {4 24}] + // [{0 18} {1 14}] + // [{3 18} {4 24}] + // [{4 24} {3 18} {2 22} {1 14} {0 18}] + // [{1 14} {0 18}] + // [{0 18} {2 22} {4 24}] + // [{4 24} {2 22}] + // [{2 22}] +} + +func ExampleClient_timeseries_query_multi() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "rg:2", "rg:3", "rg:4") + // REMOVE_END + + // STEP_START query_multi + // Create three new "rg:" time series (two in the US + // and one in the UK, with different units) and add some + // data points. + res20, err := rdb.TSCreateWithArgs(ctx, "rg:2", &redis.TSOptions{ + Labels: map[string]string{"location": "us", "unit": "cm"}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res20) // >>> OK + + res21, err := rdb.TSCreateWithArgs(ctx, "rg:3", &redis.TSOptions{ + Labels: map[string]string{"location": "us", "unit": "in"}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res21) // >>> OK + + res22, err := rdb.TSCreateWithArgs(ctx, "rg:4", &redis.TSOptions{ + Labels: map[string]string{"location": "uk", "unit": "mm"}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res22) // >>> OK + + res23, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"rg:2", 0, 1.8}, + {"rg:3", 0, 0.9}, + {"rg:4", 0, 25}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res23) // >>> [0 0 0] + + res24, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"rg:2", 1, 2.1}, + {"rg:3", 1, 0.77}, + {"rg:4", 1, 18}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res24) // >>> [1 1 1] + + res25, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"rg:2", 2, 2.3}, + {"rg:3", 2, 1.1}, + {"rg:4", 2, 21}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res25) // >>> [2 2 2] + + res26, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"rg:2", 3, 1.9}, + {"rg:3", 3, 0.81}, + {"rg:4", 3, 19}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res26) // >>> [3 3 3] + + res27, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"rg:2", 4, 1.78}, + {"rg:3", 4, 0.74}, + {"rg:4", 4, 23}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res27) // >>> [4 4 4] + + // Retrieve the last data point from each US time series. + res28, err := rdb.TSMGet(ctx, []string{"location=us"}).Result() + if err != nil { + panic(err) + } + + res28Keys := slices.Collect(maps.Keys(res28)) + sort.Strings(res28Keys) + + for _, k := range res28Keys { + labels := res28[k][0].(map[interface{}]interface{}) + + labelKeys := make([]string, 0, len(labels)) + + for lk := range labels { + labelKeys = append(labelKeys, lk.(string)) + } + + sort.Strings(labelKeys) + + fmt.Printf("%v:\n", k) + + for _, lk := range labelKeys { + fmt.Printf(" %v: %v\n", lk, labels[lk]) + } + + fmt.Printf(" %v\n", res28[k][1]) + } + // >>> rg:2: + // >>> {4 1.78} + // >>> rg:3: + // >>> {4 0.74} + + // Retrieve the same data points, but include the `unit` + // label in the results. + res29, err := rdb.TSMGetWithArgs( + ctx, + []string{"location=us"}, + &redis.TSMGetOptions{ + SelectedLabels: []interface{}{"unit"}, + }, + ).Result() + if err != nil { + panic(err) + } + + res29Keys := slices.Collect(maps.Keys(res29)) + sort.Strings(res29Keys) + + for _, k := range res29Keys { + labels := res29[k][0].(map[interface{}]interface{}) + + labelKeys := make([]string, 0, len(labels)) + + for lk := range labels { + labelKeys = append(labelKeys, lk.(string)) + } + + sort.Strings(labelKeys) + + fmt.Printf("%v:\n", k) + + for _, lk := range labelKeys { + fmt.Printf(" %v: %v\n", lk, labels[lk]) + } + + fmt.Printf(" %v\n", res29[k][1]) + } + + // >>> rg:2: + // >>> unit: cm + // >>> [4 1.78] + // >>> rg:3: + // >>> unit: in + // >>> [4 0.74] + + // Retrieve data points up to time 2 (inclusive) from all + // time series that use millimeters as the unit. Include all + // labels in the results. + // Note that the `aggregators` field is empty if you don't + // specify any aggregators. + res30, err := rdb.TSMRangeWithArgs( + ctx, + 0, + 2, + []string{"unit=mm"}, + &redis.TSMRangeOptions{ + WithLabels: true, + }, + ).Result() + if err != nil { + panic(err) + } + + res30Keys := slices.Collect(maps.Keys(res30)) + sort.Strings(res30Keys) + + for _, k := range res30Keys { + labels := res30[k][0].(map[interface{}]interface{}) + labelKeys := make([]string, 0, len(labels)) + + for lk := range labels { + labelKeys = append(labelKeys, lk.(string)) + } + + sort.Strings(labelKeys) + + fmt.Printf("%v:\n", k) + + for _, lk := range labelKeys { + fmt.Printf(" %v: %v\n", lk, labels[lk]) + } + + fmt.Printf(" Aggregators: %v\n", res30[k][1]) + fmt.Printf(" %v\n", res30[k][2]) + } + // >>> rg:4: + // >>> location: uk + // >>> unit: mm + // >>> Aggregators: map[aggregators:[]] + // >>> [{0 25} {1 18} {2 21}] + + // Retrieve data points from time 1 to time 3 (inclusive) from + // all time series that use centimeters or millimeters as the unit, + // but only return the `location` label. Return the results + // in descending order of timestamp. + res31, err := rdb.TSMRevRangeWithArgs( + ctx, + 1, + 3, + []string{"unit=(cm,mm)"}, + &redis.TSMRevRangeOptions{ + SelectedLabels: []interface{}{"location"}, + }, + ).Result() + if err != nil { + panic(err) + } + + res31Keys := slices.Collect(maps.Keys(res31)) + sort.Strings(res31Keys) + + for _, k := range res31Keys { + labels := res31[k][0].(map[interface{}]interface{}) + labelKeys := make([]string, 0, len(labels)) + + for lk := range labels { + labelKeys = append(labelKeys, lk.(string)) + } + + sort.Strings(labelKeys) + + fmt.Printf("%v:\n", k) + + for _, lk := range labelKeys { + fmt.Printf(" %v: %v\n", lk, labels[lk]) + } + + fmt.Printf(" Aggregators: %v\n", res31[k][1]) + fmt.Printf(" %v\n", res31[k][2]) + } + // >>> rg:2: + // >>> location: us + // >>> Aggregators: map[aggregators:[]] + // >>> [{3 1.9} {2 2.3} {1 2.1}] + // >>> rg:4: + // >>> location: uk + // >>> Aggregators: map[aggregators:[]] + // >>> [{3 19} {2 21} {1 18}] + // STEP_END + + // Output: + // OK + // OK + // OK + // [0 0 0] + // [1 1 1] + // [2 2 2] + // [3 3 3] + // [4 4 4] + // rg:2: + // [4 1.78] + // rg:3: + // [4 0.74] + // rg:2: + // unit: cm + // [4 1.78] + // rg:3: + // unit: in + // [4 0.74] + // rg:4: + // location: uk + // unit: mm + // Aggregators: map[aggregators:[]] + // [[0 25] [1 18] [2 21]] + // rg:2: + // location: us + // Aggregators: map[aggregators:[]] + // [[3 1.9] [2 2.3] [1 2.1]] + // rg:4: + // location: uk + // Aggregators: map[aggregators:[]] + // [[3 19] [2 21] [1 18]] +} + +func ExampleClient_timeseries_aggregation() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "rg:2") + // REMOVE_END + + // Setup data for aggregation example + _, err := rdb.TSCreateWithArgs(ctx, "rg:2", &redis.TSOptions{ + Labels: map[string]string{"location": "us", "unit": "cm"}, + }).Result() + if err != nil { + panic(err) + } + + _, err = rdb.TSMAdd(ctx, [][]interface{}{ + {"rg:2", 0, 1.8}, + {"rg:2", 1, 2.1}, + {"rg:2", 2, 2.3}, + {"rg:2", 3, 1.9}, + {"rg:2", 4, 1.78}, + }).Result() + if err != nil { + panic(err) + } + + // STEP_START agg + res32, err := rdb.TSRangeWithArgs( + ctx, + "rg:2", + 0, + math.MaxInt64, + &redis.TSRangeOptions{ + Aggregator: redis.Avg, + BucketDuration: 2, + }, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res32) + // >>> [{0 1.9500000000000002} {2 2.0999999999999996} {4 1.78}] + // STEP_END + + // Output: + // [{0 1.9500000000000002} {2 2.0999999999999996} {4 1.78}] +} +func ExampleClient_timeseries_agg_bucket() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "sensor3") + // REMOVE_END + + // STEP_START agg_bucket + res1, err := rdb.TSCreate(ctx, "sensor3").Result() + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> OK + + res2, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"sensor3", 10, 1000}, + {"sensor3", 20, 2000}, + {"sensor3", 30, 3000}, + {"sensor3", 40, 4000}, + {"sensor3", 50, 5000}, + {"sensor3", 60, 6000}, + {"sensor3", 70, 7000}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [10 20 30 40 50 60 70] + + res3, err := rdb.TSRangeWithArgs( + ctx, + "sensor3", + 10, + 70, + &redis.TSRangeOptions{ + Aggregator: redis.Min, + BucketDuration: 25, + }, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [{0 1000} {25 3000} {50 5000}] + // STEP_END + + // STEP_START agg_align + res4, err := rdb.TSRangeWithArgs( + ctx, + "sensor3", + 10, + 70, + &redis.TSRangeOptions{ + Aggregator: redis.Min, + BucketDuration: 25, + Align: "START", + }, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [{10 1000} {35 4000} {60 6000}] + // STEP_END + + // Output: + // OK + // [10 20 30 40 50 60 70] + // [{0 1000} {25 3000} {50 5000}] + // [{10 1000} {35 4000} {60 6000}] +} + +func ExampleClient_timeseries_aggmulti() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "wind:1", "wind:2", "wind:3", "wind:4") + // REMOVE_END + + // STEP_START agg_multi + res37, err := rdb.TSCreateWithArgs(ctx, "wind:1", &redis.TSOptions{ + Labels: map[string]string{"country": "uk"}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res37) // >>> OK + + res38, err := rdb.TSCreateWithArgs(ctx, "wind:2", &redis.TSOptions{ + Labels: map[string]string{"country": "uk"}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res38) // >>> OK + + res39, err := rdb.TSCreateWithArgs(ctx, "wind:3", &redis.TSOptions{ + Labels: map[string]string{"country": "us"}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res39) // >>> OK + + res40, err := rdb.TSCreateWithArgs(ctx, "wind:4", &redis.TSOptions{ + Labels: map[string]string{"country": "us"}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res40) // >>> OK + + res41, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"wind:1", 1, 12}, + {"wind:2", 1, 18}, + {"wind:3", 1, 5}, + {"wind:4", 1, 20}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res41) // >>> [1 1 1 1] + + res42, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"wind:1", 2, 14}, + {"wind:2", 2, 21}, + {"wind:3", 2, 4}, + {"wind:4", 2, 25}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res42) // >>> [2 2 2 2] + + res43, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"wind:1", 3, 10}, + {"wind:2", 3, 24}, + {"wind:3", 3, 8}, + {"wind:4", 3, 18}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res43) // >>> [3 3 3 3] + + // The result pairs contain the timestamp and the maximum sample value + // for the country at that timestamp. + res44, err := rdb.TSMRangeWithArgs( + ctx, + 0, + math.MaxInt64, + []string{"country=(us,uk)"}, + &redis.TSMRangeOptions{ + GroupByLabel: "country", + Reducer: "max", + }, + ).Result() + if err != nil { + panic(err) + } + + res44Keys := slices.Collect(maps.Keys(res44)) + sort.Strings(res44Keys) + + for _, k := range res44Keys { + labels := res44[k][0].(map[interface{}]interface{}) + labelKeys := make([]string, 0, len(labels)) + + for lk := range labels { + labelKeys = append(labelKeys, lk.(string)) + } + + sort.Strings(labelKeys) + + fmt.Printf("%v:\n", k) + + for _, lk := range labelKeys { + fmt.Printf(" %v: %v\n", lk, labels[lk]) + } + + fmt.Printf(" %v\n", res44[k][1]) + fmt.Printf(" %v\n", res44[k][2]) + fmt.Printf(" %v\n", res44[k][3]) + } + // >>> country=uk: + // >>> map[reducers:[max]] + // >>> map[sources:[wind:1 wind:2]] + // >>> [[1 18] [2 21] [3 24]] + // >>> country=us: + // >>> map[reducers:[max]] + // >>> map[sources:[wind:3 wind:4]] + // >>> [[1 20] [2 25] [3 18]] + + // The result pairs contain the timestamp and the average sample value + // for the country at that timestamp. + res45, err := rdb.TSMRangeWithArgs( + ctx, + 0, + math.MaxInt64, + []string{"country=(us,uk)"}, + &redis.TSMRangeOptions{ + GroupByLabel: "country", + Reducer: "avg", + }, + ).Result() + if err != nil { + panic(err) + } + + res45Keys := slices.Collect(maps.Keys(res45)) + sort.Strings(res45Keys) + + for _, k := range res45Keys { + labels := res45[k][0].(map[interface{}]interface{}) + labelKeys := make([]string, 0, len(labels)) + + for lk := range labels { + labelKeys = append(labelKeys, lk.(string)) + } + + sort.Strings(labelKeys) + + fmt.Printf("%v:\n", k) + + for _, lk := range labelKeys { + fmt.Printf(" %v: %v\n", lk, labels[lk]) + } + + fmt.Printf(" %v\n", res45[k][1]) + fmt.Printf(" %v\n", res45[k][2]) + fmt.Printf(" %v\n", res45[k][3]) + } + // >>> country=uk: + // >>> map[reducers:[avg]] + // >>> map[sources:[wind:1 wind:2]] + // >>> [[1 15] [2 17.5] [3 17]] + // >>> country=us: + // >>> map[reducers:[avg]] + // >>> map[sources:[wind:3 wind:4]] + // >>> [[1 12.5] [2 14.5] [3 13]] + // STEP_END + + // Output: + // OK + // OK + // OK + // OK + // [1 1 1 1] + // [2 2 2 2] + // [3 3 3 3] + // country=uk: + // map[reducers:[max]] + // map[sources:[wind:1 wind:2]] + // [[1 18] [2 21] [3 24]] + // country=us: + // map[reducers:[max]] + // map[sources:[wind:3 wind:4]] + // [[1 20] [2 25] [3 18]] + // country=uk: + // map[reducers:[avg]] + // map[sources:[wind:1 wind:2]] + // [[1 15] [2 17.5] [3 17]] + // country=us: + // map[reducers:[avg]] + // map[sources:[wind:3 wind:4]] + // [[1 12.5] [2 14.5] [3 13]] +} + +func ExampleClient_timeseries_compaction() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "hyg:1", "hyg:compacted") + // REMOVE_END + + // STEP_START create_compaction + res45, err := rdb.TSCreate(ctx, "hyg:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res45) // >>> OK + + res46, err := rdb.TSCreate(ctx, "hyg:compacted").Result() + if err != nil { + panic(err) + } + + fmt.Println(res46) // >>> OK + + res47, err := rdb.TSCreateRule( + ctx, "hyg:1", "hyg:compacted", redis.Min, 3, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res47) // >>> OK + + res48, err := rdb.TSInfo(ctx, "hyg:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res48["rules"]) // >>> [[hyg:compacted 3 MIN 0]] + + res49, err := rdb.TSInfo(ctx, "hyg:compacted").Result() + if err != nil { + panic(err) + } + + fmt.Println(res49["sourceKey"]) // >>> hyg:1 + // STEP_END + + // STEP_START comp_add + res50, err := rdb.TSMAdd(ctx, [][]interface{}{ + {"hyg:1", 0, 75}, + {"hyg:1", 1, 77}, + {"hyg:1", 2, 78}, + }).Result() + if err != nil { + panic(err) + } + + fmt.Println(res50) // >>> [0 1 2] + + res51, err := rdb.TSRange( + ctx, "hyg:compacted", 0, math.MaxInt64, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res51) // >>> [] + + res52, err := rdb.TSAdd(ctx, "hyg:1", 3, 79).Result() + if err != nil { + panic(err) + } + + fmt.Println(res52) // >>> 3 + + res53, err := rdb.TSRange( + ctx, "hyg:compacted", 0, math.MaxInt64, + ).Result() + if err != nil { + panic(err) + } + + fmt.Println(res53) // >>> [{0 75}] + // STEP_END + + // Output: + // OK + // OK + // OK + // map[hyg:compacted:[3 MIN 0]] + // hyg:1 + // [0 1 2] + // [] + // 3 + // [{0 75}] +} + +func ExampleClient_timeseries_delete() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "thermometer:1") + // Setup initial data + rdb.TSCreate(ctx, "thermometer:1") + rdb.TSMAdd(ctx, [][]interface{}{ + {"thermometer:1", 1, 9.2}, + {"thermometer:1", 2, 9.9}, + }) + // REMOVE_END + + // STEP_START del + res54, err := rdb.TSInfo(ctx, "thermometer:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res54["totalSamples"]) // >>> 2 + fmt.Println(res54["firstTimestamp"]) // >>> 1 + fmt.Println(res54["lastTimestamp"]) // >>> 2 + + res55, err := rdb.TSAdd(ctx, "thermometer:1", 3, 9.7).Result() + if err != nil { + panic(err) + } + + fmt.Println(res55) // >>> 3 + + res56, err := rdb.TSInfo(ctx, "thermometer:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res56["totalSamples"]) // >>> 3 + fmt.Println(res56["firstTimestamp"]) // >>> 1 + fmt.Println(res56["lastTimestamp"]) // >>> 3 + + res57, err := rdb.TSDel(ctx, "thermometer:1", 1, 2).Result() + if err != nil { + panic(err) + } + + fmt.Println(res57) // >>> 2 + + res58, err := rdb.TSInfo(ctx, "thermometer:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res58["totalSamples"]) // >>> 1 + fmt.Println(res58["firstTimestamp"]) // >>> 3 + fmt.Println(res58["lastTimestamp"]) // >>> 3 + + res59, err := rdb.TSDel(ctx, "thermometer:1", 3, 3).Result() + if err != nil { + panic(err) + } + + fmt.Println(res59) // >>> 1 + + res60, err := rdb.TSInfo(ctx, "thermometer:1").Result() + if err != nil { + panic(err) + } + + fmt.Println(res60["totalSamples"]) // >>> 0 + // STEP_END + + // Output: + // 2 + // 1 + // 2 + // 3 + // 3 + // 1 + // 3 + // 2 + // 1 + // 3 + // 3 + // 1 + // 0 +}