如何在Golang中使用redigo(Redis)设置时间?

huangapple go评论83阅读模式
英文:

How to HSET time in Golang to redigo (Redis)?

问题

我正在使用redigo在Redis中存储和检索数据。我有一个包含类型定义的结构体,后面跟着时间。我想使用Redis的HSET命令存储结构体Data。为了能够使用ScanStruct函数,我为我的Timestamp类型添加了一个RedisScan函数。

问题是Redis将Timestamp存储为遵循时间字段ext, wall, loc。你无法从这些字段创建一个新的Time对象,所以这样相当无用。如何正确地将结构体序列化为redigo?

type Timestamp time.Time

func (t *Timestamp) RedisScan(x interface{}) error {
	...
}

type Data struct {
	Timestamp  Timestamp `redis:"timestamp"`
}

func (r *RedisRepo) Write(data Data, key string) error {
	conn := r.pool.Get()
	defer conn.Close()
    conn.Send("HSET", redis.Args{key}.AddFlat(data)...)     
}

func (r *RedisRepo) Read(key string) (*Data, error) {
    var data Data
	conn := r.pool.Get()
	defer conn.Close()
    v, err := redis.Values(conn.Do("HGETALL", key))
    return redis.ScanStruct(v, &data)
}
英文:

I'm using redigo to store and retrieve data in redigo.
I have a struct that contains a type definition following time. I want to store the struct Data using HSET in Redis. I have a type definition to be able to use ScanStruct by adding a function RedisScan to my Timestamp type.

The problem is that Redis stores the Timestamp as ext, wall, loc following the time fields. You can't create a new Time object from these fields so that's fairly useless. What is the proper way to serialize a struct for redigo?

type Timestamp time.Time

func (t *Timestamp) RedisScan(x interface{}) error {
	...
}

type Data struct {
	Timestamp  Timestamp `redis:"timestamp"`
}

func (r *RedisRepo) Write(data Data, key String) error {
	conn := r.pool.Get()
	defer conn.Close()
    conn.Send("HSET", redis.Args{key}.AddFlat(data)...)     
}

func (r *RedisRepo) Read(key string) (*Data, error) {
    var data Data
	conn := r.pool.Get()
	defer conn.Close()
    v, err := redis.Values(conn.Do("HGETALL", key))
    return redis.ScanStruct(v, &data)
}

答案1

得分: 2

redis.ScanStruct函数和Args.AddFlat方法缺少一些功能,这使得它们无法作为通用的编组/解组函数使用。

修复这个问题的方法取决于你的目标是什么。如果你的目标是加载和保存结构体,而不是访问Redis哈希,请参考Save generic struct to redis

如果你的目标是使用定义的名称和值访问Redis哈希,请编写代码在这些定义和Go值之间进行转换。下面是一个示例,用于定义具有字段"timestamp"和以十进制编码的Unix秒为值的哈希:

type Data struct {
    Timestamp time.Time
}

func (r *RedisRepo) Write(data Data, key string) error {
    conn := r.pool.Get()
    defer conn.Close()
    _, err := conn.Do("HSET", key, "timestamp", data.Timestamp.Unix())
    return err
}

func (r *RedisRepo) Read(key string) (*Data, error) {
    conn := r.pool.Get()
    defer conn.Close()
    v, err := redis.Values(conn.Do("HGETALL", key))
    if err != nil {
        return nil, err
    }

    var fields struct {
        Timestamp int64 `redis:"timestamp"`
    }

    err = redis.ScanStruct(v, &fields)
    if err != nil {
        return nil, err
    }
    return &Data{Timestamp: time.Unix(fields.Timestamp, 0)}, nil
}

根据需要调整代码以匹配Redis哈希字段的定义。下面是使用RFC 3339格式表示时间的代码:

type Data struct {
    Timestamp time.Time
}

func (r *RedisRepo) Write(data Data, key string) error {
    conn := r.pool.Get()
    defer conn.Close()
    _, err := conn.Do("HSET", key, "timestamp", data.Timestamp.Format(time.RFC3339))
    return err
}

func (r *RedisRepo) Read(key string) (*Data, error) {
    conn := r.pool.Get()
    defer conn.Close()
    v, err := redis.Values(conn.Do("HGETALL", key))
    if err != nil {
        return nil, err
    }

    var fields struct {
        Timestamp string `redis:"timestamp"`
    }

    err = redis.ScanStruct(v, &fields)
    if err != nil {
        return nil, err
    }
    t, err := time.Parse(time.RFC3339, fields.Timestamp)
    if err != nil {
        return nil, err
    }
    return &Data{Timestamp: t}, nil
}

