package gocommon import ( "context" "encoding/json" "fmt" "os" "strconv" "strings" "github.com/go-redis/redis/v8" ) func newRedisClient(uri string, dbidxoffset int) *redis.Client { option, err := redis.ParseURL(uri) if err != nil { return nil } option.DB += dbidxoffset return redis.NewClient(option) } func NewRedisClient(uri string) (*redis.Client, error) { if !*devflag { return newRedisClient(uri, 0), nil } option, err := redis.ParseURL(uri) if err != nil { return nil, err } zero := *option zero.DB = 0 rdb := redis.NewClient(&zero) defer rdb.Close() hostname, _ := os.Hostname() myidx, _ := rdb.HGet(context.Background(), "private_db", hostname).Result() if len(myidx) > 0 { offset, _ := strconv.Atoi(myidx) option.DB += offset return redis.NewClient(option), nil } alldbs, err := rdb.HGetAll(context.Background(), "private_db").Result() if err != nil { return nil, err } maxidx := 0 for _, prvdb := range alldbs { actualidx, _ := strconv.Atoi(prvdb) if maxidx < actualidx { maxidx = actualidx } } newidx := maxidx + 1 _, err = rdb.HSet(context.Background(), "private_db", hostname, newidx).Result() if err != nil { return nil, err } option.DB += newidx return redis.NewClient(option), nil } type RedisonSetOption = string type RedisonGetOption = [2]any const ( // JSONSET command Options RedisonSetOptionNX RedisonSetOption = "NX" RedisonSetOptionXX RedisonSetOption = "XX" ) var ( RedisonGetOptionSPACE = RedisonGetOption{"SPACE", " "} RedisonGetOptionINDENT = RedisonGetOption{"INDENT", "\t"} RedisonGetOptionNEWLINE = RedisonGetOption{"NEWLINE", "\n"} RedisonGetOptionNOESCAPE = RedisonGetOption{"NOESCAPE", ""} ) // gocommon으로 옮길 거 type RedisonHandler struct { *redis.Client ctx context.Context } func NewRedisonHandler(ctx context.Context, redisClient *redis.Client) *RedisonHandler { return &RedisonHandler{ Client: redisClient, ctx: ctx, } } func respToArray[T any](resp any, err error) ([]T, error) { if err != nil { return nil, err } resArr := resp.([]any) v := make([]T, len(resArr)) for i, e := range resArr { v[i] = e.(T) } return v, nil } func appendArgs[T any](args []any, ext ...T) []any { for _, e := range ext { args = append(args, e) } return args } func (rh *RedisonHandler) JSONMSetRel(key string, prefixPath string, kv map[string]any) error { if len(prefixPath) > 0 && !strings.HasSuffix(prefixPath, ".") { prefixPath += "." } pl := rh.Pipeline() for path, obj := range kv { b, err := json.Marshal(obj) if err != nil { return err } pl.Do(rh.ctx, "JSON.SET", key, prefixPath+path, b) } cmders, err := pl.Exec(rh.ctx) if err != nil { return err } for _, cmder := range cmders { if cmder.Err() != nil { return cmder.Err() } } return nil } func (rh *RedisonHandler) JSONMSet(key string, kv map[string]any) error { return rh.JSONMSetRel(key, "", kv) } func (rh *RedisonHandler) jsonSetMergeJSONSet(cmd, key, path string, obj any, opts ...RedisonSetOption) (bool, error) { b, err := json.Marshal(obj) if err != nil { return false, err } args := []any{ "JSON.SET", key, path, b, } if len(opts) > 0 { args = append(args, opts[0]) } res, err := rh.Do(rh.ctx, args...).Result() if err != nil { return false, err } return res.(string) == "OK", nil } func (rh *RedisonHandler) JSONSet(key, path string, obj any, opts ...RedisonSetOption) (bool, error) { return rh.jsonSetMergeJSONSet("JSON.SET", key, path, obj, opts...) } func (rh *RedisonHandler) JSONMerge(key, path string, obj any, opts ...RedisonSetOption) (bool, error) { return rh.jsonSetMergeJSONSet("JSON.MERGE", key, path, obj, opts...) } func (rh *RedisonHandler) JSONGet(key, path string, opts ...RedisonGetOption) (res any, err error) { args := appendArgs[string]([]any{ "JSON.GET", key, }, strings.Split(path, " ")...) for _, opt := range opts { args = append(args, opt[:]...) } return rh.Do(rh.ctx, args...).Result() } func (rh *RedisonHandler) JSONGetString(key, path string) ([]string, error) { return respToArray[string](rh.JSONResp(key, path)) } func (rh *RedisonHandler) JSONGetDocuments(key, path string) ([]map[string]any, error) { resp, err := rh.JSONGet(key, path) if err != nil { if err == redis.Nil { return nil, nil } return nil, err } var objs []map[string]any err = json.Unmarshal([]byte(resp.(string)), &objs) return objs, err } func (rh *RedisonHandler) JSONGetInt64(key, path string) ([]int64, error) { return respToArray[int64](rh.JSONResp(key, path)) } func (rh *RedisonHandler) JSONMGet(path string, keys ...string) (res any, err error) { args := appendArgs[string]([]any{ "JSON.MGET", path, }, keys...) return rh.Do(rh.ctx, args...).Result() } func (rh *RedisonHandler) JSONMDel(key string, paths []string) error { pl := rh.Pipeline() for _, path := range paths { args := []any{ "JSON.DEL", key, path, } pl.Do(rh.ctx, args...) } _, err := pl.Exec(rh.ctx) return err } func (rh *RedisonHandler) JSONDel(key, path string) (int64, error) { args := []any{ "JSON.DEL", key, path, } resp, err := rh.Do(rh.ctx, args...).Result() if err != nil { return 0, err } return resp.(int64), nil } func (rh *RedisonHandler) JSONType(key, path string) ([]string, error) { args := []any{ "JSON.TYPE", key, path, } return respToArray[string](rh.Do(rh.ctx, args...).Result()) } func (rh *RedisonHandler) JSONNumIncrBy(key, path string, number int) ([]int64, error) { args := []any{ "JSON.NUMINCRBY", key, path, number, } resp, err := rh.Do(rh.ctx, args...).Result() if err != nil { return nil, err } var cnts []int64 err = json.Unmarshal([]byte(resp.(string)), &cnts) return cnts, err } func (rh *RedisonHandler) JSONNumMultBy(key, path string, number int) (res any, err error) { args := []any{ "JSON.NUMMULTBY", key, path, number, } resp, err := rh.Do(rh.ctx, args...).Result() if err != nil { return nil, err } return resp.([]any), nil } func (rh *RedisonHandler) JSONStrAppend(key, path string, jsonstring string) ([]int64, error) { args := []any{ "JSON.STRAPPEND", key, path, fmt.Sprintf(`'"%s"'`, jsonstring), } return respToArray[int64](rh.Do(rh.ctx, args...).Result()) } func (rh *RedisonHandler) JSONStrLen(key, path string) (res []int64, err error) { args := []any{ "JSON.STRLEN", key, path, } return respToArray[int64](rh.Do(rh.ctx, args...).Result()) } func (rh *RedisonHandler) JSONArrAppend(key, path string, values ...any) (int64, error) { args := []any{ "JSON.ARRAPPEND", key, path, } resp, err := rh.Do(rh.ctx, args...).Result() if err != nil { return 0, err } return resp.(int64), nil } func (rh *RedisonHandler) JSONArrLen(key, path string) ([]int64, error) { args := []any{ "JSON.ARRLEN", key, path, } return respToArray[int64](rh.Do(rh.ctx, args...).Result()) } func (rh *RedisonHandler) JSONArrPop(key, path string, index int) (res any, err error) { args := []any{ "JSON.ARRPOP", key, path, index, } resp, err := rh.Do(rh.ctx, args...).Result() if err != nil { return nil, err } return resp.([]any)[0], nil } func appendValues(args []any, values ...any) []any { for _, jsonValue := range values { switch jsonValue := jsonValue.(type) { case string: args = append(args, fmt.Sprintf(`'"%s"'`, jsonValue)) case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: args = append(args, jsonValue) default: bt, _ := json.Marshal(jsonValue) args = append(args, bt) } } return args } func (rh *RedisonHandler) JSONArrIndex(key, path string, jsonValue any, optionalRange ...int) ([]int64, error) { args := appendValues([]any{ "JSON.ARRINDEX", key, path, }, jsonValue) return respToArray[int64](rh.Do(rh.ctx, args...).Result()) } func (rh *RedisonHandler) JSONArrTrim(key, path string, start, end int) (res any, err error) { args := []any{ "JSON.ARRTRIM", key, path, start, end, } return respToArray[int64](rh.Do(rh.ctx, args...).Result()) } func (rh *RedisonHandler) JSONArrInsert(key, path string, index int, values ...any) (res any, err error) { args := appendValues([]any{ "JSON.ARRINSERT", key, path, index, }, values...) return respToArray[int64](rh.Do(rh.ctx, args...).Result()) } func (rh *RedisonHandler) JSONObjKeys(key, path string) ([]string, error) { args := []any{ "JSON.OBJKEYS", key, path, } res, err := rh.Do(rh.ctx, args...).Result() if err != nil { return nil, err } resArr := res.([]any) if len(resArr) == 0 { return nil, nil } resArr = resArr[0].([]any) slc := make([]string, len(resArr)) for i, r := range resArr { slc[i] = r.(string) } return slc, nil } func (rh *RedisonHandler) JSONObjLen(key, path string) ([]int64, error) { args := []any{ "JSON.OBJLEN", key, } if path != "$" { args = append(args, path) } resp, err := rh.Do(rh.ctx, args...).Result() if err != nil { return nil, err } switch resp := resp.(type) { case []any: return respToArray[int64](resp, nil) case int64: return []int64{resp}, nil } return []int64{0}, nil } func (rh *RedisonHandler) JSONDebug(key, path string) (res any, err error) { args := []any{ "JSON.DEBUG", "MEMORY", key, path, } return rh.Do(rh.ctx, args...).Result() } func (rh *RedisonHandler) JSONForget(key, path string) (int64, error) { return rh.JSONDel(key, path) } func (rh *RedisonHandler) JSONResp(key, path string) (res any, err error) { args := []any{ "JSON.RESP", key, path, } return rh.Do(rh.ctx, args...).Result() }