...

Source file src/github.com/redis/go-redis/v9/probabilistic.go

Documentation: github.com/redis/go-redis/v9

     1  package redis
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/redis/go-redis/v9/internal/proto"
     8  )
     9  
    10  type ProbabilisticCmdable interface {
    11  	BFAdd(ctx context.Context, key string, element interface{}) *BoolCmd
    12  	BFCard(ctx context.Context, key string) *IntCmd
    13  	BFExists(ctx context.Context, key string, element interface{}) *BoolCmd
    14  	BFInfo(ctx context.Context, key string) *BFInfoCmd
    15  	BFInfoArg(ctx context.Context, key, option string) *BFInfoCmd
    16  	BFInfoCapacity(ctx context.Context, key string) *BFInfoCmd
    17  	BFInfoSize(ctx context.Context, key string) *BFInfoCmd
    18  	BFInfoFilters(ctx context.Context, key string) *BFInfoCmd
    19  	BFInfoItems(ctx context.Context, key string) *BFInfoCmd
    20  	BFInfoExpansion(ctx context.Context, key string) *BFInfoCmd
    21  	BFInsert(ctx context.Context, key string, options *BFInsertOptions, elements ...interface{}) *BoolSliceCmd
    22  	BFMAdd(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd
    23  	BFMExists(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd
    24  	BFReserve(ctx context.Context, key string, errorRate float64, capacity int64) *StatusCmd
    25  	BFReserveExpansion(ctx context.Context, key string, errorRate float64, capacity, expansion int64) *StatusCmd
    26  	BFReserveNonScaling(ctx context.Context, key string, errorRate float64, capacity int64) *StatusCmd
    27  	BFReserveWithArgs(ctx context.Context, key string, options *BFReserveOptions) *StatusCmd
    28  	BFScanDump(ctx context.Context, key string, iterator int64) *ScanDumpCmd
    29  	BFLoadChunk(ctx context.Context, key string, iterator int64, data interface{}) *StatusCmd
    30  
    31  	CFAdd(ctx context.Context, key string, element interface{}) *BoolCmd
    32  	CFAddNX(ctx context.Context, key string, element interface{}) *BoolCmd
    33  	CFCount(ctx context.Context, key string, element interface{}) *IntCmd
    34  	CFDel(ctx context.Context, key string, element interface{}) *BoolCmd
    35  	CFExists(ctx context.Context, key string, element interface{}) *BoolCmd
    36  	CFInfo(ctx context.Context, key string) *CFInfoCmd
    37  	CFInsert(ctx context.Context, key string, options *CFInsertOptions, elements ...interface{}) *BoolSliceCmd
    38  	CFInsertNX(ctx context.Context, key string, options *CFInsertOptions, elements ...interface{}) *IntSliceCmd
    39  	CFMExists(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd
    40  	CFReserve(ctx context.Context, key string, capacity int64) *StatusCmd
    41  	CFReserveWithArgs(ctx context.Context, key string, options *CFReserveOptions) *StatusCmd
    42  	CFReserveExpansion(ctx context.Context, key string, capacity int64, expansion int64) *StatusCmd
    43  	CFReserveBucketSize(ctx context.Context, key string, capacity int64, bucketsize int64) *StatusCmd
    44  	CFReserveMaxIterations(ctx context.Context, key string, capacity int64, maxiterations int64) *StatusCmd
    45  	CFScanDump(ctx context.Context, key string, iterator int64) *ScanDumpCmd
    46  	CFLoadChunk(ctx context.Context, key string, iterator int64, data interface{}) *StatusCmd
    47  
    48  	CMSIncrBy(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd
    49  	CMSInfo(ctx context.Context, key string) *CMSInfoCmd
    50  	CMSInitByDim(ctx context.Context, key string, width, height int64) *StatusCmd
    51  	CMSInitByProb(ctx context.Context, key string, errorRate, probability float64) *StatusCmd
    52  	CMSMerge(ctx context.Context, destKey string, sourceKeys ...string) *StatusCmd
    53  	CMSMergeWithWeight(ctx context.Context, destKey string, sourceKeys map[string]int64) *StatusCmd
    54  	CMSQuery(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd
    55  
    56  	TopKAdd(ctx context.Context, key string, elements ...interface{}) *StringSliceCmd
    57  	TopKCount(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd
    58  	TopKIncrBy(ctx context.Context, key string, elements ...interface{}) *StringSliceCmd
    59  	TopKInfo(ctx context.Context, key string) *TopKInfoCmd
    60  	TopKList(ctx context.Context, key string) *StringSliceCmd
    61  	TopKListWithCount(ctx context.Context, key string) *MapStringIntCmd
    62  	TopKQuery(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd
    63  	TopKReserve(ctx context.Context, key string, k int64) *StatusCmd
    64  	TopKReserveWithOptions(ctx context.Context, key string, k int64, width, depth int64, decay float64) *StatusCmd
    65  
    66  	TDigestAdd(ctx context.Context, key string, elements ...float64) *StatusCmd
    67  	TDigestByRank(ctx context.Context, key string, rank ...uint64) *FloatSliceCmd
    68  	TDigestByRevRank(ctx context.Context, key string, rank ...uint64) *FloatSliceCmd
    69  	TDigestCDF(ctx context.Context, key string, elements ...float64) *FloatSliceCmd
    70  	TDigestCreate(ctx context.Context, key string) *StatusCmd
    71  	TDigestCreateWithCompression(ctx context.Context, key string, compression int64) *StatusCmd
    72  	TDigestInfo(ctx context.Context, key string) *TDigestInfoCmd
    73  	TDigestMax(ctx context.Context, key string) *FloatCmd
    74  	TDigestMin(ctx context.Context, key string) *FloatCmd
    75  	TDigestMerge(ctx context.Context, destKey string, options *TDigestMergeOptions, sourceKeys ...string) *StatusCmd
    76  	TDigestQuantile(ctx context.Context, key string, elements ...float64) *FloatSliceCmd
    77  	TDigestRank(ctx context.Context, key string, values ...float64) *IntSliceCmd
    78  	TDigestReset(ctx context.Context, key string) *StatusCmd
    79  	TDigestRevRank(ctx context.Context, key string, values ...float64) *IntSliceCmd
    80  	TDigestTrimmedMean(ctx context.Context, key string, lowCutQuantile, highCutQuantile float64) *FloatCmd
    81  }
    82  
    83  type BFInsertOptions struct {
    84  	Capacity   int64
    85  	Error      float64
    86  	Expansion  int64
    87  	NonScaling bool
    88  	NoCreate   bool
    89  }
    90  
    91  type BFReserveOptions struct {
    92  	Capacity   int64
    93  	Error      float64
    94  	Expansion  int64
    95  	NonScaling bool
    96  }
    97  
    98  type CFReserveOptions struct {
    99  	Capacity      int64
   100  	BucketSize    int64
   101  	MaxIterations int64
   102  	Expansion     int64
   103  }
   104  
   105  type CFInsertOptions struct {
   106  	Capacity int64
   107  	NoCreate bool
   108  }
   109  
   110  // -------------------------------------------
   111  // Bloom filter commands
   112  //-------------------------------------------
   113  
   114  // BFReserve creates an empty Bloom filter with a single sub-filter
   115  // for the initial specified capacity and with an upper bound error_rate.
   116  // For more information - https://redis.io/commands/bf.reserve/
   117  func (c cmdable) BFReserve(ctx context.Context, key string, errorRate float64, capacity int64) *StatusCmd {
   118  	args := []interface{}{"BF.RESERVE", key, errorRate, capacity}
   119  	cmd := NewStatusCmd(ctx, args...)
   120  	_ = c(ctx, cmd)
   121  	return cmd
   122  }
   123  
   124  // BFReserveExpansion creates an empty Bloom filter with a single sub-filter
   125  // for the initial specified capacity and with an upper bound error_rate.
   126  // This function also allows for specifying an expansion rate for the filter.
   127  // For more information - https://redis.io/commands/bf.reserve/
   128  func (c cmdable) BFReserveExpansion(ctx context.Context, key string, errorRate float64, capacity, expansion int64) *StatusCmd {
   129  	args := []interface{}{"BF.RESERVE", key, errorRate, capacity, "EXPANSION", expansion}
   130  	cmd := NewStatusCmd(ctx, args...)
   131  	_ = c(ctx, cmd)
   132  	return cmd
   133  }
   134  
   135  // BFReserveNonScaling creates an empty Bloom filter with a single sub-filter
   136  // for the initial specified capacity and with an upper bound error_rate.
   137  // This function also allows for specifying that the filter should not scale.
   138  // For more information - https://redis.io/commands/bf.reserve/
   139  func (c cmdable) BFReserveNonScaling(ctx context.Context, key string, errorRate float64, capacity int64) *StatusCmd {
   140  	args := []interface{}{"BF.RESERVE", key, errorRate, capacity, "NONSCALING"}
   141  	cmd := NewStatusCmd(ctx, args...)
   142  	_ = c(ctx, cmd)
   143  	return cmd
   144  }
   145  
   146  // BFReserveWithArgs creates an empty Bloom filter with a single sub-filter
   147  // for the initial specified capacity and with an upper bound error_rate.
   148  // This function also allows for specifying additional options such as expansion rate and non-scaling behavior.
   149  // For more information - https://redis.io/commands/bf.reserve/
   150  func (c cmdable) BFReserveWithArgs(ctx context.Context, key string, options *BFReserveOptions) *StatusCmd {
   151  	args := []interface{}{"BF.RESERVE", key}
   152  	if options != nil {
   153  		args = append(args, options.Error, options.Capacity)
   154  		if options.Expansion != 0 {
   155  			args = append(args, "EXPANSION", options.Expansion)
   156  		}
   157  		if options.NonScaling {
   158  			args = append(args, "NONSCALING")
   159  		}
   160  	}
   161  	cmd := NewStatusCmd(ctx, args...)
   162  	_ = c(ctx, cmd)
   163  	return cmd
   164  }
   165  
   166  // BFAdd adds an item to a Bloom filter.
   167  // For more information - https://redis.io/commands/bf.add/
   168  func (c cmdable) BFAdd(ctx context.Context, key string, element interface{}) *BoolCmd {
   169  	args := []interface{}{"BF.ADD", key, element}
   170  	cmd := NewBoolCmd(ctx, args...)
   171  	_ = c(ctx, cmd)
   172  	return cmd
   173  }
   174  
   175  // BFCard returns the cardinality of a Bloom filter -
   176  // number of items that were added to a Bloom filter and detected as unique
   177  // (items that caused at least one bit to be set in at least one sub-filter).
   178  // For more information - https://redis.io/commands/bf.card/
   179  func (c cmdable) BFCard(ctx context.Context, key string) *IntCmd {
   180  	args := []interface{}{"BF.CARD", key}
   181  	cmd := NewIntCmd(ctx, args...)
   182  	_ = c(ctx, cmd)
   183  	return cmd
   184  }
   185  
   186  // BFExists determines whether a given item was added to a Bloom filter.
   187  // For more information - https://redis.io/commands/bf.exists/
   188  func (c cmdable) BFExists(ctx context.Context, key string, element interface{}) *BoolCmd {
   189  	args := []interface{}{"BF.EXISTS", key, element}
   190  	cmd := NewBoolCmd(ctx, args...)
   191  	_ = c(ctx, cmd)
   192  	return cmd
   193  }
   194  
   195  // BFLoadChunk restores a Bloom filter previously saved using BF.SCANDUMP.
   196  // For more information - https://redis.io/commands/bf.loadchunk/
   197  func (c cmdable) BFLoadChunk(ctx context.Context, key string, iterator int64, data interface{}) *StatusCmd {
   198  	args := []interface{}{"BF.LOADCHUNK", key, iterator, data}
   199  	cmd := NewStatusCmd(ctx, args...)
   200  	_ = c(ctx, cmd)
   201  	return cmd
   202  }
   203  
   204  // Begins an incremental save of the Bloom filter.
   205  // This command is useful for large Bloom filters that cannot fit into the DUMP and RESTORE model.
   206  // For more information - https://redis.io/commands/bf.scandump/
   207  func (c cmdable) BFScanDump(ctx context.Context, key string, iterator int64) *ScanDumpCmd {
   208  	args := []interface{}{"BF.SCANDUMP", key, iterator}
   209  	cmd := newScanDumpCmd(ctx, args...)
   210  	_ = c(ctx, cmd)
   211  	return cmd
   212  }
   213  
   214  type ScanDump struct {
   215  	Iter int64
   216  	Data string
   217  }
   218  
   219  type ScanDumpCmd struct {
   220  	baseCmd
   221  
   222  	val ScanDump
   223  }
   224  
   225  func newScanDumpCmd(ctx context.Context, args ...interface{}) *ScanDumpCmd {
   226  	return &ScanDumpCmd{
   227  		baseCmd: baseCmd{
   228  			ctx:  ctx,
   229  			args: args,
   230  		},
   231  	}
   232  }
   233  
   234  func (cmd *ScanDumpCmd) String() string {
   235  	return cmdString(cmd, cmd.val)
   236  }
   237  
   238  func (cmd *ScanDumpCmd) SetVal(val ScanDump) {
   239  	cmd.val = val
   240  }
   241  
   242  func (cmd *ScanDumpCmd) Result() (ScanDump, error) {
   243  	return cmd.val, cmd.err
   244  }
   245  
   246  func (cmd *ScanDumpCmd) Val() ScanDump {
   247  	return cmd.val
   248  }
   249  
   250  func (cmd *ScanDumpCmd) readReply(rd *proto.Reader) (err error) {
   251  	n, err := rd.ReadMapLen()
   252  	if err != nil {
   253  		return err
   254  	}
   255  	cmd.val = ScanDump{}
   256  	for i := 0; i < n; i++ {
   257  		iter, err := rd.ReadInt()
   258  		if err != nil {
   259  			return err
   260  		}
   261  		data, err := rd.ReadString()
   262  		if err != nil {
   263  			return err
   264  		}
   265  		cmd.val.Data = data
   266  		cmd.val.Iter = iter
   267  
   268  	}
   269  
   270  	return nil
   271  }
   272  
   273  // Returns information about a Bloom filter.
   274  // For more information - https://redis.io/commands/bf.info/
   275  func (c cmdable) BFInfo(ctx context.Context, key string) *BFInfoCmd {
   276  	args := []interface{}{"BF.INFO", key}
   277  	cmd := NewBFInfoCmd(ctx, args...)
   278  	_ = c(ctx, cmd)
   279  	return cmd
   280  }
   281  
   282  type BFInfo struct {
   283  	Capacity      int64
   284  	Size          int64
   285  	Filters       int64
   286  	ItemsInserted int64
   287  	ExpansionRate int64
   288  }
   289  
   290  type BFInfoCmd struct {
   291  	baseCmd
   292  
   293  	val BFInfo
   294  }
   295  
   296  func NewBFInfoCmd(ctx context.Context, args ...interface{}) *BFInfoCmd {
   297  	return &BFInfoCmd{
   298  		baseCmd: baseCmd{
   299  			ctx:  ctx,
   300  			args: args,
   301  		},
   302  	}
   303  }
   304  
   305  func (cmd *BFInfoCmd) SetVal(val BFInfo) {
   306  	cmd.val = val
   307  }
   308  
   309  func (cmd *BFInfoCmd) String() string {
   310  	return cmdString(cmd, cmd.val)
   311  }
   312  
   313  func (cmd *BFInfoCmd) Val() BFInfo {
   314  	return cmd.val
   315  }
   316  
   317  func (cmd *BFInfoCmd) Result() (BFInfo, error) {
   318  	return cmd.val, cmd.err
   319  }
   320  
   321  func (cmd *BFInfoCmd) readReply(rd *proto.Reader) (err error) {
   322  	result := BFInfo{}
   323  
   324  	// Create a mapping from key names to pointers of struct fields
   325  	respMapping := map[string]*int64{
   326  		"Capacity":                 &result.Capacity,
   327  		"CAPACITY":                 &result.Capacity,
   328  		"Size":                     &result.Size,
   329  		"SIZE":                     &result.Size,
   330  		"Number of filters":        &result.Filters,
   331  		"FILTERS":                  &result.Filters,
   332  		"Number of items inserted": &result.ItemsInserted,
   333  		"ITEMS":                    &result.ItemsInserted,
   334  		"Expansion rate":           &result.ExpansionRate,
   335  		"EXPANSION":                &result.ExpansionRate,
   336  	}
   337  
   338  	// Helper function to read and assign a value based on the key
   339  	readAndAssignValue := func(key string) error {
   340  		fieldPtr, exists := respMapping[key]
   341  		if !exists {
   342  			return fmt.Errorf("redis: BLOOM.INFO unexpected key %s", key)
   343  		}
   344  
   345  		// Read the integer and assign to the field via pointer dereferencing
   346  		val, err := rd.ReadInt()
   347  		if err != nil {
   348  			return err
   349  		}
   350  		*fieldPtr = val
   351  		return nil
   352  	}
   353  
   354  	readType, err := rd.PeekReplyType()
   355  	if err != nil {
   356  		return err
   357  	}
   358  
   359  	if len(cmd.args) > 2 && readType == proto.RespArray {
   360  		n, err := rd.ReadArrayLen()
   361  		if err != nil {
   362  			return err
   363  		}
   364  		if key, ok := cmd.args[2].(string); ok && n == 1 {
   365  			if err := readAndAssignValue(key); err != nil {
   366  				return err
   367  			}
   368  		} else {
   369  			return fmt.Errorf("redis: BLOOM.INFO invalid argument key type")
   370  		}
   371  	} else {
   372  		n, err := rd.ReadMapLen()
   373  		if err != nil {
   374  			return err
   375  		}
   376  		for i := 0; i < n; i++ {
   377  			key, err := rd.ReadString()
   378  			if err != nil {
   379  				return err
   380  			}
   381  			if err := readAndAssignValue(key); err != nil {
   382  				return err
   383  			}
   384  		}
   385  	}
   386  
   387  	cmd.val = result
   388  	return nil
   389  }
   390  
   391  // BFInfoCapacity returns information about the capacity of a Bloom filter.
   392  // For more information - https://redis.io/commands/bf.info/
   393  func (c cmdable) BFInfoCapacity(ctx context.Context, key string) *BFInfoCmd {
   394  	return c.BFInfoArg(ctx, key, "CAPACITY")
   395  }
   396  
   397  // BFInfoSize returns information about the size of a Bloom filter.
   398  // For more information - https://redis.io/commands/bf.info/
   399  func (c cmdable) BFInfoSize(ctx context.Context, key string) *BFInfoCmd {
   400  	return c.BFInfoArg(ctx, key, "SIZE")
   401  }
   402  
   403  // BFInfoFilters returns information about the filters of a Bloom filter.
   404  // For more information - https://redis.io/commands/bf.info/
   405  func (c cmdable) BFInfoFilters(ctx context.Context, key string) *BFInfoCmd {
   406  	return c.BFInfoArg(ctx, key, "FILTERS")
   407  }
   408  
   409  // BFInfoItems returns information about the items of a Bloom filter.
   410  // For more information - https://redis.io/commands/bf.info/
   411  func (c cmdable) BFInfoItems(ctx context.Context, key string) *BFInfoCmd {
   412  	return c.BFInfoArg(ctx, key, "ITEMS")
   413  }
   414  
   415  // BFInfoExpansion returns information about the expansion rate of a Bloom filter.
   416  // For more information - https://redis.io/commands/bf.info/
   417  func (c cmdable) BFInfoExpansion(ctx context.Context, key string) *BFInfoCmd {
   418  	return c.BFInfoArg(ctx, key, "EXPANSION")
   419  }
   420  
   421  // BFInfoArg returns information about a specific option of a Bloom filter.
   422  // For more information - https://redis.io/commands/bf.info/
   423  func (c cmdable) BFInfoArg(ctx context.Context, key, option string) *BFInfoCmd {
   424  	args := []interface{}{"BF.INFO", key, option}
   425  	cmd := NewBFInfoCmd(ctx, args...)
   426  	_ = c(ctx, cmd)
   427  	return cmd
   428  }
   429  
   430  // BFInsert inserts elements into a Bloom filter.
   431  // This function also allows for specifying additional options such as:
   432  // capacity, error rate, expansion rate, and non-scaling behavior.
   433  // For more information - https://redis.io/commands/bf.insert/
   434  func (c cmdable) BFInsert(ctx context.Context, key string, options *BFInsertOptions, elements ...interface{}) *BoolSliceCmd {
   435  	args := []interface{}{"BF.INSERT", key}
   436  	if options != nil {
   437  		if options.Capacity != 0 {
   438  			args = append(args, "CAPACITY", options.Capacity)
   439  		}
   440  		if options.Error != 0 {
   441  			args = append(args, "ERROR", options.Error)
   442  		}
   443  		if options.Expansion != 0 {
   444  			args = append(args, "EXPANSION", options.Expansion)
   445  		}
   446  		if options.NoCreate {
   447  			args = append(args, "NOCREATE")
   448  		}
   449  		if options.NonScaling {
   450  			args = append(args, "NONSCALING")
   451  		}
   452  	}
   453  	args = append(args, "ITEMS")
   454  	args = append(args, elements...)
   455  
   456  	cmd := NewBoolSliceCmd(ctx, args...)
   457  	_ = c(ctx, cmd)
   458  	return cmd
   459  }
   460  
   461  // BFMAdd adds multiple elements to a Bloom filter.
   462  // Returns an array of booleans indicating whether each element was added to the filter or not.
   463  // For more information - https://redis.io/commands/bf.madd/
   464  func (c cmdable) BFMAdd(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd {
   465  	args := []interface{}{"BF.MADD", key}
   466  	args = append(args, elements...)
   467  	cmd := NewBoolSliceCmd(ctx, args...)
   468  	_ = c(ctx, cmd)
   469  	return cmd
   470  }
   471  
   472  // BFMExists check if multiple elements exist in a Bloom filter.
   473  // Returns an array of booleans indicating whether each element exists in the filter or not.
   474  // For more information - https://redis.io/commands/bf.mexists/
   475  func (c cmdable) BFMExists(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd {
   476  	args := []interface{}{"BF.MEXISTS", key}
   477  	args = append(args, elements...)
   478  
   479  	cmd := NewBoolSliceCmd(ctx, args...)
   480  	_ = c(ctx, cmd)
   481  	return cmd
   482  }
   483  
   484  // -------------------------------------------
   485  // Cuckoo filter commands
   486  //-------------------------------------------
   487  
   488  // CFReserve creates an empty Cuckoo filter with the specified capacity.
   489  // For more information - https://redis.io/commands/cf.reserve/
   490  func (c cmdable) CFReserve(ctx context.Context, key string, capacity int64) *StatusCmd {
   491  	args := []interface{}{"CF.RESERVE", key, capacity}
   492  	cmd := NewStatusCmd(ctx, args...)
   493  	_ = c(ctx, cmd)
   494  	return cmd
   495  }
   496  
   497  // CFReserveExpansion creates an empty Cuckoo filter with the specified capacity and expansion rate.
   498  // For more information - https://redis.io/commands/cf.reserve/
   499  func (c cmdable) CFReserveExpansion(ctx context.Context, key string, capacity int64, expansion int64) *StatusCmd {
   500  	args := []interface{}{"CF.RESERVE", key, capacity, "EXPANSION", expansion}
   501  	cmd := NewStatusCmd(ctx, args...)
   502  	_ = c(ctx, cmd)
   503  	return cmd
   504  }
   505  
   506  // CFReserveBucketSize creates an empty Cuckoo filter with the specified capacity and bucket size.
   507  // For more information - https://redis.io/commands/cf.reserve/
   508  func (c cmdable) CFReserveBucketSize(ctx context.Context, key string, capacity int64, bucketsize int64) *StatusCmd {
   509  	args := []interface{}{"CF.RESERVE", key, capacity, "BUCKETSIZE", bucketsize}
   510  	cmd := NewStatusCmd(ctx, args...)
   511  	_ = c(ctx, cmd)
   512  	return cmd
   513  }
   514  
   515  // CFReserveMaxIterations creates an empty Cuckoo filter with the specified capacity and maximum number of iterations.
   516  // For more information - https://redis.io/commands/cf.reserve/
   517  func (c cmdable) CFReserveMaxIterations(ctx context.Context, key string, capacity int64, maxiterations int64) *StatusCmd {
   518  	args := []interface{}{"CF.RESERVE", key, capacity, "MAXITERATIONS", maxiterations}
   519  	cmd := NewStatusCmd(ctx, args...)
   520  	_ = c(ctx, cmd)
   521  	return cmd
   522  }
   523  
   524  // CFReserveWithArgs creates an empty Cuckoo filter with the specified options.
   525  // This function allows for specifying additional options such as bucket size and maximum number of iterations.
   526  // For more information - https://redis.io/commands/cf.reserve/
   527  func (c cmdable) CFReserveWithArgs(ctx context.Context, key string, options *CFReserveOptions) *StatusCmd {
   528  	args := []interface{}{"CF.RESERVE", key, options.Capacity}
   529  	if options.BucketSize != 0 {
   530  		args = append(args, "BUCKETSIZE", options.BucketSize)
   531  	}
   532  	if options.MaxIterations != 0 {
   533  		args = append(args, "MAXITERATIONS", options.MaxIterations)
   534  	}
   535  	if options.Expansion != 0 {
   536  		args = append(args, "EXPANSION", options.Expansion)
   537  	}
   538  	cmd := NewStatusCmd(ctx, args...)
   539  	_ = c(ctx, cmd)
   540  	return cmd
   541  }
   542  
   543  // CFAdd adds an element to a Cuckoo filter.
   544  // Returns true if the element was added to the filter or false if it already exists in the filter.
   545  // For more information - https://redis.io/commands/cf.add/
   546  func (c cmdable) CFAdd(ctx context.Context, key string, element interface{}) *BoolCmd {
   547  	args := []interface{}{"CF.ADD", key, element}
   548  	cmd := NewBoolCmd(ctx, args...)
   549  	_ = c(ctx, cmd)
   550  	return cmd
   551  }
   552  
   553  // CFAddNX adds an element to a Cuckoo filter only if it does not already exist in the filter.
   554  // Returns true if the element was added to the filter or false if it already exists in the filter.
   555  // For more information - https://redis.io/commands/cf.addnx/
   556  func (c cmdable) CFAddNX(ctx context.Context, key string, element interface{}) *BoolCmd {
   557  	args := []interface{}{"CF.ADDNX", key, element}
   558  	cmd := NewBoolCmd(ctx, args...)
   559  	_ = c(ctx, cmd)
   560  	return cmd
   561  }
   562  
   563  // CFCount returns an estimate of the number of times an element may be in a Cuckoo Filter.
   564  // For more information - https://redis.io/commands/cf.count/
   565  func (c cmdable) CFCount(ctx context.Context, key string, element interface{}) *IntCmd {
   566  	args := []interface{}{"CF.COUNT", key, element}
   567  	cmd := NewIntCmd(ctx, args...)
   568  	_ = c(ctx, cmd)
   569  	return cmd
   570  }
   571  
   572  // CFDel deletes an item once from the cuckoo filter.
   573  // For more information - https://redis.io/commands/cf.del/
   574  func (c cmdable) CFDel(ctx context.Context, key string, element interface{}) *BoolCmd {
   575  	args := []interface{}{"CF.DEL", key, element}
   576  	cmd := NewBoolCmd(ctx, args...)
   577  	_ = c(ctx, cmd)
   578  	return cmd
   579  }
   580  
   581  // CFExists determines whether an item may exist in the Cuckoo Filter or not.
   582  // For more information - https://redis.io/commands/cf.exists/
   583  func (c cmdable) CFExists(ctx context.Context, key string, element interface{}) *BoolCmd {
   584  	args := []interface{}{"CF.EXISTS", key, element}
   585  	cmd := NewBoolCmd(ctx, args...)
   586  	_ = c(ctx, cmd)
   587  	return cmd
   588  }
   589  
   590  // CFLoadChunk restores a filter previously saved using SCANDUMP.
   591  // For more information - https://redis.io/commands/cf.loadchunk/
   592  func (c cmdable) CFLoadChunk(ctx context.Context, key string, iterator int64, data interface{}) *StatusCmd {
   593  	args := []interface{}{"CF.LOADCHUNK", key, iterator, data}
   594  	cmd := NewStatusCmd(ctx, args...)
   595  	_ = c(ctx, cmd)
   596  	return cmd
   597  }
   598  
   599  // CFScanDump begins an incremental save of the cuckoo filter.
   600  // For more information - https://redis.io/commands/cf.scandump/
   601  func (c cmdable) CFScanDump(ctx context.Context, key string, iterator int64) *ScanDumpCmd {
   602  	args := []interface{}{"CF.SCANDUMP", key, iterator}
   603  	cmd := newScanDumpCmd(ctx, args...)
   604  	_ = c(ctx, cmd)
   605  	return cmd
   606  }
   607  
   608  type CFInfo struct {
   609  	Size             int64
   610  	NumBuckets       int64
   611  	NumFilters       int64
   612  	NumItemsInserted int64
   613  	NumItemsDeleted  int64
   614  	BucketSize       int64
   615  	ExpansionRate    int64
   616  	MaxIteration     int64
   617  }
   618  
   619  type CFInfoCmd struct {
   620  	baseCmd
   621  
   622  	val CFInfo
   623  }
   624  
   625  func NewCFInfoCmd(ctx context.Context, args ...interface{}) *CFInfoCmd {
   626  	return &CFInfoCmd{
   627  		baseCmd: baseCmd{
   628  			ctx:  ctx,
   629  			args: args,
   630  		},
   631  	}
   632  }
   633  
   634  func (cmd *CFInfoCmd) SetVal(val CFInfo) {
   635  	cmd.val = val
   636  }
   637  
   638  func (cmd *CFInfoCmd) String() string {
   639  	return cmdString(cmd, cmd.val)
   640  }
   641  
   642  func (cmd *CFInfoCmd) Val() CFInfo {
   643  	return cmd.val
   644  }
   645  
   646  func (cmd *CFInfoCmd) Result() (CFInfo, error) {
   647  	return cmd.val, cmd.err
   648  }
   649  
   650  func (cmd *CFInfoCmd) readReply(rd *proto.Reader) (err error) {
   651  	n, err := rd.ReadMapLen()
   652  	if err != nil {
   653  		return err
   654  	}
   655  
   656  	var key string
   657  	var result CFInfo
   658  	for f := 0; f < n; f++ {
   659  		key, err = rd.ReadString()
   660  		if err != nil {
   661  			return err
   662  		}
   663  
   664  		switch key {
   665  		case "Size":
   666  			result.Size, err = rd.ReadInt()
   667  		case "Number of buckets":
   668  			result.NumBuckets, err = rd.ReadInt()
   669  		case "Number of filters":
   670  			result.NumFilters, err = rd.ReadInt()
   671  		case "Number of items inserted":
   672  			result.NumItemsInserted, err = rd.ReadInt()
   673  		case "Number of items deleted":
   674  			result.NumItemsDeleted, err = rd.ReadInt()
   675  		case "Bucket size":
   676  			result.BucketSize, err = rd.ReadInt()
   677  		case "Expansion rate":
   678  			result.ExpansionRate, err = rd.ReadInt()
   679  		case "Max iterations":
   680  			result.MaxIteration, err = rd.ReadInt()
   681  
   682  		default:
   683  			return fmt.Errorf("redis: CF.INFO unexpected key %s", key)
   684  		}
   685  
   686  		if err != nil {
   687  			return err
   688  		}
   689  	}
   690  
   691  	cmd.val = result
   692  	return nil
   693  }
   694  
   695  // CFInfo returns information about a Cuckoo filter.
   696  // For more information - https://redis.io/commands/cf.info/
   697  func (c cmdable) CFInfo(ctx context.Context, key string) *CFInfoCmd {
   698  	args := []interface{}{"CF.INFO", key}
   699  	cmd := NewCFInfoCmd(ctx, args...)
   700  	_ = c(ctx, cmd)
   701  	return cmd
   702  }
   703  
   704  // CFInsert inserts elements into a Cuckoo filter.
   705  // This function also allows for specifying additional options such as capacity, error rate, expansion rate, and non-scaling behavior.
   706  // Returns an array of booleans indicating whether each element was added to the filter or not.
   707  // For more information - https://redis.io/commands/cf.insert/
   708  func (c cmdable) CFInsert(ctx context.Context, key string, options *CFInsertOptions, elements ...interface{}) *BoolSliceCmd {
   709  	args := []interface{}{"CF.INSERT", key}
   710  	args = c.getCfInsertWithArgs(args, options, elements...)
   711  
   712  	cmd := NewBoolSliceCmd(ctx, args...)
   713  	_ = c(ctx, cmd)
   714  	return cmd
   715  }
   716  
   717  // CFInsertNX inserts elements into a Cuckoo filter only if they do not already exist in the filter.
   718  // This function also allows for specifying additional options such as:
   719  // capacity, error rate, expansion rate, and non-scaling behavior.
   720  // Returns an array of integers indicating whether each element was added to the filter or not.
   721  // For more information - https://redis.io/commands/cf.insertnx/
   722  func (c cmdable) CFInsertNX(ctx context.Context, key string, options *CFInsertOptions, elements ...interface{}) *IntSliceCmd {
   723  	args := []interface{}{"CF.INSERTNX", key}
   724  	args = c.getCfInsertWithArgs(args, options, elements...)
   725  
   726  	cmd := NewIntSliceCmd(ctx, args...)
   727  	_ = c(ctx, cmd)
   728  	return cmd
   729  }
   730  
   731  func (c cmdable) getCfInsertWithArgs(args []interface{}, options *CFInsertOptions, elements ...interface{}) []interface{} {
   732  	if options != nil {
   733  		if options.Capacity != 0 {
   734  			args = append(args, "CAPACITY", options.Capacity)
   735  		}
   736  		if options.NoCreate {
   737  			args = append(args, "NOCREATE")
   738  		}
   739  	}
   740  	args = append(args, "ITEMS")
   741  	args = append(args, elements...)
   742  
   743  	return args
   744  }
   745  
   746  // CFMExists check if multiple elements exist in a Cuckoo filter.
   747  // Returns an array of booleans indicating whether each element exists in the filter or not.
   748  // For more information - https://redis.io/commands/cf.mexists/
   749  func (c cmdable) CFMExists(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd {
   750  	args := []interface{}{"CF.MEXISTS", key}
   751  	args = append(args, elements...)
   752  	cmd := NewBoolSliceCmd(ctx, args...)
   753  	_ = c(ctx, cmd)
   754  	return cmd
   755  }
   756  
   757  // -------------------------------------------
   758  // CMS commands
   759  //-------------------------------------------
   760  
   761  // CMSIncrBy increments the count of one or more items in a Count-Min Sketch filter.
   762  // Returns an array of integers representing the updated count of each item.
   763  // For more information - https://redis.io/commands/cms.incrby/
   764  func (c cmdable) CMSIncrBy(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd {
   765  	args := make([]interface{}, 2, 2+len(elements))
   766  	args[0] = "CMS.INCRBY"
   767  	args[1] = key
   768  	args = appendArgs(args, elements)
   769  
   770  	cmd := NewIntSliceCmd(ctx, args...)
   771  	_ = c(ctx, cmd)
   772  	return cmd
   773  }
   774  
   775  type CMSInfo struct {
   776  	Width int64
   777  	Depth int64
   778  	Count int64
   779  }
   780  
   781  type CMSInfoCmd struct {
   782  	baseCmd
   783  
   784  	val CMSInfo
   785  }
   786  
   787  func NewCMSInfoCmd(ctx context.Context, args ...interface{}) *CMSInfoCmd {
   788  	return &CMSInfoCmd{
   789  		baseCmd: baseCmd{
   790  			ctx:  ctx,
   791  			args: args,
   792  		},
   793  	}
   794  }
   795  
   796  func (cmd *CMSInfoCmd) SetVal(val CMSInfo) {
   797  	cmd.val = val
   798  }
   799  
   800  func (cmd *CMSInfoCmd) String() string {
   801  	return cmdString(cmd, cmd.val)
   802  }
   803  
   804  func (cmd *CMSInfoCmd) Val() CMSInfo {
   805  	return cmd.val
   806  }
   807  
   808  func (cmd *CMSInfoCmd) Result() (CMSInfo, error) {
   809  	return cmd.val, cmd.err
   810  }
   811  
   812  func (cmd *CMSInfoCmd) readReply(rd *proto.Reader) (err error) {
   813  	n, err := rd.ReadMapLen()
   814  	if err != nil {
   815  		return err
   816  	}
   817  
   818  	var key string
   819  	var result CMSInfo
   820  	for f := 0; f < n; f++ {
   821  		key, err = rd.ReadString()
   822  		if err != nil {
   823  			return err
   824  		}
   825  
   826  		switch key {
   827  		case "width":
   828  			result.Width, err = rd.ReadInt()
   829  		case "depth":
   830  			result.Depth, err = rd.ReadInt()
   831  		case "count":
   832  			result.Count, err = rd.ReadInt()
   833  		default:
   834  			return fmt.Errorf("redis: CMS.INFO unexpected key %s", key)
   835  		}
   836  
   837  		if err != nil {
   838  			return err
   839  		}
   840  	}
   841  
   842  	cmd.val = result
   843  	return nil
   844  }
   845  
   846  // CMSInfo returns information about a Count-Min Sketch filter.
   847  // For more information - https://redis.io/commands/cms.info/
   848  func (c cmdable) CMSInfo(ctx context.Context, key string) *CMSInfoCmd {
   849  	args := []interface{}{"CMS.INFO", key}
   850  	cmd := NewCMSInfoCmd(ctx, args...)
   851  	_ = c(ctx, cmd)
   852  	return cmd
   853  }
   854  
   855  // CMSInitByDim creates an empty Count-Min Sketch filter with the specified dimensions.
   856  // For more information - https://redis.io/commands/cms.initbydim/
   857  func (c cmdable) CMSInitByDim(ctx context.Context, key string, width, depth int64) *StatusCmd {
   858  	args := []interface{}{"CMS.INITBYDIM", key, width, depth}
   859  	cmd := NewStatusCmd(ctx, args...)
   860  	_ = c(ctx, cmd)
   861  	return cmd
   862  }
   863  
   864  // CMSInitByProb creates an empty Count-Min Sketch filter with the specified error rate and probability.
   865  // For more information - https://redis.io/commands/cms.initbyprob/
   866  func (c cmdable) CMSInitByProb(ctx context.Context, key string, errorRate, probability float64) *StatusCmd {
   867  	args := []interface{}{"CMS.INITBYPROB", key, errorRate, probability}
   868  	cmd := NewStatusCmd(ctx, args...)
   869  	_ = c(ctx, cmd)
   870  	return cmd
   871  }
   872  
   873  // CMSMerge merges multiple Count-Min Sketch filters into a single filter.
   874  // The destination filter must not exist and will be created with the dimensions of the first source filter.
   875  // The number of items in each source filter must be equal.
   876  // Returns OK on success or an error if the filters could not be merged.
   877  // For more information - https://redis.io/commands/cms.merge/
   878  func (c cmdable) CMSMerge(ctx context.Context, destKey string, sourceKeys ...string) *StatusCmd {
   879  	args := []interface{}{"CMS.MERGE", destKey, len(sourceKeys)}
   880  	for _, s := range sourceKeys {
   881  		args = append(args, s)
   882  	}
   883  	cmd := NewStatusCmd(ctx, args...)
   884  	_ = c(ctx, cmd)
   885  	return cmd
   886  }
   887  
   888  // CMSMergeWithWeight merges multiple Count-Min Sketch filters into a single filter with weights for each source filter.
   889  // The destination filter must not exist and will be created with the dimensions of the first source filter.
   890  // The number of items in each source filter must be equal.
   891  // Returns OK on success or an error if the filters could not be merged.
   892  // For more information - https://redis.io/commands/cms.merge/
   893  func (c cmdable) CMSMergeWithWeight(ctx context.Context, destKey string, sourceKeys map[string]int64) *StatusCmd {
   894  	args := make([]interface{}, 0, 4+(len(sourceKeys)*2+1))
   895  	args = append(args, "CMS.MERGE", destKey, len(sourceKeys))
   896  
   897  	if len(sourceKeys) > 0 {
   898  		sk := make([]interface{}, len(sourceKeys))
   899  		sw := make([]interface{}, len(sourceKeys))
   900  
   901  		i := 0
   902  		for k, w := range sourceKeys {
   903  			sk[i] = k
   904  			sw[i] = w
   905  			i++
   906  		}
   907  
   908  		args = append(args, sk...)
   909  		args = append(args, "WEIGHTS")
   910  		args = append(args, sw...)
   911  	}
   912  
   913  	cmd := NewStatusCmd(ctx, args...)
   914  	_ = c(ctx, cmd)
   915  	return cmd
   916  }
   917  
   918  // CMSQuery returns count for item(s).
   919  // For more information - https://redis.io/commands/cms.query/
   920  func (c cmdable) CMSQuery(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd {
   921  	args := []interface{}{"CMS.QUERY", key}
   922  	args = append(args, elements...)
   923  	cmd := NewIntSliceCmd(ctx, args...)
   924  	_ = c(ctx, cmd)
   925  	return cmd
   926  }
   927  
   928  // -------------------------------------------
   929  // TopK commands
   930  //--------------------------------------------
   931  
   932  // TopKAdd adds one or more elements to a Top-K filter.
   933  // Returns an array of strings representing the items that were removed from the filter, if any.
   934  // For more information - https://redis.io/commands/topk.add/
   935  func (c cmdable) TopKAdd(ctx context.Context, key string, elements ...interface{}) *StringSliceCmd {
   936  	args := make([]interface{}, 2, 2+len(elements))
   937  	args[0] = "TOPK.ADD"
   938  	args[1] = key
   939  	args = appendArgs(args, elements)
   940  
   941  	cmd := NewStringSliceCmd(ctx, args...)
   942  	_ = c(ctx, cmd)
   943  	return cmd
   944  }
   945  
   946  // TopKReserve creates an empty Top-K filter with the specified number of top items to keep.
   947  // For more information - https://redis.io/commands/topk.reserve/
   948  func (c cmdable) TopKReserve(ctx context.Context, key string, k int64) *StatusCmd {
   949  	args := []interface{}{"TOPK.RESERVE", key, k}
   950  
   951  	cmd := NewStatusCmd(ctx, args...)
   952  	_ = c(ctx, cmd)
   953  	return cmd
   954  }
   955  
   956  // TopKReserveWithOptions creates an empty Top-K filter with the specified number of top items to keep and additional options.
   957  // This function allows for specifying additional options such as width, depth and decay.
   958  // For more information - https://redis.io/commands/topk.reserve/
   959  func (c cmdable) TopKReserveWithOptions(ctx context.Context, key string, k int64, width, depth int64, decay float64) *StatusCmd {
   960  	args := []interface{}{"TOPK.RESERVE", key, k, width, depth, decay}
   961  
   962  	cmd := NewStatusCmd(ctx, args...)
   963  	_ = c(ctx, cmd)
   964  	return cmd
   965  }
   966  
   967  type TopKInfo struct {
   968  	K     int64
   969  	Width int64
   970  	Depth int64
   971  	Decay float64
   972  }
   973  
   974  type TopKInfoCmd struct {
   975  	baseCmd
   976  
   977  	val TopKInfo
   978  }
   979  
   980  func NewTopKInfoCmd(ctx context.Context, args ...interface{}) *TopKInfoCmd {
   981  	return &TopKInfoCmd{
   982  		baseCmd: baseCmd{
   983  			ctx:  ctx,
   984  			args: args,
   985  		},
   986  	}
   987  }
   988  
   989  func (cmd *TopKInfoCmd) SetVal(val TopKInfo) {
   990  	cmd.val = val
   991  }
   992  
   993  func (cmd *TopKInfoCmd) String() string {
   994  	return cmdString(cmd, cmd.val)
   995  }
   996  
   997  func (cmd *TopKInfoCmd) Val() TopKInfo {
   998  	return cmd.val
   999  }
  1000  
  1001  func (cmd *TopKInfoCmd) Result() (TopKInfo, error) {
  1002  	return cmd.val, cmd.err
  1003  }
  1004  
  1005  func (cmd *TopKInfoCmd) readReply(rd *proto.Reader) (err error) {
  1006  	n, err := rd.ReadMapLen()
  1007  	if err != nil {
  1008  		return err
  1009  	}
  1010  
  1011  	var key string
  1012  	var result TopKInfo
  1013  	for f := 0; f < n; f++ {
  1014  		key, err = rd.ReadString()
  1015  		if err != nil {
  1016  			return err
  1017  		}
  1018  
  1019  		switch key {
  1020  		case "k":
  1021  			result.K, err = rd.ReadInt()
  1022  		case "width":
  1023  			result.Width, err = rd.ReadInt()
  1024  		case "depth":
  1025  			result.Depth, err = rd.ReadInt()
  1026  		case "decay":
  1027  			result.Decay, err = rd.ReadFloat()
  1028  		default:
  1029  			return fmt.Errorf("redis: topk.info unexpected key %s", key)
  1030  		}
  1031  
  1032  		if err != nil {
  1033  			return err
  1034  		}
  1035  	}
  1036  
  1037  	cmd.val = result
  1038  	return nil
  1039  }
  1040  
  1041  // TopKInfo returns information about a Top-K filter.
  1042  // For more information - https://redis.io/commands/topk.info/
  1043  func (c cmdable) TopKInfo(ctx context.Context, key string) *TopKInfoCmd {
  1044  	args := []interface{}{"TOPK.INFO", key}
  1045  
  1046  	cmd := NewTopKInfoCmd(ctx, args...)
  1047  	_ = c(ctx, cmd)
  1048  	return cmd
  1049  }
  1050  
  1051  // TopKQuery check if multiple elements exist in a Top-K filter.
  1052  // Returns an array of booleans indicating whether each element exists in the filter or not.
  1053  // For more information - https://redis.io/commands/topk.query/
  1054  func (c cmdable) TopKQuery(ctx context.Context, key string, elements ...interface{}) *BoolSliceCmd {
  1055  	args := make([]interface{}, 2, 2+len(elements))
  1056  	args[0] = "TOPK.QUERY"
  1057  	args[1] = key
  1058  	args = appendArgs(args, elements)
  1059  
  1060  	cmd := NewBoolSliceCmd(ctx, args...)
  1061  	_ = c(ctx, cmd)
  1062  	return cmd
  1063  }
  1064  
  1065  // TopKCount returns an estimate of the number of times an item may be in a Top-K filter.
  1066  // For more information - https://redis.io/commands/topk.count/
  1067  func (c cmdable) TopKCount(ctx context.Context, key string, elements ...interface{}) *IntSliceCmd {
  1068  	args := make([]interface{}, 2, 2+len(elements))
  1069  	args[0] = "TOPK.COUNT"
  1070  	args[1] = key
  1071  	args = appendArgs(args, elements)
  1072  
  1073  	cmd := NewIntSliceCmd(ctx, args...)
  1074  	_ = c(ctx, cmd)
  1075  	return cmd
  1076  }
  1077  
  1078  // TopKIncrBy increases the count of one or more items in a Top-K filter.
  1079  // For more information - https://redis.io/commands/topk.incrby/
  1080  func (c cmdable) TopKIncrBy(ctx context.Context, key string, elements ...interface{}) *StringSliceCmd {
  1081  	args := make([]interface{}, 2, 2+len(elements))
  1082  	args[0] = "TOPK.INCRBY"
  1083  	args[1] = key
  1084  	args = appendArgs(args, elements)
  1085  
  1086  	cmd := NewStringSliceCmd(ctx, args...)
  1087  	_ = c(ctx, cmd)
  1088  	return cmd
  1089  }
  1090  
  1091  // TopKList returns all items in Top-K list.
  1092  // For more information - https://redis.io/commands/topk.list/
  1093  func (c cmdable) TopKList(ctx context.Context, key string) *StringSliceCmd {
  1094  	args := []interface{}{"TOPK.LIST", key}
  1095  
  1096  	cmd := NewStringSliceCmd(ctx, args...)
  1097  	_ = c(ctx, cmd)
  1098  	return cmd
  1099  }
  1100  
  1101  // TopKListWithCount returns all items in Top-K list with their respective count.
  1102  // For more information - https://redis.io/commands/topk.list/
  1103  func (c cmdable) TopKListWithCount(ctx context.Context, key string) *MapStringIntCmd {
  1104  	args := []interface{}{"TOPK.LIST", key, "WITHCOUNT"}
  1105  
  1106  	cmd := NewMapStringIntCmd(ctx, args...)
  1107  	_ = c(ctx, cmd)
  1108  	return cmd
  1109  }
  1110  
  1111  // -------------------------------------------
  1112  // t-digest commands
  1113  // --------------------------------------------
  1114  
  1115  // TDigestAdd adds one or more elements to a t-Digest data structure.
  1116  // Returns OK on success or an error if the operation could not be completed.
  1117  // For more information - https://redis.io/commands/tdigest.add/
  1118  func (c cmdable) TDigestAdd(ctx context.Context, key string, elements ...float64) *StatusCmd {
  1119  	args := make([]interface{}, 2+len(elements))
  1120  	args[0] = "TDIGEST.ADD"
  1121  	args[1] = key
  1122  
  1123  	for i, v := range elements {
  1124  		args[2+i] = v
  1125  	}
  1126  
  1127  	cmd := NewStatusCmd(ctx, args...)
  1128  	_ = c(ctx, cmd)
  1129  	return cmd
  1130  }
  1131  
  1132  // TDigestByRank returns an array of values from a t-Digest data structure based on their rank.
  1133  // The rank of an element is its position in the sorted list of all elements in the t-Digest.
  1134  // Returns an array of floats representing the values at the specified ranks or an error if the operation could not be completed.
  1135  // For more information - https://redis.io/commands/tdigest.byrank/
  1136  func (c cmdable) TDigestByRank(ctx context.Context, key string, rank ...uint64) *FloatSliceCmd {
  1137  	args := make([]interface{}, 2+len(rank))
  1138  	args[0] = "TDIGEST.BYRANK"
  1139  	args[1] = key
  1140  
  1141  	for i, r := range rank {
  1142  		args[2+i] = r
  1143  	}
  1144  
  1145  	cmd := NewFloatSliceCmd(ctx, args...)
  1146  	_ = c(ctx, cmd)
  1147  	return cmd
  1148  }
  1149  
  1150  // TDigestByRevRank returns an array of values from a t-Digest data structure based on their reverse rank.
  1151  // 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.
  1152  // Returns an array of floats representing the values at the specified ranks or an error if the operation could not be completed.
  1153  // For more information - https://redis.io/commands/tdigest.byrevrank/
  1154  func (c cmdable) TDigestByRevRank(ctx context.Context, key string, rank ...uint64) *FloatSliceCmd {
  1155  	args := make([]interface{}, 2+len(rank))
  1156  	args[0] = "TDIGEST.BYREVRANK"
  1157  	args[1] = key
  1158  
  1159  	for i, r := range rank {
  1160  		args[2+i] = r
  1161  	}
  1162  
  1163  	cmd := NewFloatSliceCmd(ctx, args...)
  1164  	_ = c(ctx, cmd)
  1165  	return cmd
  1166  }
  1167  
  1168  // TDigestCDF returns an array of cumulative distribution function (CDF) values for one or more elements in a t-Digest data structure.
  1169  // The CDF value for an element is the fraction of all elements in the t-Digest that are less than or equal to it.
  1170  // Returns an array of floats representing the CDF values for each element or an error if the operation could not be completed.
  1171  // For more information - https://redis.io/commands/tdigest.cdf/
  1172  func (c cmdable) TDigestCDF(ctx context.Context, key string, elements ...float64) *FloatSliceCmd {
  1173  	args := make([]interface{}, 2+len(elements))
  1174  	args[0] = "TDIGEST.CDF"
  1175  	args[1] = key
  1176  
  1177  	for i, v := range elements {
  1178  		args[2+i] = v
  1179  	}
  1180  
  1181  	cmd := NewFloatSliceCmd(ctx, args...)
  1182  	_ = c(ctx, cmd)
  1183  	return cmd
  1184  }
  1185  
  1186  // TDigestCreate creates an empty t-Digest data structure with default parameters.
  1187  // Returns OK on success or an error if the operation could not be completed.
  1188  // For more information - https://redis.io/commands/tdigest.create/
  1189  func (c cmdable) TDigestCreate(ctx context.Context, key string) *StatusCmd {
  1190  	args := []interface{}{"TDIGEST.CREATE", key}
  1191  
  1192  	cmd := NewStatusCmd(ctx, args...)
  1193  	_ = c(ctx, cmd)
  1194  	return cmd
  1195  }
  1196  
  1197  // TDigestCreateWithCompression creates an empty t-Digest data structure with a specified compression parameter.
  1198  // The compression parameter controls the accuracy and memory usage of the t-Digest.
  1199  // Returns OK on success or an error if the operation could not be completed.
  1200  // For more information - https://redis.io/commands/tdigest.create/
  1201  func (c cmdable) TDigestCreateWithCompression(ctx context.Context, key string, compression int64) *StatusCmd {
  1202  	args := []interface{}{"TDIGEST.CREATE", key, "COMPRESSION", compression}
  1203  
  1204  	cmd := NewStatusCmd(ctx, args...)
  1205  	_ = c(ctx, cmd)
  1206  	return cmd
  1207  }
  1208  
  1209  type TDigestInfo struct {
  1210  	Compression       int64
  1211  	Capacity          int64
  1212  	MergedNodes       int64
  1213  	UnmergedNodes     int64
  1214  	MergedWeight      int64
  1215  	UnmergedWeight    int64
  1216  	Observations      int64
  1217  	TotalCompressions int64
  1218  	MemoryUsage       int64
  1219  }
  1220  
  1221  type TDigestInfoCmd struct {
  1222  	baseCmd
  1223  
  1224  	val TDigestInfo
  1225  }
  1226  
  1227  func NewTDigestInfoCmd(ctx context.Context, args ...interface{}) *TDigestInfoCmd {
  1228  	return &TDigestInfoCmd{
  1229  		baseCmd: baseCmd{
  1230  			ctx:  ctx,
  1231  			args: args,
  1232  		},
  1233  	}
  1234  }
  1235  
  1236  func (cmd *TDigestInfoCmd) SetVal(val TDigestInfo) {
  1237  	cmd.val = val
  1238  }
  1239  
  1240  func (cmd *TDigestInfoCmd) String() string {
  1241  	return cmdString(cmd, cmd.val)
  1242  }
  1243  
  1244  func (cmd *TDigestInfoCmd) Val() TDigestInfo {
  1245  	return cmd.val
  1246  }
  1247  
  1248  func (cmd *TDigestInfoCmd) Result() (TDigestInfo, error) {
  1249  	return cmd.val, cmd.err
  1250  }
  1251  
  1252  func (cmd *TDigestInfoCmd) readReply(rd *proto.Reader) (err error) {
  1253  	n, err := rd.ReadMapLen()
  1254  	if err != nil {
  1255  		return err
  1256  	}
  1257  
  1258  	var key string
  1259  	var result TDigestInfo
  1260  	for f := 0; f < n; f++ {
  1261  		key, err = rd.ReadString()
  1262  		if err != nil {
  1263  			return err
  1264  		}
  1265  
  1266  		switch key {
  1267  		case "Compression":
  1268  			result.Compression, err = rd.ReadInt()
  1269  		case "Capacity":
  1270  			result.Capacity, err = rd.ReadInt()
  1271  		case "Merged nodes":
  1272  			result.MergedNodes, err = rd.ReadInt()
  1273  		case "Unmerged nodes":
  1274  			result.UnmergedNodes, err = rd.ReadInt()
  1275  		case "Merged weight":
  1276  			result.MergedWeight, err = rd.ReadInt()
  1277  		case "Unmerged weight":
  1278  			result.UnmergedWeight, err = rd.ReadInt()
  1279  		case "Observations":
  1280  			result.Observations, err = rd.ReadInt()
  1281  		case "Total compressions":
  1282  			result.TotalCompressions, err = rd.ReadInt()
  1283  		case "Memory usage":
  1284  			result.MemoryUsage, err = rd.ReadInt()
  1285  		default:
  1286  			return fmt.Errorf("redis: tdigest.info unexpected key %s", key)
  1287  		}
  1288  
  1289  		if err != nil {
  1290  			return err
  1291  		}
  1292  	}
  1293  
  1294  	cmd.val = result
  1295  	return nil
  1296  }
  1297  
  1298  // TDigestInfo returns information about a t-Digest data structure.
  1299  // For more information - https://redis.io/commands/tdigest.info/
  1300  func (c cmdable) TDigestInfo(ctx context.Context, key string) *TDigestInfoCmd {
  1301  	args := []interface{}{"TDIGEST.INFO", key}
  1302  
  1303  	cmd := NewTDigestInfoCmd(ctx, args...)
  1304  	_ = c(ctx, cmd)
  1305  	return cmd
  1306  }
  1307  
  1308  // TDigestMax returns the maximum value from a t-Digest data structure.
  1309  // For more information - https://redis.io/commands/tdigest.max/
  1310  func (c cmdable) TDigestMax(ctx context.Context, key string) *FloatCmd {
  1311  	args := []interface{}{"TDIGEST.MAX", key}
  1312  
  1313  	cmd := NewFloatCmd(ctx, args...)
  1314  	_ = c(ctx, cmd)
  1315  	return cmd
  1316  }
  1317  
  1318  type TDigestMergeOptions struct {
  1319  	Compression int64
  1320  	Override    bool
  1321  }
  1322  
  1323  // TDigestMerge merges multiple t-Digest data structures into a single t-Digest.
  1324  // This function also allows for specifying additional options such as compression and override behavior.
  1325  // Returns OK on success or an error if the operation could not be completed.
  1326  // For more information - https://redis.io/commands/tdigest.merge/
  1327  func (c cmdable) TDigestMerge(ctx context.Context, destKey string, options *TDigestMergeOptions, sourceKeys ...string) *StatusCmd {
  1328  	args := []interface{}{"TDIGEST.MERGE", destKey, len(sourceKeys)}
  1329  
  1330  	for _, sourceKey := range sourceKeys {
  1331  		args = append(args, sourceKey)
  1332  	}
  1333  
  1334  	if options != nil {
  1335  		if options.Compression != 0 {
  1336  			args = append(args, "COMPRESSION", options.Compression)
  1337  		}
  1338  		if options.Override {
  1339  			args = append(args, "OVERRIDE")
  1340  		}
  1341  	}
  1342  
  1343  	cmd := NewStatusCmd(ctx, args...)
  1344  	_ = c(ctx, cmd)
  1345  	return cmd
  1346  }
  1347  
  1348  // TDigestMin returns the minimum value from a t-Digest data structure.
  1349  // For more information - https://redis.io/commands/tdigest.min/
  1350  func (c cmdable) TDigestMin(ctx context.Context, key string) *FloatCmd {
  1351  	args := []interface{}{"TDIGEST.MIN", key}
  1352  
  1353  	cmd := NewFloatCmd(ctx, args...)
  1354  	_ = c(ctx, cmd)
  1355  	return cmd
  1356  }
  1357  
  1358  // TDigestQuantile returns an array of quantile values for one or more elements in a t-Digest data structure.
  1359  // The quantile value for an element is the fraction of all elements in the t-Digest that are less than or equal to it.
  1360  // Returns an array of floats representing the quantile values for each element or an error if the operation could not be completed.
  1361  // For more information - https://redis.io/commands/tdigest.quantile/
  1362  func (c cmdable) TDigestQuantile(ctx context.Context, key string, elements ...float64) *FloatSliceCmd {
  1363  	args := make([]interface{}, 2+len(elements))
  1364  	args[0] = "TDIGEST.QUANTILE"
  1365  	args[1] = key
  1366  
  1367  	for i, v := range elements {
  1368  		args[2+i] = v
  1369  	}
  1370  
  1371  	cmd := NewFloatSliceCmd(ctx, args...)
  1372  	_ = c(ctx, cmd)
  1373  	return cmd
  1374  }
  1375  
  1376  // TDigestRank returns an array of rank values for one or more elements in a t-Digest data structure.
  1377  // The rank of an element is its position in the sorted list of all elements in the t-Digest.
  1378  // Returns an array of integers representing the rank values for each element or an error if the operation could not be completed.
  1379  // For more information - https://redis.io/commands/tdigest.rank/
  1380  func (c cmdable) TDigestRank(ctx context.Context, key string, values ...float64) *IntSliceCmd {
  1381  	args := make([]interface{}, 2+len(values))
  1382  	args[0] = "TDIGEST.RANK"
  1383  	args[1] = key
  1384  
  1385  	for i, v := range values {
  1386  		args[i+2] = v
  1387  	}
  1388  
  1389  	cmd := NewIntSliceCmd(ctx, args...)
  1390  	_ = c(ctx, cmd)
  1391  	return cmd
  1392  }
  1393  
  1394  // TDigestReset resets a t-Digest data structure to its initial state.
  1395  // Returns OK on success or an error if the operation could not be completed.
  1396  // For more information - https://redis.io/commands/tdigest.reset/
  1397  func (c cmdable) TDigestReset(ctx context.Context, key string) *StatusCmd {
  1398  	args := []interface{}{"TDIGEST.RESET", key}
  1399  
  1400  	cmd := NewStatusCmd(ctx, args...)
  1401  	_ = c(ctx, cmd)
  1402  	return cmd
  1403  }
  1404  
  1405  // TDigestRevRank returns an array of reverse rank values for one or more elements in a t-Digest data structure.
  1406  // 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.
  1407  // Returns an array of integers representing the reverse rank values for each element or an error if the operation could not be completed.
  1408  // For more information - https://redis.io/commands/tdigest.revrank/
  1409  func (c cmdable) TDigestRevRank(ctx context.Context, key string, values ...float64) *IntSliceCmd {
  1410  	args := make([]interface{}, 2+len(values))
  1411  	args[0] = "TDIGEST.REVRANK"
  1412  	args[1] = key
  1413  
  1414  	for i, v := range values {
  1415  		args[2+i] = v
  1416  	}
  1417  
  1418  	cmd := NewIntSliceCmd(ctx, args...)
  1419  	_ = c(ctx, cmd)
  1420  	return cmd
  1421  }
  1422  
  1423  // TDigestTrimmedMean returns the trimmed mean value from a t-Digest data structure.
  1424  // 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.
  1425  // Returns a float representing the trimmed mean value or an error if the operation could not be completed.
  1426  // For more information - https://redis.io/commands/tdigest.trimmed_mean/
  1427  func (c cmdable) TDigestTrimmedMean(ctx context.Context, key string, lowCutQuantile, highCutQuantile float64) *FloatCmd {
  1428  	args := []interface{}{"TDIGEST.TRIMMED_MEAN", key, lowCutQuantile, highCutQuantile}
  1429  
  1430  	cmd := NewFloatCmd(ctx, args...)
  1431  	_ = c(ctx, cmd)
  1432  	return cmd
  1433  }
  1434  

View as plain text