上述的Read示例是为了方便扩展到多个字段而编写的。如果应用程序只需要访问一个字段,可以将fields变量和ScanStruct替换为调用redis.Int64(conn.Do("HGET", key, "timestamp")redis.String(conn.Do("HGET", key, "timestamp")

英文:

The redis.ScanStruct function and the Args.AddFlat method are missing features that make the pair usable as general purpose marshal/unmarshal functions.

The approach for fixing the problem depends on what your goal is. ​
See Save generic struct to redis if your goal is to load and save structs, not to access a Redis hash.

If your goal is to access Redis hashes with defined names and values, then write code that translates between those definitions and Go values. Here's an example for a hash that's defined to have field "timestamp" with a value as decimal encoded Unix seconds:

type Data struct {
	Timestamp time.Time
}

func (r *RedisRepo) Write(data Data, key string) error {
	conn := r.pool.Get()
	defer conn.Close()
	_, err := conn.Do("HSET", key, "timestamp", data.Timestamp.Unix())
	return err
}

func (r *RedisRepo) Read(key string) (*Data, error) {
	conn := r.pool.Get()
	defer conn.Close()
	v, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		return nil, err
	}

	var fields struct {
		Timestamp int64 `redis:"timestamp"`
	}

	err = redis.ScanStruct(v, &fields)
	if err != nil {
		return nil, err
	}
	return &Data{Timestamp: time.Unix(fields.Timestamp, 0)}, nil
}

Adjust the code as needed to match the Redis hash field definitions. Here's the code for time in RFC 3339 format:

type Data struct {
	Timestamp time.Time
}

func (r *RedisRepo) Write(data Data, key string) error {
	conn := r.pool.Get()
	defer conn.Close()
	_, err := conn.Do("HSET", key, "timestamp", data.Timestamp.Format(time.RFC3339))
	return err
}

func (r *RedisRepo) Read(key string) (*Data, error) {
	conn := r.pool.Get()
	defer conn.Close()
	v, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		return nil, err
	}

	var fields struct {
		Timestamp string `redis:"timestamp"`
	}

	err = redis.ScanStruct(v, &fields)
	if err != nil {
		return nil, err
	}
	t, err := time.Parse(time.RFC3339, fields.Timestamp)
	if err != nil {
		return nil, err
	}
	return &Data{Timestamp: t}, nil
}

The Read examples above are written so that the examples are easy to extend to multiple fields. If the application only needs to access a single field, replace the fields variable and ScanStruct nonsense with a call to redis.Int64(conn.Do("HGET", key, "timestamp") or redis.String(conn.Do("HGET", key, "timestamp")

答案2

得分: 0

如果要向Redis的哈希集合中添加内容,至少需要以下三个值:

  1. 哈希集合的名称
  2. 哈希集合的键(也可以稍后添加额外的键)
  3. 哈希集合的值

编写代码的最佳方式是在redigo库上添加一个包装器,以抽象实现并将依赖注入到你的代码中。

// 不是最佳方式
func dummy() {
    redisConn := redisCache.pool.Get()
    defer redisConn.Close()
    response, err := redisConn.Do("HSET", args...)
}

type Implementation struct {
    RedisImplementation RedigoAbstractionConnection
}

// 最佳方式
func (i Implementation) AddDataToRedisHashSet(hashName string, key string, value string) error {
    var args = []interface{}{hashName}
    args = append(args, key, value)
    _, err := i.RedisImplementation.HSET(args...) // 返回一个接口和一个错误
    if err != nil {
        // 处理错误
    }
    return nil
}

以上是要翻译的内容。

英文:

If you are going to add something into Hash Set of Redis you need to have these three values at least.

  1. A name to your hashset
  2. Keys for your hashset(can add extra keys later on as well)
  3. Values for your hashset

Best way of coding this is to add a wrapper on the redigo library to abstract the implementation and inject the dependency into your code.

// Not the best way
func dummy(){

    redisConn := redisCache.pool.Get()
    defer redisConn.Close()
    response, err := redisConn.Do("HSET", args...)

}

type Implementation struct {
    RedisImplementation RedigoAbstractionConnection
}

// Best way
func (i Implementation) AddDataToRedisHashSet(hashName string, key string, value string) error {
    var args = []interface{}{hashName}
	args = append(args, key, value)
    _, err := i.RedisImplementation.HSET(args...) // Returns an interface and an error
    if err != nil{
        // Handle Error
    }
    return nil
}

huangapple
  • 本文由 发表于 2021年6月29日 15:42:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/68174485.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定