1. 連接 Redis
ctx := context.Background()rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379",Password: "",DB: 0,Protocol: 2, // 推薦 RESP2// UnstableResp3: true, // 若要體驗 RESP3 + Raw*
})
2. 準備示例數據
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 數據建索引
_, err := rdb.FTCreate(ctx, "idx:users",&redis.FTCreateOptions{OnJSON: true, // 針對 JSONPrefix: []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 文檔
_, _ = rdb.JSONSet(ctx, "user:1", "$", user1)
_, _ = rdb.JSONSet(ctx, "user:2", "$", user2)
_, _ = rdb.JSONSet(ctx, "user:3", "$", user3)
RediSearch 監聽 user:
前綴,寫入即自動索引。
4. 查詢示例
4.1 復合搜索
res, _ := rdb.FTSearch(ctx,"idx:users","Paul @age:[30 40]",
).Result()fmt.Printf("匹配總數:%d\n", res.Total)
4.2 指定返回字段(RETURN
)
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 僅計數不取文檔
cnt, _ := rdb.FTSearchWithArgs(ctx, "idx:users", "Paul",&redis.FTSearchOptions{CountOnly: true},
).Result()
fmt.Println(cnt.Total) // 輸出 2
4.4 聚合:統計每個城市的用戶數
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 模式的差異
- 建索引
_, 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。
- 寫入數據
rdb.HSet(ctx, "huser:1", user1)
rdb.HSet(ctx, "huser:2", user2)
rdb.HSet(ctx, "huser:3", user3)
- 查詢語法相同,但結果字段直接展開:
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 存儲”二合一的能力。
趕快復制示例代碼試一把,讓你的業務查詢飛起來 🚀