# 8.2 Go与各种Redis数据类型

## 8.2.1 读写列表类对象

工程结构如下:

```
(base) yanglei@yuanhong redisDataType % tree ./
./
├── conn
│   ├── pool.go
│   └── redis.go
├── dataType
│   └── list
│       └── list.go
├── go.mod
├── go.sum
└── main.go

3 directories, 6 files
```

其中`conn/`目录下的文件和上一小节的完全相同.

`dataType/list/list.go`:

```go
package list

import (
	"github.com/gomodule/redigo/redis"
)

func LPush(conn redis.Conn, key string, values ...string) (int, error) {
	length := 0

	for _, value := range values {
		err := conn.Send("LPUSH", key, value)
		if err != nil {
			return 0, err
		}
	}
	err := conn.Flush()
	if err != nil {
		return 0, err
	}

	for i := 0; i < len(values); i++ {
		reply, _ := redis.Int(conn.Receive())
		length = reply
	}

	return length, nil
}

func RPush(conn redis.Conn, key string, values ...string) (int, error) {
	length := 0

	for _, value := range values {
		err := conn.Send("RPUSH", key, value)
		if err != nil {
			return 0, err
		}
	}
	err := conn.Flush()
	if err != nil {
		return 0, err
	}

	for i := 0; i < len(values); i++ {
		reply, _ := redis.Int(conn.Receive())
		length = reply
	}

	return length, nil
}

func LPop(conn redis.Conn, key string) (string, error) {
	return redis.String(conn.Do("LPOP", key))
}

func RPop(conn redis.Conn, key string) (string, error) {
	return redis.String(conn.Do("RPOP", key))
}

func LLen(conn redis.Conn, key string) (int, error) {
	return redis.Int(conn.Do("LLEN", key))
}

func LRange(conn redis.Conn, key string, start int, end int) ([]string, error) {
	return redis.Strings(conn.Do("LRANGE", key, start, end))
}

func LTrim(conn redis.Conn, key string, start int, end int) (string, error) {
	return redis.String(conn.Do("LTRIM", key, start, end))
}

func LSet(conn redis.Conn, key string, index int, value string) (string, error) {
	return redis.String(conn.Do("LSET", key, index, value))
}

func LIndex(conn redis.Conn, key string, index int) (string, error) {
	return redis.String(conn.Do("LINDEX", key, index))
}
```

`main.go`:

```go
package main

import (
	"fmt"
	"log"
	"redisDataType/conn"
	"redisDataType/dataType/list"
	"time"
)

func main() {
	conf := conn.Conf{
		NetWork:  "tcp",
		Address:  "localhost:6379",
		User:     "redis_user",
		Password: "redis_password",
	}

	poolConf := conn.PoolConf{
		MaxIdle:     10,
		MaxActive:   100,
		IdleTimeout: time.Hour,
		Conf:        conf,
	}

	// 初始化连接池
	conn.NewPool(poolConf)

	// 从连接池中获取一个连接
	redisConn, err := conn.GetConnFromPool(poolConf)
	if err != nil {
		log.Fatalf("get conn from pool error: %v\n", err)
	}

	// 将该连接返回到连接池中
	defer conn.CloseConnToPool(redisConn)

	// 用LPUSH命令将元素插入到列表头部
	values := []string{"3", "2", "1"}
	length, err := list.LPush(redisConn, "myList", values...)
	if err != nil {
		log.Fatalf("LPUSH error: %v\n", err)
	}
	fmt.Printf("after LPUSH, length of list =  %d\n", length)

	// 查看列表中的元素
	elements, err := list.LRange(redisConn, "myList", 0, -1)
	if err != nil {
		log.Fatalf("LRANGE error: %v\n", err)
	}
	fmt.Printf("after LPUSH, LRANGE: %v\n", elements)

	// 用RPUSH命令将元素插入到列表尾部
	values = []string{"4", "5", "6"}
	length, err = list.RPush(redisConn, "myList", values...)
	if err != nil {
		log.Fatalf("RPUSH error: %v\n", err)
	}
	fmt.Printf("after RPUSH, length of list =  %d\n", length)

	// 查看列表中的元素
	elements, err = list.LRange(redisConn, "myList", 0, -1)
	if err != nil {
		log.Fatalf("LRANGE error: %v\n", err)
	}
	fmt.Printf("after RPUSH, LRANGE: %v\n", elements)

	// 用LPOP命令从列表头部弹出一个元素
	element, err := list.LPop(redisConn, "myList")
	if err != nil {
		log.Fatalf("LPOP error: %v\n", err)
	}
	fmt.Printf("the element which operated by LPOP =  %s\n", element)

	// 查看列表中的元素
	elements, err = list.LRange(redisConn, "myList", 0, -1)
	if err != nil {
		log.Fatalf("LRANGE error: %v\n", err)
	}
	fmt.Printf("after LPOP, LRANGE: %v\n", elements)
}
```

