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 optional for 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 is equal 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 the 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().
The valkey:",ver" tag on an int64 field enables optimistic locking to prevent lost updates. When saving an entity:
- If the version in Valkey doesn't match the entity's version,
Save()returnsErrVersionMismatch - On successful save, the version is automatically incremented
If you don't need optimistic locking, you can omit the valkey:",ver" field entirely:
type VerlessExample struct {
Key string `valkey:",key"`
Data string `json:"data"`
}
repo := om.NewHashRepository("my_prefix", VerlessExample{}, c)
exp := repo.NewEntity()
exp.Data = "value"
repo.Save(ctx, exp) // succeeds without version checking
repo.Save(ctx, exp) // succeeds again - no version conflictNewHashRepository only accepts these field types:
string,*stringint64,*int64bool,*bool[]byte,json.RawMessage[]float32,[]float64for vector searchjson.Marshaler+json.Unmarshaler
Field projection by RediSearch is not supported.