The om.NewHashRepository
and om.NewJSONRepository
creates an OM repository backed by valkey hash or RedisJSON.
package main
import (
"context"
"fmt"
"time"
"github.com/valkey-io/valkey-go"
"github.com/valkey-io/valkey-go/om"
)
type Example struct {
Key string `json:"key" valkey:",key"` // the valkey:",key" is required to indicate which field is the ULID key
Ver int64 `json:"ver" valkey:",ver"` // the valkey:",ver" is required to do optimistic locking to prevent lost update
ExAt time.Time `json:"exat" valkey:",exat"` // the valkey:",exat" is optional for setting record expiry with unix timestamp
Str string `json:"str"` // both NewHashRepository and NewJSONRepository use json tag as field name
}
func main() {
ctx := context.Background()
c, err := valkey.NewClient(valkey.ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
if err != nil {
panic(err)
}
// create the repo with NewHashRepository or NewJSONRepository
repo := om.NewHashRepository("my_prefix", Example{}, c)
exp := repo.NewEntity()
exp.Str = "mystr"
exp.ExAt = time.Now().Add(time.Hour)
fmt.Println(exp.Key) // output 01FNH4FCXV9JTB9WTVFAAKGSYB
repo.Save(ctx, exp) // success
// lookup "my_prefix:01FNH4FCXV9JTB9WTVFAAKGSYB" through client side caching
exp2, _ := repo.FetchCache(ctx, exp.Key, time.Second*5)
fmt.Println(exp2.Str) // output "mystr", which equals to exp.Str
exp2.Ver = 0 // if someone changes the version during your GET then SET operation,
repo.Save(ctx, exp2) // the save will fail with ErrVersionMismatch.
}
If you have RediSearch, you can create and search the repository against the index.
if _, ok := repo.(*om.HashRepository[Example]); ok {
repo.CreateIndex(ctx, func(schema om.FtCreateSchema) valkey.Completed {
return schema.FieldName("str").Tag().Build() // Note that the Example.Str field is mapped to str on valkey by its json tag
})
}
if _, ok := repo.(*om.JSONRepository[Example]); ok {
repo.CreateIndex(ctx, func(schema om.FtCreateSchema) valkey.Completed {
return schema.FieldName("$.str").As("str").Tag().Build() // the FieldName of a json index should be a json path syntax
})
}
exp := repo.NewEntity()
exp.Str = "special_chars:[$.-]"
repo.Save(ctx, exp)
n, records, _ := repo.Search(ctx, func(search om.FtSearchIndex) valkey.Completed {
// Note that by using the named parameters with DIALECT >= 2, you won't have to escape chars for building queries.
return search.Query("@str:{$v}").Params().Nargs(2).NameValue().NameValue("v", exp.Str).Dialect(2).Build()
})
fmt.Println("total", n) // n is total number of results matched in valkey, which is >= len(records)
for _, v := range records {
fmt.Println(v.Str) // print "special_chars:[$.-]"
}
The default index name for HashRepository
and JSONRepository
is hashidx:{prefix}
and jsonidx:{prefix}
respectively.
They can be changed by WithIndexName
option to allow searching difference indexes:
repo1 := om.NewHashRepository("my_prefix", Example{}, c, om.WithIndexName("my_index1"))
repo2 := om.NewHashRepository("my_prefix", Example{}, c, om.WithIndexName("my_index2"))
Setting a valkey:",exat"
tag on a time.Time
field will set PEXPIREAT
on the record accordingly when calling .Save()
.
If the time.Time
is zero, then the expiry will be untouched when calling .Save()
.
NewHashRepository
only accepts these field types:
string
,*string
int64
,*int64
bool
,*bool
[]byte
,json.RawMessage
[]float32
,[]float64
for vector searchjson.Marshaler+json.Unmarshaler
Field projection by RediSearch is not supported.