运行结果如下:

```
(base) yanglei@yuanhong redisDataType % go run main.go
after LPUSH, length of list =  3
after LPUSH, LRANGE: [1 2 3]
after RPUSH, length of list =  6
after RPUSH, LRANGE: [1 2 3 4 5 6]
the element which operated by LPOP =  1
after LPOP, LRANGE: [2 3 4 5 6]
```

## 8.2.2 读写哈希表类对象

工程结构如下:

```
(base) yanglei@yuanhong redisDataType % tree ./       
./
├── conn
│   ├── pool.go
│   └── redis.go
├── dataType
│   ├── hash
│   │   └── hash.go
│   └── list
│       └── list.go
├── go.mod
├── go.sum
└── main.go

4 directories, 7 files
```

`dataType/hash/hash.go`:

```go
package hash

import (
	"github.com/gomodule/redigo/redis"
)

func HSet(conn redis.Conn, key string, hashes map[string]string) (int, error) {
	_, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(hashes)...)
	if err != nil {
		return 0, err
	}

	length, err := HLen(conn, key)
	if err != nil {
		// TODO: 此处获取长度失败不应该返回0
		return 0, err
	}

	return length, nil
}

func HLen(conn redis.Conn, key string) (int, error) {
	return redis.Int(conn.Do("HLEN", key))
}

func HGet(conn redis.Conn, key string, field string) (string, error) {
	return redis.String(conn.Do("HGET", key, field))
}

func HGetAll(conn redis.Conn, key string) (map[string]string, error) {
	return redis.StringMap(conn.Do("HGETALL", key))
}

func HExists(conn redis.Conn, key string, field string) (bool, error) {
	return redis.Bool(conn.Do("HEXISTS", key, field))
}

func HDel(conn redis.Conn, key string, fields ...string) (int, error) {
	args := redis.Args{}.Add(key).AddFlat(fields)
	return redis.Int(conn.Do("HDEL", args...))
}
```

`main.go`:

