Go-Redis × RediSearch 全流程实践

1. 连接 Redis

go 复制代码
ctx := context.Background()

rdb := redis.NewClient(&redis.Options{
	Addr:     "localhost:6379",
	Password: "",
	DB:       0,
	Protocol: 2,           // 推荐 RESP2
	// UnstableResp3: true, // 若要体验 RESP3 + Raw*
})

2. 准备示例数据

go 复制代码
user1 := map[string]interface{}{
	"name":  "Paul John",
	"email": "paul.john@example.com",
	"age":   42,
	"city":  "London",
}
user2 := map[string]interface{}{
	"name":  "Eden Zamir",
	"email": "eden.zamir@example.com",
	"age":   29,
	"city":  "Tel Aviv",
}
user3 := map[string]interface{}{
	"name":  "Paul Zamir",
	"email": "paul.zamir@example.com",
	"age":   35,
	"city":  "Tel Aviv",
}

3. 为 JSON 数据建索引

go 复制代码
_, err := rdb.FTCreate(
	ctx, "idx:users",
	&redis.FTCreateOptions{
		OnJSON: true,                   // 针对 JSON
		Prefix: []interface{}{"user:"},// 仅索引 user:* 键
	},
	// schema
	&redis.FieldSchema{              // 全文本字段
		FieldName: "$.name",
		As:        "name",
		FieldType: redis.SearchFieldTypeText,
	},
	&redis.FieldSchema{              // TAG 用于精确匹配/聚合
		FieldName: "$.city",
		As:        "city",
		FieldType: redis.SearchFieldTypeTag,
	},
	&redis.FieldSchema{              // 数值范围查询
		FieldName: "$.age",
		As:        "age",
		FieldType: redis.SearchFieldTypeNumeric,
	},
).Result()
if err != nil { panic(err) }

写入 JSON 文档

go 复制代码
_, _ = rdb.JSONSet(ctx, "user:1", "$", user1)
_, _ = rdb.JSONSet(ctx, "user:2", "$", user2)
_, _ = rdb.JSONSet(ctx, "user:3", "$", user3)

RediSearch 监听 user: 前缀,写入即自动索引。

4. 查询示例

4.1 复合搜索

go 复制代码
res, _ := rdb.FTSearch(ctx,
	"idx:users",
	"Paul @age:[30 40]",
).Result()

fmt.Printf("匹配总数:%d\n", res.Total)

4.2 指定返回字段(RETURN

go 复制代码
cities, _ := rdb.FTSearchWithArgs(
	ctx, "idx:users", "Paul",
	&redis.FTSearchOptions{
		Return: []redis.FTSearchReturn{
			{FieldName: "$.city", As: "city"},
		},
	},
).Result()

for _, d := range cities.Docs {
	fmt.Println(d.Fields["city"])
}
// London / Tel Aviv

4.3 仅计数不取文档

go 复制代码
cnt, _ := rdb.FTSearchWithArgs(
	ctx, "idx:users", "Paul",
	&redis.FTSearchOptions{CountOnly: true},
).Result()
fmt.Println(cnt.Total) // 输出 2

4.4 聚合:统计每个城市的用户数

go 复制代码
agg, _ := rdb.FTAggregateWithArgs(
	ctx, "idx:users", "*",
	&redis.FTAggregateOptions{
		GroupBy: []redis.FTAggregateGroupBy{
			{
				Fields: []interface{}{"@city"},
				Reduce: []redis.FTAggregateReducer{
					{Reducer: redis.SearchCount, As: "count"},
				},
			},
		},
	},
).Result()

for _, row := range agg.Rows {
	fmt.Printf("%s - %v\n", row.Fields["city"], row.Fields["count"])
}
// London - 1
// Tel Aviv - 2

5. 切换到 Hash 模式的差异

  1. 建索引
go 复制代码
_, err := rdb.FTCreate(
	ctx, "hash-idx:users",
	&redis.FTCreateOptions{
		OnHash: true,
		Prefix: []interface{}{"huser:"},
	},
	&redis.FieldSchema{FieldName: "name", FieldType: redis.SearchFieldTypeText},
	&redis.FieldSchema{FieldName: "city", FieldType: redis.SearchFieldTypeTag},
	&redis.FieldSchema{FieldName: "age",  FieldType: redis.SearchFieldTypeNumeric},
).Result()

OnHash:true不需要 As 别名------字段名即 Hash 的 key。

  1. 写入数据
go 复制代码
rdb.HSet(ctx, "huser:1", user1)
rdb.HSet(ctx, "huser:2", user2)
rdb.HSet(ctx, "huser:3", user3)
  1. 查询语法相同,但结果字段直接展开:
go 复制代码
docs, _ := rdb.FTSearch(ctx,
	"hash-idx:users",
	"Paul @age:[30 40]",
).Result()
fmt.Println(docs.Docs[0].Fields["city"]) // 直接获取 city

6. 常见坑位

问题 解决方案
返回 "dialect version not supported" 显式升级 RediSearch ≥ 2.4,或在服务器 FT.CONFIG SET DEFAULT_DIALECT 2
Cannot create index while write traffic is on 在生产写高峰创建索引时加 FT.CREATE ... ON JSON ... STOPWORDS 0 或使用 FT.ALTER 增量添加字段
RESP3 报 unsupported 使用 Protocol:2,或启用 UnstableResp3 并用 RawResult() 解析
查询结果字段在 $ 键下 这是 JSON 模式的设计:所有字段存入 Fields["$"] 字符串里,需要二次解析或用 RETURN $.field

7. 结语

通过 FTCreate → FTSearch → FTAggregate 等指令,Redis 在单节点即可完成近实时全文搜索与 OLAP 式聚合。

配合 go-redis,你可以:

  • 统一接口同时操作 JSON 与 Hash;
  • 利用 RETURN / CountOnly / GROUPBY 精确控制返回量;
  • 轻松在 Go 服务中嵌入"搜索引擎 + KV 存储"二合一的能力。

赶快复制示例代码试一把,让你的业务查询飞起来 🚀

相关推荐
海上小飞龙15 小时前
Redis 分布式锁原理:从 SET NX EX 到 Redisson 看门狗
数据库·redis·分布式
万物皆字节18 小时前
【避坑】使用CacheEvict需要注意的坑
redis
进击的程序猿~21 小时前
Go Interface源码深度解析指南
开发语言·后端·golang
—Qeyser1 天前
Sponge 脚手架快速创建后端API及接口使用完整教程
golang
不爱洗脚的小滕1 天前
【Golang】Go 语言实现高可用、用户态感知与多端广播的服务端 SSE 架构
开发语言·架构·golang
报错小能手1 天前
Go 简介
开发语言·后端·golang
范什么特西1 天前
回答知识总结04(redis)
数据库·redis·缓存
golang学习记2 天前
Go 项目使用docker compose的正确方式
开发语言·docker·golang
higherzjm2 天前
如何通过Linux命令自动切割nginx和redis日志
linux·redis·nginx