diff --git a/contrib/nosql/redis/redis_operation.go b/contrib/nosql/redis/redis_operation.go index 3e7ba762600..1bb83f1be0c 100644 --- a/contrib/nosql/redis/redis_operation.go +++ b/contrib/nosql/redis/redis_operation.go @@ -43,3 +43,33 @@ func (r *Redis) Conn(ctx context.Context) (gredis.Conn, error) { redis: r, }, nil } + +// Client returns the underlying redis client instance. +// This method provides access to the raw redis client for advanced operations +// that are not covered by the standard Redis interface. +// +// Example usage with type assertion: +// +// import goredis "github.com/redis/go-redis/v9" +// +// func ExampleUsage(ctx context.Context, redis *Redis) error { +// client := redis.Client() +// universalClient, ok := client.(goredis.UniversalClient) +// if !ok { +// return errors.New("failed to assert to UniversalClient") +// } +// +// // Use universalClient for advanced operations like Pipeline +// pipe := universalClient.Pipeline() +// pipe.Set(ctx, "key1", "value1", 0) +// pipe.Set(ctx, "key2", "value2", 0) +// results, err := pipe.Exec(ctx) +// if err != nil { +// return err +// } +// // ... handle results +// return nil +// } +func (r *Redis) Client() gredis.RedisRawClient { + return r.client +} diff --git a/contrib/nosql/redis/redis_z_unit_test.go b/contrib/nosql/redis/redis_z_unit_test.go index bae2e849d11..7098e06fbef 100644 --- a/contrib/nosql/redis/redis_z_unit_test.go +++ b/contrib/nosql/redis/redis_z_unit_test.go @@ -10,6 +10,8 @@ import ( "testing" "time" + goredis "github.com/redis/go-redis/v9" + "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/database/gredis" "github.com/gogf/gf/v2/frame/g" @@ -31,6 +33,73 @@ func Test_NewClose(t *testing.T) { }) } +func Test_Client(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + redis, err := gredis.New(config) + t.AssertNil(err) + t.AssertNE(redis, nil) + defer redis.Close(ctx) + + // Test getting the client + client := redis.Client() + t.AssertNE(client, nil) + + // Test type assertion to goredis.UniversalClient + universalClient, ok := client.(goredis.UniversalClient) + t.Assert(ok, true) + t.AssertNE(universalClient, nil) + + // Test that we can use the client directly for redis operations + // This demonstrates that the returned client is properly configured + result := universalClient.Set(ctx, "test-client-key", "test-value", 0) + t.AssertNil(result.Err()) + + getResult := universalClient.Get(ctx, "test-client-key") + t.AssertNil(getResult.Err()) + t.Assert(getResult.Val(), "test-value") + + // Clean up + delResult := universalClient.Del(ctx, "test-client-key") + t.AssertNil(delResult.Err()) + + // Test Pipeline functionality + pipe := universalClient.Pipeline() + t.AssertNE(pipe, nil) + + // Add multiple commands to the pipeline + pipe.Set(ctx, "pipeline-key1", "value1", 0) + pipe.Set(ctx, "pipeline-key2", "value2", 0) + pipe.Set(ctx, "pipeline-key3", "value3", 0) + pipe.Get(ctx, "pipeline-key1") + pipe.Get(ctx, "pipeline-key2") + pipe.Get(ctx, "pipeline-key3") + + // Execute the pipeline + results, err := pipe.Exec(ctx) + t.AssertNil(err) + t.Assert(len(results), 6) // 3 SET commands + 3 GET commands + + // Verify the SET results + for i := range 3 { + t.AssertNil(results[i].Err()) + } + + // Verify the GET results + getResults := results[3:] + t.Assert(getResults[0].(*goredis.StringCmd).Val(), "value1") + t.Assert(getResults[1].(*goredis.StringCmd).Val(), "value2") + t.Assert(getResults[2].(*goredis.StringCmd).Val(), "value3") + + // Clean up pipeline test keys + cleanupPipe := universalClient.Pipeline() + cleanupPipe.Del(ctx, "pipeline-key1") + cleanupPipe.Del(ctx, "pipeline-key2") + cleanupPipe.Del(ctx, "pipeline-key3") + _, err = cleanupPipe.Exec(ctx) + t.AssertNil(err) + }) +} + func Test_Do(t *testing.T) { gtest.C(t, func(t *gtest.T) { _, err := redis.Do(ctx, "SET", "k", "v") diff --git a/database/gredis/gredis_adapter.go b/database/gredis/gredis_adapter.go index 321ee96c0f9..1d57ab5fd01 100644 --- a/database/gredis/gredis_adapter.go +++ b/database/gredis/gredis_adapter.go @@ -30,6 +30,10 @@ type AdapterGroup interface { GroupString() IGroupString } +// RedisRawClient is a type alias for any, representing the raw underlying redis client. +// Implementations should return their concrete client type as this interface. +type RedisRawClient any + // AdapterOperation is the core operation functions for redis. // These functions can be easily overwritten by custom implements. type AdapterOperation interface { @@ -43,6 +47,11 @@ type AdapterOperation interface { // Close closes current redis client, closes its connection pool and releases all its related resources. Close(ctx context.Context) (err error) + + // Client returns the underlying redis client instance. + // This method provides access to the raw redis client for advanced operations + // that are not covered by the standard redis adapter interface. + Client() RedisRawClient } // Conn is an interface of a connection from universal redis client.