```go
package main

import (
	"fmt"
	"log"
	"redisDataType/conn"
	"redisDataType/dataType/hash"
	"time"
)

func main() {
	conf := conn.Conf{
		NetWork:  "tcp",
		Address:  "localhost:6379",
		User:     "redis_user",
		Password: "redis_password",
	}

	poolConf := conn.PoolConf{
		MaxIdle:     10,
		MaxActive:   100,
		IdleTimeout: time.Hour,
		Conf:        conf,
	}

	// 初始化连接池
	conn.NewPool(poolConf)

	// 从连接池中获取一个连接
	redisConn, err := conn.GetConnFromPool(poolConf)
	if err != nil {
		log.Fatalf("get conn from pool error: %v\n", err)
	}

	// 将该连接返回到连接池中
	defer conn.CloseConnToPool(redisConn)

	// 向哈希表添加字段
	hashMap := map[string]string{
		"field1": "value1",
		"field2": "value2",
	}
	length, err := hash.HSet(redisConn, "hash", hashMap)
	if err != nil {
		log.Fatalf("HSET error: %v\n", err)
	}
	fmt.Printf("after HSET, the length of hash is %d\n", length)

	// 从哈希表中获取字段
	value, err := hash.HGet(redisConn, "hash", "field1")
	if err != nil {
		log.Fatalf("HGET error: %v\n", err)
	}
	fmt.Printf("key = %s, field = %s, value = %s \n", "hash", "field1", value)

	// 获取哈希表的长度
	length, err = hash.HLen(redisConn, "hash")
	if err != nil {
		log.Fatalf("HLEN error: %v\n", err)
	}
	fmt.Printf("the length of hash is %d\n", length)

	// 获取哈希表的所有字段和值
	hashMapResult, err := hash.HGetAll(redisConn, "hash")
	if err != nil {
		log.Fatalf("HGETALL error: %v\n", err)
	}
	fmt.Printf("the result of HGETALL is %v\n", hashMapResult)

	// 判断哈希表中是否存在某个字段
	exists, err := hash.HExists(redisConn, "hash", "field1")
	if err != nil {
		log.Fatalf("HEXISTS error: %v\n", err)
	}
	fmt.Printf("the field1 of hash exists: %v\n", exists)

	exists, err = hash.HExists(redisConn, "hash", "field3")
	if err != nil {
		log.Fatalf("HEXISTS error: %v\n", err)
	}
	fmt.Printf("the field3 of hash exists: %v\n", exists)

	// 删除哈希表中的字段
	deletedNum, err := hash.HDel(redisConn, "hash", "field1", "field2", "field3")
	if err != nil {
		log.Fatalf("HDEL error: %v\n", err)
	}
	fmt.Printf("the number of deleted fields is %d\n", deletedNum)
}
```

```
(base) yanglei@yuanhong redisDataType % go run main.go
after HSET, the length of hashes is 2
key = hashes, field = field1, value = value1 
the length of hashes is 2
the result of HGETALL is map[field1:value1 field2:value2]
the field1 of hashes exists: true
the field3 of hashes exists: false
the number of deleted fields is 2
```

## 8.2.3 读写集合类对象

工程结构如下:

```
(base) yanglei@yuanhong redisDataType % tree ./
./
├── conn
│   ├── pool.go
│   └── redis.go
├── dataType
│   ├── hash
│   │   └── hash.go
│   ├── list
│   │   └── list.go
│   └── set
│       └── set.go
├── go.mod
├── go.sum
└── main.go

5 directories, 8 files
```

`dataType/set/set.go`:

```go
package set

import "github.com/gomodule/redigo/redis"

func SAdd(conn redis.Conn, key string, members ...interface{}) (int, error) {
	args := redis.Args{}.Add(key).AddFlat(members)
	return redis.Int(conn.Do("SADD", args...))
}

func SIsMember(conn redis.Conn, key string, member interface{}) (bool, error) {
	return redis.Bool(conn.Do("SISMEMBER", key, member))
}

func SMembers(conn redis.Conn, key string) ([]string, error) {
	return redis.Strings(conn.Do("SMEMBERS", key))
}

// SRem 删除集合中的元素(根据给定的元素值删除) 返回删除的元素个数
func SRem(conn redis.Conn, key string, members ...interface{}) (int, error) {
	args := redis.Args{}.Add(key).AddFlat(members)
	return redis.Int(conn.Do("SREM", args...))
}

// SPop 随机删除并返回集合中的一个元素
func SPop(conn redis.Conn, key string) (string, error) {
	return redis.String(conn.Do("SPOP", key))
}
```

`main.go`:

