mirror of
https://gitee.com/johng/gf.git
synced 2024-11-29 18:57:44 +08:00
369 lines
14 KiB
Go
369 lines
14 KiB
Go
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
|
//
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
// If a copy of the MIT was not distributed with this file,
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
package redis
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/container/gvar"
|
|
"github.com/gogf/gf/v2/database/gredis"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
// GroupGeneric provides generic functions of redis.
|
|
type GroupGeneric struct {
|
|
Operation gredis.AdapterOperation
|
|
}
|
|
|
|
// GroupGeneric creates and returns GroupGeneric.
|
|
func (r *Redis) GroupGeneric() gredis.IGroupGeneric {
|
|
return GroupGeneric{
|
|
Operation: r.AdapterOperation,
|
|
}
|
|
}
|
|
|
|
// Copy copies the value stored at the source key to the destination key.
|
|
//
|
|
// By default, the destination key is created in the logical database used by the connection.
|
|
// The DB option allows specifying an alternative logical database index for the destination key.
|
|
//
|
|
// The command returns an error when the destination key already exists.
|
|
//
|
|
// It returns:
|
|
// - 1 if source was copied.
|
|
// - 0 if source was not copied.
|
|
//
|
|
// https://redis.io/commands/copy/
|
|
func (r GroupGeneric) Copy(ctx context.Context, source, destination string, option ...gredis.CopyOption) (int64, error) {
|
|
var usedOption interface{}
|
|
if len(option) > 0 {
|
|
usedOption = option[0]
|
|
}
|
|
v, err := r.Operation.Do(ctx, "Copy", mustMergeOptionToArgs(
|
|
[]interface{}{source, destination}, usedOption,
|
|
)...)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// Exists returns if key exists.
|
|
// The user should be aware that if the same existing key is mentioned in the arguments multiple times,
|
|
// it will be counted multiple times.
|
|
//
|
|
// It returns the number of keys that exist from those specified as arguments.
|
|
//
|
|
// https://redis.io/commands/exists/
|
|
func (r GroupGeneric) Exists(ctx context.Context, keys ...string) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "Exists", gconv.Interfaces(keys)...)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// Type returns the string representation of the type of the value stored at key.
|
|
// The different types that can be returned are: string, list, set, zset, hash and stream.
|
|
//
|
|
// It returns type of key, or none when key does not exist.
|
|
//
|
|
// https://redis.io/commands/type/
|
|
func (r GroupGeneric) Type(ctx context.Context, key string) (string, error) {
|
|
v, err := r.Operation.Do(ctx, "Type", key)
|
|
return v.String(), err
|
|
}
|
|
|
|
// Unlink is very similar to DEL: it removes the specified keys. Just like DEL a key is ignored if it does not exist.
|
|
// However, the command performs the actual memory reclaiming in a different thread, so it is not blocking, while DEL is.
|
|
// This is where the command name comes from: the command just unlinks the keys from the keyspace.
|
|
// The actual removal will happen later asynchronously.
|
|
//
|
|
// It returns the number of keys that were unlinked.
|
|
//
|
|
// https://redis.io/commands/unlink/
|
|
func (r GroupGeneric) Unlink(ctx context.Context, keys ...string) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "Unlink", gconv.Interfaces(keys)...)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// Rename renames key to newKey. It returns an error when key does not exist.
|
|
// If newKey already exists it is overwritten, when this happens RENAME executes an implicit DEL operation,
|
|
// so if the deleted key contains a very big value it may cause high latency even if RENAME itself is usually a constant-time operation.
|
|
//
|
|
// In Cluster mode, both key and newKey must be in the same hash slot,
|
|
// meaning that in practice only keys that have the same hashtag can be reliably renamed in cluster.
|
|
//
|
|
// https://redis.io/commands/rename/
|
|
func (r GroupGeneric) Rename(ctx context.Context, key, newKey string) error {
|
|
_, err := r.Operation.Do(ctx, "Rename", key, newKey)
|
|
return err
|
|
}
|
|
|
|
// RenameNX renames key to newKey if newKey does not yet exist.
|
|
// It returns an error when key does not exist.
|
|
// In Cluster mode, both key and newKey must be in the same hash slot,
|
|
// meaning that in practice only keys that have the same hashtag can be reliably renamed in cluster.
|
|
//
|
|
// It returns:
|
|
// - 1 if key was renamed to newKey.
|
|
// - 0 if newKey already exists.
|
|
//
|
|
// https://redis.io/commands/renamenx/
|
|
func (r GroupGeneric) RenameNX(ctx context.Context, key, newKey string) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "RenameNX", key, newKey)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// Move moves key from the currently selected database (see SELECT) to the specified destination database.
|
|
// When key already exists in the destination database, or it does not exist in the source database,
|
|
// it does nothing.
|
|
// It is possible to use MOVE as a locking primitive because of this.
|
|
//
|
|
// It returns:
|
|
// - 1 if key was moved.
|
|
// - 0 if key was not moved.
|
|
//
|
|
// https://redis.io/commands/move/
|
|
func (r GroupGeneric) Move(ctx context.Context, key string, db int) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "Move", key, db)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// Del removes the specified keys.
|
|
// a key is ignored if it does not exist.
|
|
//
|
|
// It returns the number of keys that were removed.
|
|
//
|
|
// https://redis.io/commands/del/
|
|
func (r GroupGeneric) Del(ctx context.Context, keys ...string) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "Del", gconv.Interfaces(keys)...)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// RandomKey return a random key from the currently selected database.
|
|
//
|
|
// It returns the random key, or nil when the database is empty.
|
|
//
|
|
// https://redis.io/commands/randomkey/
|
|
func (r GroupGeneric) RandomKey(ctx context.Context) (string, error) {
|
|
v, err := r.Operation.Do(ctx, "RandomKey")
|
|
return v.String(), err
|
|
}
|
|
|
|
// DBSize return the number of keys in the currently-selected database.
|
|
//
|
|
// https://redis.io/commands/dbsize/
|
|
func (r GroupGeneric) DBSize(ctx context.Context) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "DBSize")
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// Keys return all keys matching pattern.
|
|
//
|
|
// While the time complexity for this operation is O(N), the constant times are fairly low.
|
|
// For example, Redis running on an entry level laptop can scan a 1 million key database in 40 milliseconds.
|
|
//
|
|
// https://redis.io/commands/keys/
|
|
func (r GroupGeneric) Keys(ctx context.Context, pattern string) ([]string, error) {
|
|
v, err := r.Operation.Do(ctx, "Keys", pattern)
|
|
return v.Strings(), err
|
|
}
|
|
|
|
// Scan executes a single iteration of the SCAN command, returning a subset of keys matching the pattern along with the next cursor position.
|
|
// This method provides more efficient and safer way to iterate over large datasets compared to KEYS command.
|
|
//
|
|
// Users are responsible for controlling the iteration by managing the cursor.
|
|
//
|
|
// The `count` optional parameter advises Redis on the number of keys to return. While it's not a strict limit, it guides the operation's granularity.
|
|
//
|
|
// https://redis.io/commands/scan/
|
|
func (r GroupGeneric) Scan(ctx context.Context, cursor uint64, option ...gredis.ScanOption) (uint64, []string, error) {
|
|
var usedOption interface{}
|
|
if len(option) > 0 {
|
|
usedOption = option[0].ToUsedOption()
|
|
}
|
|
|
|
v, err := r.Operation.Do(ctx, "Scan", mustMergeOptionToArgs(
|
|
[]interface{}{cursor}, usedOption,
|
|
)...)
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
|
|
nextCursor := gconv.Uint64(v.Slice()[0])
|
|
keys := gconv.SliceStr(v.Slice()[1])
|
|
|
|
return nextCursor, keys, nil
|
|
}
|
|
|
|
// FlushDB delete all the keys of the currently selected DB. This command never fails.
|
|
//
|
|
// https://redis.io/commands/flushdb/
|
|
func (r GroupGeneric) FlushDB(ctx context.Context, option ...gredis.FlushOp) error {
|
|
_, err := r.Operation.Do(ctx, "FlushDB", gconv.Interfaces(option)...)
|
|
return err
|
|
}
|
|
|
|
// FlushAll delete all the keys of all the existing databases, not just the currently selected one.
|
|
// This command never fails.
|
|
// By default, FlushAll will synchronously flush all the databases.
|
|
//
|
|
// It is possible to use one of the following modifiers to dictate the flushing mode explicitly:
|
|
// ASYNC: flushes the databases asynchronously
|
|
// SYNC: flushes the databases synchronously
|
|
//
|
|
// Note: an asynchronous FlushAll command only deletes keys that were present at the time the command was invoked.
|
|
// Keys created during an asynchronous flush will be unaffected.
|
|
//
|
|
// https://redis.io/commands/flushall/
|
|
func (r GroupGeneric) FlushAll(ctx context.Context, option ...gredis.FlushOp) error {
|
|
_, err := r.Operation.Do(ctx, "FlushAll", gconv.Interfaces(option)...)
|
|
return err
|
|
}
|
|
|
|
// Expire sets a timeout on key.
|
|
// After the timeout has expired, the key will automatically be deleted.
|
|
//
|
|
// It returns:
|
|
// - 1 if the timeout was set.
|
|
// - 0 if the timeout was not set. e.g. key doesn't exist, or operation skipped due to the provided arguments.
|
|
//
|
|
// https://redis.io/commands/expire/
|
|
func (r GroupGeneric) Expire(ctx context.Context, key string, seconds int64, option ...gredis.ExpireOption) (int64, error) {
|
|
var usedOption interface{}
|
|
if len(option) > 0 {
|
|
usedOption = option[0]
|
|
}
|
|
v, err := r.Operation.Do(ctx, "Expire", mustMergeOptionToArgs(
|
|
[]interface{}{key, seconds}, usedOption,
|
|
)...)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// ExpireAt has the same effect and semantic as EXPIRE, but instead of specifying the number of
|
|
// seconds representing the TTL (time to live), it takes an absolute Unix timestamp (seconds since
|
|
// January 1, 1970).
|
|
// A timestamp in the past will delete the key immediately.
|
|
//
|
|
// It returns:
|
|
// - 1 if the timeout was set.
|
|
// - 0 if the timeout was not set. e.g. key doesn't exist, or operation skipped due to the provided arguments.
|
|
//
|
|
// https://redis.io/commands/expireat/
|
|
func (r GroupGeneric) ExpireAt(ctx context.Context, key string, time time.Time, option ...gredis.ExpireOption) (int64, error) {
|
|
var usedOption interface{}
|
|
if len(option) > 0 {
|
|
usedOption = option[0]
|
|
}
|
|
v, err := r.Operation.Do(ctx, "ExpireAt", mustMergeOptionToArgs(
|
|
[]interface{}{key, gtime.New(time).Timestamp()}, usedOption,
|
|
)...)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// ExpireTime returns the absolute time at which the given key will expire.
|
|
//
|
|
// It returns:
|
|
// - -1 if the key exists but has no associated expiration time.
|
|
// - -2 if the key does not exist.
|
|
//
|
|
// https://redis.io/commands/expiretime/
|
|
func (r GroupGeneric) ExpireTime(ctx context.Context, key string) (*gvar.Var, error) {
|
|
return r.Operation.Do(ctx, "ExpireTime", key)
|
|
}
|
|
|
|
// TTL returns the remaining time to live of a key that has a timeout.
|
|
// This introspection capability allows a Redis client to check how many seconds a given key
|
|
// will continue to be part of the dataset.
|
|
// In Redis 2.6 or older the command returns -1 if the key does not exist or if the key exist but has
|
|
// no associated expire.
|
|
//
|
|
// Starting with Redis 2.8 the return value in case of error changed:
|
|
//
|
|
// The command returns -2 if the key does not exist.
|
|
// The command returns -1 if the key exists but has no associated expire.
|
|
// See also the PTTL command that returns the same information with milliseconds resolution
|
|
// (Only available in Redis 2.6 or greater).
|
|
//
|
|
// It returns TTL in seconds, or a negative value in order to signal an error (see the description above).
|
|
//
|
|
// https://redis.io/commands/ttl/
|
|
func (r GroupGeneric) TTL(ctx context.Context, key string) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "TTL", key)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// Persist removes the existing timeout on key, turning the key from volatile (a key with an expire set)
|
|
// to persistent (a key that will never expire as no timeout is associated).
|
|
//
|
|
// It returns:
|
|
// - 1 if the timeout was removed.
|
|
// - 0 if key does not exist or does not have an associated timeout.
|
|
//
|
|
// https://redis.io/commands/persist/
|
|
func (r GroupGeneric) Persist(ctx context.Context, key string) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "Persist", key)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// PExpire works exactly like EXPIRE but the time to live of the key is specified in milliseconds
|
|
// instead of seconds.
|
|
//
|
|
// It returns:
|
|
// - 1 if the timeout was set.
|
|
// - 0 if the timeout was not set. e.g. key doesn't exist, or operation skipped due to the provided arguments.
|
|
//
|
|
// https://redis.io/commands/pexpire/
|
|
func (r GroupGeneric) PExpire(ctx context.Context, key string, milliseconds int64, option ...gredis.ExpireOption) (int64, error) {
|
|
var usedOption interface{}
|
|
if len(option) > 0 {
|
|
usedOption = option[0]
|
|
}
|
|
v, err := r.Operation.Do(ctx, "PExpire", mustMergeOptionToArgs(
|
|
[]interface{}{key, milliseconds}, usedOption,
|
|
)...)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// PExpireAt has the same effect and semantic as ExpireAt, but the Unix time at which the key will
|
|
// expire is specified in milliseconds instead of seconds.
|
|
//
|
|
// https://redis.io/commands/pexpireat/
|
|
func (r GroupGeneric) PExpireAt(ctx context.Context, key string, time time.Time, option ...gredis.ExpireOption) (int64, error) {
|
|
var usedOption interface{}
|
|
if len(option) > 0 {
|
|
usedOption = option[0]
|
|
}
|
|
v, err := r.Operation.Do(ctx, "PExpireAt", mustMergeOptionToArgs(
|
|
[]interface{}{key, gtime.New(time).TimestampMilli()}, usedOption,
|
|
)...)
|
|
return v.Int64(), err
|
|
}
|
|
|
|
// PExpireTime returns the expiration time of given `key`.
|
|
//
|
|
// It returns:
|
|
// - -1 if the key exists but has no associated expiration time.
|
|
// - -2 if the key does not exist.
|
|
//
|
|
// https://redis.io/commands/pexpiretime/
|
|
func (r GroupGeneric) PExpireTime(ctx context.Context, key string) (*gvar.Var, error) {
|
|
return r.Operation.Do(ctx, "PExpireTime", key)
|
|
}
|
|
|
|
// PTTL like TTL this command returns the remaining time to live of a key that has an expired set,
|
|
// with the sole difference that TTL returns the amount of remaining time in seconds while PTTL
|
|
// returns it in milliseconds.
|
|
//
|
|
// In Redis 2.6 or older the command returns -1 if the key does not exist or if the key exist but has
|
|
// no associated expire.
|
|
//
|
|
// It returns TTL in milliseconds, or a negative value in order to signal an error (see the description above).
|
|
//
|
|
// https://redis.io/commands/pttl/
|
|
func (r GroupGeneric) PTTL(ctx context.Context, key string) (int64, error) {
|
|
v, err := r.Operation.Do(ctx, "PTTL", key)
|
|
return v.Int64(), err
|
|
}
|