redis/probabilistic.go

1434 lines
46 KiB
Go
Raw Normal View History

2023-08-15 10:19:19 +03:00
package redis
import (
"context"
"fmt"
"github.com/redis/go-redis/v9/internal/proto"
)
type probabilisticCmdable interface {
BFAdd(ctx context.Context, key string, element interface{}) *BoolCmd
BFCard(ctx context.Context, key string) *IntCmd
BFExists(ctx context.Context, key string, element interface{}) *BoolCmd
BFInfo(ctx context.Context, key string) *BFInfoCmd
BFInfoArg(ctx context.Context, key, option string) *BFInfoCmd
BFInfoCapacity(ctx context.Context, key string) *BFInfoCmd
BFInfoSize(ctx context.Context, key string) *BFInfoCmd
BFInfoFilters(ctx context.Context, key string) *BFInfoCmd
BFInfoItems(ctx context.Context, key string) *BFInfoCmd
BFInfoExpansion(ctx context.Context, key string) *BFInfoCmd
BFInsert(ctx context.Context, key string, options *BFInsertOptions, elements ...interface{}) *BoolSliceCmd
BFMAdd(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd
BFMExists(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd
BFReserve(ctx context.Context, key string, errorRate float64, capacity int64) *StatusCmd
BFReserveExpansion(ctx context.Context, key string, errorRate float64, capacity, expansion int64) *StatusCmd
BFReserveNonScaling(ctx context.Context, key string, errorRate float64, capacity int64) *StatusCmd
BFReserveArgs(ctx context.Context, key string, options *BFReserveOptions) *StatusCmd
BFScanDump(ctx context.Context, key string, iterator int64) *ScanDumpCmd
BFLoadChunk(ctx context.Context, key string, iterator int64, data interface{}) *StatusCmd
CFAdd(ctx context.Context, key string, element interface{}) *BoolCmd
CFAddNX(ctx context.Context, key string, element interface{}) *BoolCmd
CFCount(ctx context.Context, key string, element interface{}) *IntCmd
CFDel(ctx context.Context, key string, element interface{}) *BoolCmd
CFExists(ctx context.Context, key string, element interface{}) *BoolCmd
CFInfo(ctx context.Context, key string) *CFInfoCmd
CFInsert(ctx context.Context, key string, options *CFInsertOptions, elements ...interface{}) *BoolSliceCmd
CFInsertNX(ctx context.Context, key string, options *CFInsertOptions, elements ...interface{}) *IntSliceCmd
CFMExists(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd
CFReserve(ctx context.Context, key string, capacity int64) *StatusCmd
CFReserveArgs(ctx context.Context, key string, options *CFReserveOptions) *StatusCmd
CFReserveExpansion(ctx context.Context, key string, capacity int64, expansion int64) *StatusCmd
CFReserveBucketSize(ctx context.Context, key string, capacity int64, bucketsize int64) *StatusCmd
CFReserveMaxIterations(ctx context.Context, key string, capacity int64, maxiterations int64) *StatusCmd
CFScanDump(ctx context.Context, key string, iterator int64) *ScanDumpCmd
CFLoadChunk(ctx context.Context, key string, iterator int64, data interface{}) *StatusCmd
CMSIncrBy(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd
CMSInfo(ctx context.Context, key string) *CMSInfoCmd
CMSInitByDim(ctx context.Context, key string, width, height int64) *StatusCmd
CMSInitByProb(ctx context.Context, key string, errorRate, probability float64) *StatusCmd
CMSMerge(ctx context.Context, destKey string, sourceKeys ...string) *StatusCmd
CMSMergeWithWeight(ctx context.Context, destKey string, sourceKeys map[string]int64) *StatusCmd
CMSQuery(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd
TopKAdd(ctx context.Context, key string, elements ...interface{}) *StringSliceCmd
TopKCount(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd
TopKIncrBy(ctx context.Context, key string, elements ...interface{}) *StringSliceCmd
TopKInfo(ctx context.Context, key string) *TopKInfoCmd
TopKList(ctx context.Context, key string) *StringSliceCmd
TopKListWithCount(ctx context.Context, key string) *MapStringIntCmd
TopKQuery(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd
TopKReserve(ctx context.Context, key string, k int64) *StatusCmd
TopKReserveWithOptions(ctx context.Context, key string, k int64, width, depth int64, decay float64) *StatusCmd
TDigestAdd(ctx context.Context, key string, elements ...float64) *StatusCmd
TDigestByRank(ctx context.Context, key string, rank ...uint64) *FloatSliceCmd
TDigestByRevRank(ctx context.Context, key string, rank ...uint64) *FloatSliceCmd
TDigestCDF(ctx context.Context, key string, elements ...float64) *FloatSliceCmd
TDigestCreate(ctx context.Context, key string) *StatusCmd
TDigestCreateWithCompression(ctx context.Context, key string, compression int64) *StatusCmd
TDigestInfo(ctx context.Context, key string) *TDigestInfoCmd
TDigestMax(ctx context.Context, key string) *FloatCmd
TDigestMin(ctx context.Context, key string) *FloatCmd
TDigestMerge(ctx context.Context, destKey string, options *TDigestMergeOptions, sourceKeys ...string) *StatusCmd
TDigestQuantile(ctx context.Context, key string, elements ...float64) *FloatSliceCmd
TDigestRank(ctx context.Context, key string, values ...float64) *IntSliceCmd
TDigestReset(ctx context.Context, key string) *StatusCmd
TDigestRevRank(ctx context.Context, key string, values ...float64) *IntSliceCmd
TDigestTrimmedMean(ctx context.Context, key string, lowCutQuantile, highCutQuantile float64) *FloatCmd
}
type BFInsertOptions struct {
Capacity int64
Error float64
Expansion int64
NonScaling bool
NoCreate bool
}
type BFReserveOptions struct {
Capacity int64
Error float64
Expansion int64
NonScaling bool
}
type CFReserveOptions struct {
Capacity int64
BucketSize int64
MaxIterations int64
Expansion int64
}
type CFInsertOptions struct {
Capacity int64
NoCreate bool
}
// -------------------------------------------
// Bloom filter commands
//-------------------------------------------
// BFReserve creates an empty Bloom filter with a single sub-filter
// for the initial specified capacity and with an upper bound error_rate.
// For more information - https://redis.io/commands/bf.reserve/
func (c cmdable) BFReserve(ctx context.Context, key string, errorRate float64, capacity int64) *StatusCmd {
args := []interface{}{"BF.RESERVE", key, errorRate, capacity}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFReserveExpansion creates an empty Bloom filter with a single sub-filter
// for the initial specified capacity and with an upper bound error_rate.
// This function also allows for specifying an expansion rate for the filter.
// For more information - https://redis.io/commands/bf.reserve/
func (c cmdable) BFReserveExpansion(ctx context.Context, key string, errorRate float64, capacity, expansion int64) *StatusCmd {
args := []interface{}{"BF.RESERVE", key, errorRate, capacity, "EXPANSION", expansion}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFReserveNonScaling creates an empty Bloom filter with a single sub-filter
// for the initial specified capacity and with an upper bound error_rate.
// This function also allows for specifying that the filter should not scale.
// For more information - https://redis.io/commands/bf.reserve/
func (c cmdable) BFReserveNonScaling(ctx context.Context, key string, errorRate float64, capacity int64) *StatusCmd {
args := []interface{}{"BF.RESERVE", key, errorRate, capacity, "NONSCALING"}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFReserveArgs creates an empty Bloom filter with a single sub-filter
// for the initial specified capacity and with an upper bound error_rate.
// This function also allows for specifying additional options such as expansion rate and non-scaling behavior.
// For more information - https://redis.io/commands/bf.reserve/
func (c cmdable) BFReserveArgs(ctx context.Context, key string, options *BFReserveOptions) *StatusCmd {
args := []interface{}{"BF.RESERVE", key}
if options != nil {
if options.Error != 0 {
args = append(args, options.Error)
}
if options.Capacity != 0 {
args = append(args, options.Capacity)
}
if options.Expansion != 0 {
args = append(args, "EXPANSION", options.Expansion)
}
if options.NonScaling {
args = append(args, "NONSCALING")
}
}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFAdd adds an item to a Bloom filter.
// For more information - https://redis.io/commands/bf.add/
func (c cmdable) BFAdd(ctx context.Context, key string, element interface{}) *BoolCmd {
args := []interface{}{"BF.ADD", key, element}
cmd := NewBoolCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFCard returns the cardinality of a Bloom filter -
// number of items that were added to a Bloom filter and detected as unique
// (items that caused at least one bit to be set in at least one sub-filter).
// For more information - https://redis.io/commands/bf.card/
func (c cmdable) BFCard(ctx context.Context, key string) *IntCmd {
args := []interface{}{"BF.CARD", key}
cmd := NewIntCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFExists determines whether a given item was added to a Bloom filter.
// For more information - https://redis.io/commands/bf.exists/
func (c cmdable) BFExists(ctx context.Context, key string, element interface{}) *BoolCmd {
args := []interface{}{"BF.EXISTS", key, element}
cmd := NewBoolCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFLoadChunk restores a Bloom filter previously saved using BF.SCANDUMP.
// For more information - https://redis.io/commands/bf.loadchunk/
func (c cmdable) BFLoadChunk(ctx context.Context, key string, iterator int64, data interface{}) *StatusCmd {
args := []interface{}{"BF.LOADCHUNK", key, iterator, data}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// Begins an incremental save of the Bloom filter.
// This command is useful for large Bloom filters that cannot fit into the DUMP and RESTORE model.
// For more information - https://redis.io/commands/bf.scandump/
func (c cmdable) BFScanDump(ctx context.Context, key string, iterator int64) *ScanDumpCmd {
args := []interface{}{"BF.SCANDUMP", key, iterator}
cmd := newScanDumpCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
type ScanDump struct {
Iter int64
Data string
}
type ScanDumpCmd struct {
baseCmd
val ScanDump
}
func newScanDumpCmd(ctx context.Context, args ...interface{}) *ScanDumpCmd {
return &ScanDumpCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *ScanDumpCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *ScanDumpCmd) SetVal(val ScanDump) {
cmd.val = val
}
func (cmd *ScanDumpCmd) Result() (ScanDump, error) {
return cmd.val, cmd.err
}
func (cmd *ScanDumpCmd) Val() ScanDump {
return cmd.val
}
func (cmd *ScanDumpCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
cmd.val = ScanDump{}
for i := 0; i < n; i++ {
iter, err := rd.ReadInt()
if err != nil {
return err
}
data, err := rd.ReadString()
if err != nil {
return err
}
cmd.val.Data = data
cmd.val.Iter = iter
}
return nil
}
// Returns information about a Bloom filter.
// For more information - https://redis.io/commands/bf.info/
func (c cmdable) BFInfo(ctx context.Context, key string) *BFInfoCmd {
args := []interface{}{"BF.INFO", key}
cmd := NewBFInfoCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
type BFInfo struct {
Capacity int64
Size int64
Filters int64
ItemsInserted int64
ExpansionRate int64
}
type BFInfoCmd struct {
baseCmd
val BFInfo
}
func NewBFInfoCmd(ctx context.Context, args ...interface{}) *BFInfoCmd {
return &BFInfoCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *BFInfoCmd) SetVal(val BFInfo) {
cmd.val = val
}
func (cmd *BFInfoCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *BFInfoCmd) Val() BFInfo {
return cmd.val
}
func (cmd *BFInfoCmd) Result() (BFInfo, error) {
return cmd.val, cmd.err
}
func (cmd *BFInfoCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
var key string
var result BFInfo
for f := 0; f < n; f++ {
key, err = rd.ReadString()
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 err != nil {
return err
}
}
cmd.val = result
return nil
}
// BFInfoCapacity returns information about the capacity of a Bloom filter.
// For more information - https://redis.io/commands/bf.info/
func (c cmdable) BFInfoCapacity(ctx context.Context, key string) *BFInfoCmd {
return c.BFInfoArg(ctx, key, "CAPACITY")
}
// BFInfoSize returns information about the size of a Bloom filter.
// For more information - https://redis.io/commands/bf.info/
func (c cmdable) BFInfoSize(ctx context.Context, key string) *BFInfoCmd {
return c.BFInfoArg(ctx, key, "SIZE")
}
// BFInfoFilters returns information about the filters of a Bloom filter.
// For more information - https://redis.io/commands/bf.info/
func (c cmdable) BFInfoFilters(ctx context.Context, key string) *BFInfoCmd {
return c.BFInfoArg(ctx, key, "FILTERS")
}
// BFInfoItems returns information about the items of a Bloom filter.
// For more information - https://redis.io/commands/bf.info/
func (c cmdable) BFInfoItems(ctx context.Context, key string) *BFInfoCmd {
return c.BFInfoArg(ctx, key, "ITEMS")
}
// BFInfoExpansion returns information about the expansion rate of a Bloom filter.
// For more information - https://redis.io/commands/bf.info/
func (c cmdable) BFInfoExpansion(ctx context.Context, key string) *BFInfoCmd {
return c.BFInfoArg(ctx, key, "EXPANSION")
}
// BFInfoArg returns information about a specific option of a Bloom filter.
// For more information - https://redis.io/commands/bf.info/
func (c cmdable) BFInfoArg(ctx context.Context, key, option string) *BFInfoCmd {
args := []interface{}{"BF.INFO", key, option}
cmd := NewBFInfoCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFInsert inserts elements into a Bloom filter.
// This function also allows for specifying additional options such as:
// capacity, error rate, expansion rate, and non-scaling behavior.
// For more information - https://redis.io/commands/bf.insert/
func (c cmdable) BFInsert(ctx context.Context, key string, options *BFInsertOptions, elements ...interface{}) *BoolSliceCmd {
args := []interface{}{"BF.INSERT", key}
if options != nil {
if options.Capacity != 0 {
args = append(args, "CAPACITY", options.Capacity)
}
if options.Error != 0 {
args = append(args, "ERROR", options.Error)
}
if options.Expansion != 0 {
args = append(args, "EXPANSION", options.Expansion)
}
if options.NoCreate {
args = append(args, "NOCREATE")
}
if options.NonScaling {
args = append(args, "NONSCALING")
}
}
args = append(args, "ITEMS")
args = append(args, elements...)
cmd := NewBoolSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFMAdd adds multiple elements to a Bloom filter.
// Returns an array of booleans indicating whether each element was added to the filter or not.
// For more information - https://redis.io/commands/bf.madd/
func (c cmdable) BFMAdd(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd {
args := []interface{}{"BF.MADD", key}
args = append(args, elements...)
cmd := NewBoolSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// BFMExists check if multiple elements exist in a Bloom filter.
// Returns an array of booleans indicating whether each element exists in the filter or not.
// For more information - https://redis.io/commands/bf.mexists/
func (c cmdable) BFMExists(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd {
args := []interface{}{"BF.MEXISTS", key}
args = append(args, elements...)
cmd := NewBoolSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// -------------------------------------------
// Cuckoo filter commands
//-------------------------------------------
// CFReserve creates an empty Cuckoo filter with the specified capacity.
// For more information - https://redis.io/commands/cf.reserve/
func (c cmdable) CFReserve(ctx context.Context, key string, capacity int64) *StatusCmd {
args := []interface{}{"CF.RESERVE", key, capacity}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFReserveExpansion creates an empty Cuckoo filter with the specified capacity and expansion rate.
// For more information - https://redis.io/commands/cf.reserve/
func (c cmdable) CFReserveExpansion(ctx context.Context, key string, capacity int64, expansion int64) *StatusCmd {
args := []interface{}{"CF.RESERVE", key, capacity, "EXPANSION", expansion}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFReserveBucketSize creates an empty Cuckoo filter with the specified capacity and bucket size.
// For more information - https://redis.io/commands/cf.reserve/
func (c cmdable) CFReserveBucketSize(ctx context.Context, key string, capacity int64, bucketsize int64) *StatusCmd {
args := []interface{}{"CF.RESERVE", key, capacity, "BUCKETSIZE", bucketsize}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFReserveMaxIterations creates an empty Cuckoo filter with the specified capacity and maximum number of iterations.
// For more information - https://redis.io/commands/cf.reserve/
func (c cmdable) CFReserveMaxIterations(ctx context.Context, key string, capacity int64, maxiterations int64) *StatusCmd {
args := []interface{}{"CF.RESERVE", key, capacity, "MAXITERATIONS", maxiterations}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFReserveArgs creates an empty Cuckoo filter with the specified options.
// This function allows for specifying additional options such as bucket size and maximum number of iterations.
// For more information - https://redis.io/commands/cf.reserve/
func (c cmdable) CFReserveArgs(ctx context.Context, key string, options *CFReserveOptions) *StatusCmd {
args := []interface{}{"CF.RESERVE", key, options.Capacity}
if options.BucketSize != 0 {
args = append(args, "BUCKETSIZE", options.BucketSize)
}
if options.MaxIterations != 0 {
args = append(args, "MAXITERATIONS", options.MaxIterations)
}
if options.Expansion != 0 {
args = append(args, "EXPANSION", options.Expansion)
}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFAdd adds an element to a Cuckoo filter.
// Returns true if the element was added to the filter or false if it already exists in the filter.
// For more information - https://redis.io/commands/cf.add/
func (c cmdable) CFAdd(ctx context.Context, key string, element interface{}) *BoolCmd {
args := []interface{}{"CF.ADD", key, element}
cmd := NewBoolCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFAddNX adds an element to a Cuckoo filter only if it does not already exist in the filter.
// Returns true if the element was added to the filter or false if it already exists in the filter.
// For more information - https://redis.io/commands/cf.addnx/
func (c cmdable) CFAddNX(ctx context.Context, key string, element interface{}) *BoolCmd {
args := []interface{}{"CF.ADDNX", key, element}
cmd := NewBoolCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFCount returns an estimate of the number of times an element may be in a Cuckoo Filter.
// For more information - https://redis.io/commands/cf.count/
func (c cmdable) CFCount(ctx context.Context, key string, element interface{}) *IntCmd {
args := []interface{}{"CF.COUNT", key, element}
cmd := NewIntCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFDel deletes an item once from the cuckoo filter.
// For more information - https://redis.io/commands/cf.del/
func (c cmdable) CFDel(ctx context.Context, key string, element interface{}) *BoolCmd {
args := []interface{}{"CF.DEL", key, element}
cmd := NewBoolCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFExists determines whether an item may exist in the Cuckoo Filter or not.
// For more information - https://redis.io/commands/cf.exists/
func (c cmdable) CFExists(ctx context.Context, key string, element interface{}) *BoolCmd {
args := []interface{}{"CF.EXISTS", key, element}
cmd := NewBoolCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFLoadChunk restores a filter previously saved using SCANDUMP.
// For more information - https://redis.io/commands/cf.loadchunk/
func (c cmdable) CFLoadChunk(ctx context.Context, key string, iterator int64, data interface{}) *StatusCmd {
args := []interface{}{"CF.LOADCHUNK", key, iterator, data}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFScanDump begins an incremental save of the cuckoo filter.
// For more information - https://redis.io/commands/cf.scandump/
func (c cmdable) CFScanDump(ctx context.Context, key string, iterator int64) *ScanDumpCmd {
args := []interface{}{"CF.SCANDUMP", key, iterator}
cmd := newScanDumpCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
type CFInfo struct {
Size int64
NumBuckets int64
NumFilters int64
NumItemsInserted int64
NumItemsDeleted int64
BucketSize int64
ExpansionRate int64
MaxIteration int64
}
type CFInfoCmd struct {
baseCmd
val CFInfo
}
func NewCFInfoCmd(ctx context.Context, args ...interface{}) *CFInfoCmd {
return &CFInfoCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *CFInfoCmd) SetVal(val CFInfo) {
cmd.val = val
}
func (cmd *CFInfoCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *CFInfoCmd) Val() CFInfo {
return cmd.val
}
func (cmd *CFInfoCmd) Result() (CFInfo, error) {
return cmd.val, cmd.err
}
func (cmd *CFInfoCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
var key string
var result CFInfo
for f := 0; f < n; f++ {
key, err = rd.ReadString()
if err != nil {
return err
}
switch key {
case "Size":
result.Size, err = rd.ReadInt()
case "Number of buckets":
result.NumBuckets, err = rd.ReadInt()
case "Number of filters":
result.NumFilters, err = rd.ReadInt()
case "Number of items inserted":
result.NumItemsInserted, err = rd.ReadInt()
case "Number of items deleted":
result.NumItemsDeleted, err = rd.ReadInt()
case "Bucket size":
result.BucketSize, err = rd.ReadInt()
case "Expansion rate":
result.ExpansionRate, err = rd.ReadInt()
case "Max iterations":
result.MaxIteration, err = rd.ReadInt()
default:
return fmt.Errorf("redis: CF.INFO unexpected key %s", key)
}
if err != nil {
return err
}
}
cmd.val = result
return nil
}
// CFInfo returns information about a Cuckoo filter.
// For more information - https://redis.io/commands/cf.info/
func (c cmdable) CFInfo(ctx context.Context, key string) *CFInfoCmd {
args := []interface{}{"CF.INFO", key}
cmd := NewCFInfoCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFInsert inserts elements into a Cuckoo filter.
// This function also allows for specifying additional options such as capacity, error rate, expansion rate, and non-scaling behavior.
// Returns an array of booleans indicating whether each element was added to the filter or not.
// For more information - https://redis.io/commands/cf.insert/
func (c cmdable) CFInsert(ctx context.Context, key string, options *CFInsertOptions, elements ...interface{}) *BoolSliceCmd {
args := []interface{}{"CF.INSERT", key}
args = c.getCfInsertArgs(args, options, elements...)
cmd := NewBoolSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CFInsertNX inserts elements into a Cuckoo filter only if they do not already exist in the filter.
// This function also allows for specifying additional options such as:
// capacity, error rate, expansion rate, and non-scaling behavior.
// Returns an array of integers indicating whether each element was added to the filter or not.
// For more information - https://redis.io/commands/cf.insertnx/
func (c cmdable) CFInsertNX(ctx context.Context, key string, options *CFInsertOptions, elements ...interface{}) *IntSliceCmd {
args := []interface{}{"CF.INSERTNX", key}
args = c.getCfInsertArgs(args, options, elements...)
cmd := NewIntSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
func (c cmdable) getCfInsertArgs(args []interface{}, options *CFInsertOptions, elements ...interface{}) []interface{} {
if options != nil {
if options.Capacity != 0 {
args = append(args, "CAPACITY", options.Capacity)
}
if options.NoCreate {
args = append(args, "NOCREATE")
}
}
args = append(args, "ITEMS")
args = append(args, elements...)
return args
}
// CFMExists check if multiple elements exist in a Cuckoo filter.
// Returns an array of booleans indicating whether each element exists in the filter or not.
// For more information - https://redis.io/commands/cf.mexists/
func (c cmdable) CFMExists(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd {
args := []interface{}{"CF.MEXISTS", key}
args = append(args, elements...)
cmd := NewBoolSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// -------------------------------------------
// CMS commands
//-------------------------------------------
// CMSIncrBy increments the count of one or more items in a Count-Min Sketch filter.
// Returns an array of integers representing the updated count of each item.
// For more information - https://redis.io/commands/cms.incrby/
func (c cmdable) CMSIncrBy(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd {
args := make([]interface{}, 2, 2+len(elements))
args[0] = "CMS.INCRBY"
args[1] = key
args = appendArgs(args, elements)
cmd := NewIntSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
type CMSInfo struct {
Width int64
Depth int64
Count int64
}
type CMSInfoCmd struct {
baseCmd
val CMSInfo
}
func NewCMSInfoCmd(ctx context.Context, args ...interface{}) *CMSInfoCmd {
return &CMSInfoCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *CMSInfoCmd) SetVal(val CMSInfo) {
cmd.val = val
}
func (cmd *CMSInfoCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *CMSInfoCmd) Val() CMSInfo {
return cmd.val
}
func (cmd *CMSInfoCmd) Result() (CMSInfo, error) {
return cmd.val, cmd.err
}
func (cmd *CMSInfoCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
var key string
var result CMSInfo
for f := 0; f < n; f++ {
key, err = rd.ReadString()
if err != nil {
return err
}
switch key {
case "width":
result.Width, err = rd.ReadInt()
case "depth":
result.Depth, err = rd.ReadInt()
case "count":
result.Count, err = rd.ReadInt()
default:
return fmt.Errorf("redis: CMS.INFO unexpected key %s", key)
}
if err != nil {
return err
}
}
cmd.val = result
return nil
}
// CMSInfo returns information about a Count-Min Sketch filter.
// For more information - https://redis.io/commands/cms.info/
func (c cmdable) CMSInfo(ctx context.Context, key string) *CMSInfoCmd {
args := []interface{}{"CMS.INFO", key}
cmd := NewCMSInfoCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CMSInitByDim creates an empty Count-Min Sketch filter with the specified dimensions.
// For more information - https://redis.io/commands/cms.initbydim/
func (c cmdable) CMSInitByDim(ctx context.Context, key string, width, depth int64) *StatusCmd {
args := []interface{}{"CMS.INITBYDIM", key, width, depth}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CMSInitByProb creates an empty Count-Min Sketch filter with the specified error rate and probability.
// For more information - https://redis.io/commands/cms.initbyprob/
func (c cmdable) CMSInitByProb(ctx context.Context, key string, errorRate, probability float64) *StatusCmd {
args := []interface{}{"CMS.INITBYPROB", key, errorRate, probability}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CMSMerge merges multiple Count-Min Sketch filters into a single filter.
// The destination filter must not exist and will be created with the dimensions of the first source filter.
// The number of items in each source filter must be equal.
// Returns OK on success or an error if the filters could not be merged.
// For more information - https://redis.io/commands/cms.merge/
func (c cmdable) CMSMerge(ctx context.Context, destKey string, sourceKeys ...string) *StatusCmd {
args := []interface{}{"CMS.MERGE", destKey, len(sourceKeys)}
for _, s := range sourceKeys {
args = append(args, s)
}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CMSMergeWithWeight merges multiple Count-Min Sketch filters into a single filter with weights for each source filter.
// The destination filter must not exist and will be created with the dimensions of the first source filter.
// The number of items in each source filter must be equal.
// Returns OK on success or an error if the filters could not be merged.
// For more information - https://redis.io/commands/cms.merge/
func (c cmdable) CMSMergeWithWeight(ctx context.Context, destKey string, sourceKeys map[string]int64) *StatusCmd {
args := make([]interface{}, 0, 4+(len(sourceKeys)*2+1))
args = append(args, "CMS.MERGE", destKey, len(sourceKeys))
if len(sourceKeys) > 0 {
sk := make([]interface{}, len(sourceKeys))
sw := make([]interface{}, len(sourceKeys))
i := 0
for k, w := range sourceKeys {
sk[i] = k
sw[i] = w
i++
}
args = append(args, sk...)
args = append(args, "WEIGHTS")
args = append(args, sw...)
}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// CMSQuery returns count for item(s).
// For more information - https://redis.io/commands/cms.query/
func (c cmdable) CMSQuery(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd {
args := []interface{}{"CMS.QUERY", key}
args = append(args, elements...)
cmd := NewIntSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// -------------------------------------------
// TopK commands
//--------------------------------------------
// TopKAdd adds one or more elements to a Top-K filter.
// Returns an array of strings representing the items that were removed from the filter, if any.
// For more information - https://redis.io/commands/topk.add/
func (c cmdable) TopKAdd(ctx context.Context, key string, elements ...interface{}) *StringSliceCmd {
args := make([]interface{}, 2, 2+len(elements))
args[0] = "TOPK.ADD"
args[1] = key
args = appendArgs(args, elements)
cmd := NewStringSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TopKReserve creates an empty Top-K filter with the specified number of top items to keep.
// For more information - https://redis.io/commands/topk.reserve/
func (c cmdable) TopKReserve(ctx context.Context, key string, k int64) *StatusCmd {
args := []interface{}{"TOPK.RESERVE", key, k}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TopKReserveWithOptions creates an empty Top-K filter with the specified number of top items to keep and additional options.
// This function allows for specifying additional options such as width, depth and decay.
// For more information - https://redis.io/commands/topk.reserve/
func (c cmdable) TopKReserveWithOptions(ctx context.Context, key string, k int64, width, depth int64, decay float64) *StatusCmd {
args := []interface{}{"TOPK.RESERVE", key, k, width, depth, decay}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
type TopKInfo struct {
K int64
Width int64
Depth int64
Decay float64
}
type TopKInfoCmd struct {
baseCmd
val TopKInfo
}
func NewTopKInfoCmd(ctx context.Context, args ...interface{}) *TopKInfoCmd {
return &TopKInfoCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *TopKInfoCmd) SetVal(val TopKInfo) {
cmd.val = val
}
func (cmd *TopKInfoCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *TopKInfoCmd) Val() TopKInfo {
return cmd.val
}
func (cmd *TopKInfoCmd) Result() (TopKInfo, error) {
return cmd.val, cmd.err
}
func (cmd *TopKInfoCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
var key string
var result TopKInfo
for f := 0; f < n; f++ {
key, err = rd.ReadString()
if err != nil {
return err
}
switch key {
case "k":
result.K, err = rd.ReadInt()
case "width":
result.Width, err = rd.ReadInt()
case "depth":
result.Depth, err = rd.ReadInt()
case "decay":
result.Decay, err = rd.ReadFloat()
default:
return fmt.Errorf("redis: topk.info unexpected key %s", key)
}
if err != nil {
return err
}
}
cmd.val = result
return nil
}
// TopKInfo returns information about a Top-K filter.
// For more information - https://redis.io/commands/topk.info/
func (c cmdable) TopKInfo(ctx context.Context, key string) *TopKInfoCmd {
args := []interface{}{"TOPK.INFO", key}
cmd := NewTopKInfoCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TopKQuery check if multiple elements exist in a Top-K filter.
// Returns an array of booleans indicating whether each element exists in the filter or not.
// For more information - https://redis.io/commands/topk.query/
func (c cmdable) TopKQuery(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd {
args := make([]interface{}, 2, 2+len(elements))
args[0] = "TOPK.QUERY"
args[1] = key
args = appendArgs(args, elements)
cmd := NewBoolSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TopKCount returns an estimate of the number of times an item may be in a Top-K filter.
// For more information - https://redis.io/commands/topk.count/
func (c cmdable) TopKCount(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd {
args := make([]interface{}, 2, 2+len(elements))
args[0] = "TOPK.COUNT"
args[1] = key
args = appendArgs(args, elements)
cmd := NewIntSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TopKIncrBy increases the count of one or more items in a Top-K filter.
// For more information - https://redis.io/commands/topk.incrby/
func (c cmdable) TopKIncrBy(ctx context.Context, key string, elements ...interface{}) *StringSliceCmd {
args := make([]interface{}, 2, 2+len(elements))
args[0] = "TOPK.INCRBY"
args[1] = key
args = appendArgs(args, elements)
cmd := NewStringSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TopKList returns all items in Top-K list.
// For more information - https://redis.io/commands/topk.list/
func (c cmdable) TopKList(ctx context.Context, key string) *StringSliceCmd {
args := []interface{}{"TOPK.LIST", key}
cmd := NewStringSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TopKListWithCount returns all items in Top-K list with their respective count.
// For more information - https://redis.io/commands/topk.list/
func (c cmdable) TopKListWithCount(ctx context.Context, key string) *MapStringIntCmd {
args := []interface{}{"TOPK.LIST", key, "WITHCOUNT"}
cmd := NewMapStringIntCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// -------------------------------------------
// t-digest commands
// --------------------------------------------
// TDigestAdd adds one or more elements to a t-Digest data structure.
// Returns OK on success or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.add/
func (c cmdable) TDigestAdd(ctx context.Context, key string, elements ...float64) *StatusCmd {
args := make([]interface{}, 2, 2+len(elements))
args[0] = "TDIGEST.ADD"
args[1] = key
// Convert floatSlice to []interface{}
interfaceSlice := make([]interface{}, len(elements))
for i, v := range elements {
interfaceSlice[i] = v
}
args = append(args, interfaceSlice...)
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestByRank returns an array of values from a t-Digest data structure based on their rank.
// The rank of an element is its position in the sorted list of all elements in the t-Digest.
// Returns an array of floats representing the values at the specified ranks or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.byrank/
func (c cmdable) TDigestByRank(ctx context.Context, key string, rank ...uint64) *FloatSliceCmd {
args := make([]interface{}, 2, 2+len(rank))
args[0] = "TDIGEST.BYRANK"
args[1] = key
// Convert uint slice to []interface{}
interfaceSlice := make([]interface{}, len(rank))
for i, v := range rank {
interfaceSlice[i] = v
}
args = append(args, interfaceSlice...)
cmd := NewFloatSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestByRevRank returns an array of values from a t-Digest data structure based on their reverse rank.
// The reverse rank of an element is its position in the sorted list of all elements in the t-Digest when sorted in descending order.
// Returns an array of floats representing the values at the specified ranks or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.byrevrank/
func (c cmdable) TDigestByRevRank(ctx context.Context, key string, rank ...uint64) *FloatSliceCmd {
args := make([]interface{}, 2, 2+len(rank))
args[0] = "TDIGEST.BYREVRANK"
args[1] = key
// Convert uint slice to []interface{}
interfaceSlice := make([]interface{}, len(rank))
for i, v := range rank {
interfaceSlice[i] = v
}
args = append(args, interfaceSlice...)
cmd := NewFloatSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestCDF returns an array of cumulative distribution function (CDF) values for one or more elements in a t-Digest data structure.
// The CDF value for an element is the fraction of all elements in the t-Digest that are less than or equal to it.
// Returns an array of floats representing the CDF values for each element or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.cdf/
func (c cmdable) TDigestCDF(ctx context.Context, key string, elements ...float64) *FloatSliceCmd {
args := make([]interface{}, 2, 2+len(elements))
args[0] = "TDIGEST.CDF"
args[1] = key
// Convert floatSlice to []interface{}
interfaceSlice := make([]interface{}, len(elements))
for i, v := range elements {
interfaceSlice[i] = v
}
args = append(args, interfaceSlice...)
cmd := NewFloatSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestCreate creates an empty t-Digest data structure with default parameters.
// Returns OK on success or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.create/
func (c cmdable) TDigestCreate(ctx context.Context, key string) *StatusCmd {
args := []interface{}{"TDIGEST.CREATE", key}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestCreateWithCompression creates an empty t-Digest data structure with a specified compression parameter.
// The compression parameter controls the accuracy and memory usage of the t-Digest.
// Returns OK on success or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.create/
func (c cmdable) TDigestCreateWithCompression(ctx context.Context, key string, compression int64) *StatusCmd {
args := []interface{}{"TDIGEST.CREATE", key, "COMPRESSION", compression}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
type TDigestInfo struct {
Compression int64
Capacity int64
MergedNodes int64
UnmergedNodes int64
MergedWeight int64
UnmergedWeight int64
Observations int64
TotalCompressions int64
MemoryUsage int64
}
type TDigestInfoCmd struct {
baseCmd
val TDigestInfo
}
func NewTDigestInfoCmd(ctx context.Context, args ...interface{}) *TDigestInfoCmd {
return &TDigestInfoCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *TDigestInfoCmd) SetVal(val TDigestInfo) {
cmd.val = val
}
func (cmd *TDigestInfoCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *TDigestInfoCmd) Val() TDigestInfo {
return cmd.val
}
func (cmd *TDigestInfoCmd) Result() (TDigestInfo, error) {
return cmd.val, cmd.err
}
func (cmd *TDigestInfoCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
var key string
var result TDigestInfo
for f := 0; f < n; f++ {
key, err = rd.ReadString()
if err != nil {
return err
}
switch key {
case "Compression":
result.Compression, err = rd.ReadInt()
case "Capacity":
result.Capacity, err = rd.ReadInt()
case "Merged nodes":
result.MergedNodes, err = rd.ReadInt()
case "Unmerged nodes":
result.UnmergedNodes, err = rd.ReadInt()
case "Merged weight":
result.MergedWeight, err = rd.ReadInt()
case "Unmerged weight":
result.UnmergedWeight, err = rd.ReadInt()
case "Observations":
result.Observations, err = rd.ReadInt()
case "Total compressions":
result.TotalCompressions, err = rd.ReadInt()
case "Memory usage":
result.MemoryUsage, err = rd.ReadInt()
default:
return fmt.Errorf("redis: tdigest.info unexpected key %s", key)
}
if err != nil {
return err
}
}
cmd.val = result
return nil
}
// TDigestInfo returns information about a t-Digest data structure.
// For more information - https://redis.io/commands/tdigest.info/
func (c cmdable) TDigestInfo(ctx context.Context, key string) *TDigestInfoCmd {
args := []interface{}{"TDIGEST.INFO", key}
cmd := NewTDigestInfoCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestMax returns the maximum value from a t-Digest data structure.
// For more information - https://redis.io/commands/tdigest.max/
func (c cmdable) TDigestMax(ctx context.Context, key string) *FloatCmd {
args := []interface{}{"TDIGEST.MAX", key}
cmd := NewFloatCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
type TDigestMergeOptions struct {
Compression int64
Override bool
}
// TDigestMerge merges multiple t-Digest data structures into a single t-Digest.
// This function also allows for specifying additional options such as compression and override behavior.
// Returns OK on success or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.merge/
func (c cmdable) TDigestMerge(ctx context.Context, destKey string, options *TDigestMergeOptions, sourceKeys ...string) *StatusCmd {
args := []interface{}{"TDIGEST.MERGE", destKey, len(sourceKeys)}
for _, sourceKey := range sourceKeys {
args = append(args, sourceKey)
}
if options != nil {
if options.Compression != 0 {
args = append(args, "COMPRESSION", options.Compression)
}
if options.Override {
args = append(args, "OVERRIDE")
}
}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestMin returns the minimum value from a t-Digest data structure.
// For more information - https://redis.io/commands/tdigest.min/
func (c cmdable) TDigestMin(ctx context.Context, key string) *FloatCmd {
args := []interface{}{"TDIGEST.MIN", key}
cmd := NewFloatCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestQuantile returns an array of quantile values for one or more elements in a t-Digest data structure.
// The quantile value for an element is the fraction of all elements in the t-Digest that are less than or equal to it.
// Returns an array of floats representing the quantile values for each element or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.quantile/
func (c cmdable) TDigestQuantile(ctx context.Context, key string, elements ...float64) *FloatSliceCmd {
args := make([]interface{}, 2, 2+len(elements))
args[0] = "TDIGEST.QUANTILE"
args[1] = key
// Convert floatSlice to []interface{}
interfaceSlice := make([]interface{}, len(elements))
for i, v := range elements {
interfaceSlice[i] = v
}
args = append(args, interfaceSlice...)
cmd := NewFloatSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestRank returns an array of rank values for one or more elements in a t-Digest data structure.
// The rank of an element is its position in the sorted list of all elements in the t-Digest.
// Returns an array of integers representing the rank values for each element or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.rank/
func (c cmdable) TDigestRank(ctx context.Context, key string, values ...float64) *IntSliceCmd {
args := make([]interface{}, 2, 2+len(values))
args[0] = "TDIGEST.RANK"
args[1] = key
// Convert floatSlice to []interface{}
interfaceSlice := make([]interface{}, len(values))
for i, v := range values {
interfaceSlice[i] = v
}
args = append(args, interfaceSlice...)
cmd := NewIntSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestReset resets a t-Digest data structure to its initial state.
// Returns OK on success or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.reset/
func (c cmdable) TDigestReset(ctx context.Context, key string) *StatusCmd {
args := []interface{}{"TDIGEST.RESET", key}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestRevRank returns an array of reverse rank values for one or more elements in a t-Digest data structure.
// The reverse rank of an element is its position in the sorted list of all elements in the t-Digest when sorted in descending order.
// Returns an array of integers representing the reverse rank values for each element or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.revrank/
func (c cmdable) TDigestRevRank(ctx context.Context, key string, values ...float64) *IntSliceCmd {
args := make([]interface{}, 2, 2+len(values))
args[0] = "TDIGEST.REVRANK"
args[1] = key
// Convert floatSlice to []interface{}
interfaceSlice := make([]interface{}, len(values))
for i, v := range values {
interfaceSlice[i] = v
}
args = append(args, interfaceSlice...)
cmd := NewIntSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
// TDigestTrimmedMean returns the trimmed mean value from a t-Digest data structure.
// The trimmed mean is calculated by removing a specified fraction of the highest and lowest values from the t-Digest and then calculating the mean of the remaining values.
// Returns a float representing the trimmed mean value or an error if the operation could not be completed.
// For more information - https://redis.io/commands/tdigest.trimmed_mean/
func (c cmdable) TDigestTrimmedMean(ctx context.Context, key string, lowCutQuantile, highCutQuantile float64) *FloatCmd {
args := []interface{}{"TDIGEST.TRIMMED_MEAN", key, lowCutQuantile, highCutQuantile}
cmd := NewFloatCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}