```go
package main

import (
	"fmt"
	"log"
	"redisDataType/conn"
	"redisDataType/dataType/set"
	"time"
)

func main() {
	conf := conn.Conf{
		NetWork:  "tcp",
		Address:  "localhost:6379",
		User:     "redis_user",
		Password: "redis_password",
	}

	poolConf := conn.PoolConf{
		MaxIdle:     10,
		MaxActive:   100,
		IdleTimeout: time.Hour,
		Conf:        conf,
	}

	// 初始化连接池
	conn.NewPool(poolConf)

	// 从连接池中获取一个连接
	redisConn, err := conn.GetConnFromPool(poolConf)
	if err != nil {
		log.Fatalf("get conn from pool error: %v\n", err)
	}

	// 将该连接返回到连接池中
	defer conn.CloseConnToPool(redisConn)

	// 向集合中添加元素
	newMemberNum, err := set.SAdd(redisConn, "setKey", "setValue1", "setValue2", "setValue4", "setValue6")
	if err != nil {
		log.Fatalf("SADD error: %v\n", err)
	}
	fmt.Printf("new memeber num: %d\n", newMemberNum)

	newMemberNum, err = set.SAdd(redisConn, "setKey", "setValue2", "setValue3")
	if err != nil {
		log.Fatalf("SADD error: %v\n", err)
	}
	fmt.Printf("new memeber num: %d\n", newMemberNum)

	// 判断给定元素是否存在于集合中
	isMember, err := set.SIsMember(redisConn, "setKey", "setValue1")
	if err != nil {
		log.Fatalf("SISMEMBER error: %v\n", err)
	}
	fmt.Printf("setValue1 is member: %v\n", isMember)

	isMember, err = set.SIsMember(redisConn, "setKey", "setValue5")
	if err != nil {
		log.Fatalf("SISMEMBER error: %v\n", err)
	}
	fmt.Printf("setValue5 is member: %v\n", isMember)

	// 获取集合的所有元素
	members, err := set.SMembers(redisConn, "setKey")
	if err != nil {
		log.Fatalf("SMEMBERS error: %v\n", err)
	}
	fmt.Printf("members: %v\n", members)

	// 根据给定的元素值删除元素
	delSets := []interface{}{"setValue1", "setValue2", "setValue5"}
	delNum, err := set.SRem(redisConn, "setKey", delSets...)
	if err != nil {
		log.Fatalf("SREM error: %v\n", err)
	}
	fmt.Printf("delete num by SREM: %d\n", delNum)

	// 随机删除并返回集合中的一个元素
	popMember, err := set.SPop(redisConn, "setKey")
	if err != nil {
		log.Fatalf("SPOP error: %v\n", err)
	}
	fmt.Printf("pop member: %s\n", popMember)
}
```

运行结果如下:

```
(base) yanglei@yuanhong redisDataType % go run main.go
new memeber num: 4
new memeber num: 1
setValue1 is member: true
setValue5 is member: false
members: [setValue2 setValue4 setValue1 setValue6 setValue3]
delete num by SREM: 2
pop member: setValue4
```

## 8.2.4 读写有序集合类对象

工程结构如下:

```
(base) yanglei@yuanhong redisDataType % tree ./
./
├── conn
│   ├── pool.go
│   └── redis.go
├── dataType
│   ├── hash
│   │   └── hash.go
│   ├── list
│   │   └── list.go
│   ├── set
│   │   └── set.go
│   └── sortedSet
│       └── sortedSet.go
├── go.mod
├── go.sum
└── main.go

6 directories, 9 files
```

`dataType/sortedSet/sortedSet.go`:

```go
package sortedSet

import (
	"github.com/gomodule/redigo/redis"
)

func ZAdd(conn redis.Conn, key string, scoreMap map[int]interface{}) (int, error) {
	args := redis.Args{}.Add(key).AddFlat(scoreMap)
	return redis.Int(conn.Do("ZADD", args...))
}

func ZCard(conn redis.Conn, key string) (int, error) {
	return redis.Int(conn.Do("ZCARD", key))
}

// ZRange 根据索引获取有序集合中的元素(按照分数从小到大排序)
func ZRange(conn redis.Conn, key string, start int, stop int) ([]string, error) {
	return redis.Strings(conn.Do("ZRANGE", key, start, stop))
}

// ZRevRange 根据索引获取有序集合中的元素(按照分数从大到小排序)
func ZRevRange(conn redis.Conn, key string, start int, stop int) ([]string, error) {
	return redis.Strings(conn.Do("ZREVRANGE", key, start, stop))
}

func ZScore(conn redis.Conn, key string, member string) (float64, error) {
	return redis.Float64(conn.Do("ZSCORE", key, member))
}

func ZRem(conn redis.Conn, key string, members ...string) (int, error) {
	args := redis.Args{}.Add(key).AddFlat(members)
	return redis.Int(conn.Do("ZREM", args...))
}
```

`main.go`:

```go
package main

import (
	"fmt"
	"log"
	"redisDataType/conn"
	"redisDataType/dataType/sortedSet"
	"time"
)

func main() {
	conf := conn.Conf{
		NetWork:  "tcp",
		Address:  "localhost:6379",
		User:     "redis_user",
		Password: "redis_password",
	}

	poolConf := conn.PoolConf{
		MaxIdle:     10,
		MaxActive:   100,
		IdleTimeout: time.Hour,
		Conf:        conf,
	}

	// 初始化连接池
	conn.NewPool(poolConf)

	// 从连接池中获取一个连接
	redisConn, err := conn.GetConnFromPool(poolConf)
	if err != nil {
		log.Fatalf("get conn from pool error: %v\n", err)
	}

	// 将该连接返回到连接池中
	defer conn.CloseConnToPool(redisConn)

	// 添加元素到有序集合中
	scoreMap := map[int]interface{}{
		1: "one",
		3: "three",
	}
	addNum, err := sortedSet.ZAdd(redisConn, "myZSet", scoreMap)
	if err != nil {
		log.Fatalf("ZADD error: %v\n", err)
	}
	fmt.Printf("addNum: %d\n", addNum)

	scoreMap = map[int]interface{}{
		3: "three",
		5: "five",
	}
	addNum, err = sortedSet.ZAdd(redisConn, "myZSet", scoreMap)
	if err != nil {
		log.Fatalf("ZADD error: %v\n", err)
	}
	fmt.Printf("addNum: %d\n", addNum)

	// 获取有序集合中的元素数量
	cardNum, err := sortedSet.ZCard(redisConn, "myZSet")
	if err != nil {
		log.Fatalf("ZCARD error: %v\n", err)
	}
	fmt.Printf("cardNum: %d\n", cardNum)

	// 根据分数的索引获取元素
	// 按分数从小到大排序
	members, err := sortedSet.ZRange(redisConn, "myZSet", 0, 1)
	if err != nil {
		log.Fatalf("ZRANGE error: %v\n", err)
	}
	fmt.Printf("ZRANGE members: %v\n", members)
	// 按分数从大到小排序
	members, err = sortedSet.ZRevRange(redisConn, "myZSet", 0, 1)
	if err != nil {
		log.Fatalf("ZREVRANGE error: %v\n", err)
	}
	fmt.Printf("ZREVRANGE members: %v\n", members)

	// 获取指定成员的分数
	score, err := sortedSet.ZScore(redisConn, "myZSet", "three")
	if err != nil {
		log.Fatalf("ZSCORE error: %v\n", err)
	}
	fmt.Printf("ZSCORE score: %.1f\n", score)

	// 删除指定成员
	deletedMembers := []string{"three", "five", "seven"}
	delNum, err := sortedSet.ZRem(redisConn, "myZSet", deletedMembers...)
	if err != nil {
		log.Fatalf("ZREM error: %v\n", err)
	}
	fmt.Printf("ZREM delNum: %d\n", delNum)
}
```

运行结果:

```
(base) yanglei@yuanhong redisDataType % go run main.go 
addNum: 2
addNum: 1
cardNum: 3
ZRANGE members: [one three]
ZREVRANGE members: [five three]
ZSCORE score: 3.0
ZREM delNum: 2
```

## 8.2.5 操作地理位置数据


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://redis.sai.show/di-8-zhang-go-zheng-he-mysql-yu-redis/8.2-go-yu-ge-zhong-redis-shu-ju-lei-xing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
