mirror of https://github.com/go-redis/redis.git
Merge branch 'master' into test-redis-8
This commit is contained in:
commit
9ea2431935
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,7 @@ go 1.18
|
|||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/redis/go-redis/v9 v9.6.2
|
||||
go.uber.org/zap v1.24.0
|
||||
)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ go 1.18
|
|||
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require github.com/redis/go-redis/v9 v9.6.1
|
||||
require github.com/redis/go-redis/v9 v9.6.2
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
|
|
|
@ -4,7 +4,7 @@ go 1.18
|
|||
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require github.com/redis/go-redis/v9 v9.6.1
|
||||
require github.com/redis/go-redis/v9 v9.6.2
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
|
|
|
@ -9,8 +9,8 @@ replace github.com/redis/go-redis/extra/redisotel/v9 => ../../extra/redisotel
|
|||
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../../extra/rediscmd
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.6.1
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.6.2
|
||||
github.com/redis/go-redis/v9 v9.6.2
|
||||
github.com/uptrace/uptrace-go v1.21.0
|
||||
go.opentelemetry.io/otel v1.22.0
|
||||
)
|
||||
|
@ -23,7 +23,7 @@ require (
|
|||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.1 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.2 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.46.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||
|
|
|
@ -4,7 +4,7 @@ go 1.18
|
|||
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require github.com/redis/go-redis/v9 v9.6.1
|
||||
require github.com/redis/go-redis/v9 v9.6.2
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
|
|
|
@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
|
|||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/redis/go-redis/v9 v9.6.2
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../..
|
|||
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.1
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.2
|
||||
github.com/redis/go-redis/v9 v9.6.2
|
||||
go.opencensus.io v0.24.0
|
||||
)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ replace github.com/redis/go-redis/v9 => ../..
|
|||
require (
|
||||
github.com/bsm/ginkgo/v2 v2.12.0
|
||||
github.com/bsm/gomega v1.27.10
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/redis/go-redis/v9 v9.6.2
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../..
|
|||
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.1
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.6.2
|
||||
github.com/redis/go-redis/v9 v9.6.2
|
||||
go.opentelemetry.io/otel v1.22.0
|
||||
go.opentelemetry.io/otel/metric v1.22.0
|
||||
go.opentelemetry.io/otel/sdk v1.22.0
|
||||
|
|
|
@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
|
|||
|
||||
require (
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/redis/go-redis/v9 v9.6.2
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
8
json.go
8
json.go
|
@ -60,7 +60,7 @@ type JSONArrTrimArgs struct {
|
|||
type JSONCmd struct {
|
||||
baseCmd
|
||||
val string
|
||||
expanded []interface{}
|
||||
expanded interface{}
|
||||
}
|
||||
|
||||
var _ Cmder = (*JSONCmd)(nil)
|
||||
|
@ -100,11 +100,11 @@ func (cmd *JSONCmd) Result() (string, error) {
|
|||
return cmd.Val(), cmd.Err()
|
||||
}
|
||||
|
||||
func (cmd JSONCmd) Expanded() (interface{}, error) {
|
||||
func (cmd *JSONCmd) Expanded() (interface{}, error) {
|
||||
if len(cmd.val) != 0 && cmd.expanded == nil {
|
||||
err := json.Unmarshal([]byte(cmd.val), &cmd.expanded)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,7 +494,7 @@ func (c cmdable) JSONMSet(ctx context.Context, params ...interface{}) *StatusCmd
|
|||
}
|
||||
|
||||
// JSONNumIncrBy increments the number value stored at the specified path by the provided number.
|
||||
// For more information, see https://redis.io/commands/json.numincreby
|
||||
// For more information, see https://redis.io/docs/latest/commands/json.numincrby/
|
||||
func (c cmdable) JSONNumIncrBy(ctx context.Context, key, path string, value float64) *JSONCmd {
|
||||
args := []interface{}{"JSON.NUMINCRBY", key, path, value}
|
||||
cmd := newJSONCmd(ctx, args...)
|
||||
|
|
166
json_test.go
166
json_test.go
|
@ -2,6 +2,8 @@ package redis_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
. "github.com/bsm/ginkgo/v2"
|
||||
. "github.com/bsm/gomega"
|
||||
|
@ -17,13 +19,27 @@ var _ = Describe("JSON Commands", Label("json"), func() {
|
|||
ctx := context.TODO()
|
||||
var client *redis.Client
|
||||
|
||||
BeforeEach(func() {
|
||||
client = redis.NewClient(&redis.Options{Addr: ":6379"})
|
||||
Expect(client.FlushAll(ctx).Err()).NotTo(HaveOccurred())
|
||||
setupRedisClient := func(protocolVersion int) *redis.Client {
|
||||
return redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
DB: 0,
|
||||
Protocol: protocolVersion,
|
||||
UnstableResp3: true,
|
||||
})
|
||||
}
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(client.Close()).NotTo(HaveOccurred())
|
||||
if client != nil {
|
||||
client.FlushDB(ctx)
|
||||
client.Close()
|
||||
}
|
||||
})
|
||||
|
||||
protocols := []int{2, 3}
|
||||
for _, protocol := range protocols {
|
||||
BeforeEach(func() {
|
||||
client = setupRedisClient(protocol)
|
||||
Expect(client.FlushAll(ctx).Err()).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
Describe("arrays", Label("arrays"), func() {
|
||||
|
@ -657,4 +673,146 @@ var _ = Describe("JSON Commands", Label("json"), func() {
|
|||
Expect(cmd2.Val()[0]).To(Or(Equal([]interface{}{"boolean"}), Equal("boolean")))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
var _ = Describe("Go-Redis Advanced JSON and RediSearch Tests", func() {
|
||||
var client *redis.Client
|
||||
var ctx = context.Background()
|
||||
|
||||
setupRedisClient := func(protocolVersion int) *redis.Client {
|
||||
return redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
DB: 0,
|
||||
Protocol: protocolVersion, // Setting RESP2 or RESP3 protocol
|
||||
UnstableResp3: true, // Enable RESP3 features
|
||||
})
|
||||
}
|
||||
|
||||
AfterEach(func() {
|
||||
if client != nil {
|
||||
client.FlushDB(ctx)
|
||||
client.Close()
|
||||
}
|
||||
})
|
||||
|
||||
Context("when testing with RESP2 and RESP3", func() {
|
||||
protocols := []int{2, 3}
|
||||
|
||||
for _, protocol := range protocols {
|
||||
When("using protocol version", func() {
|
||||
BeforeEach(func() {
|
||||
client = setupRedisClient(protocol)
|
||||
})
|
||||
|
||||
It("should perform complex JSON and RediSearch operations", func() {
|
||||
jsonDoc := map[string]interface{}{
|
||||
"person": map[string]interface{}{
|
||||
"name": "Alice",
|
||||
"age": 30,
|
||||
"status": true,
|
||||
"address": map[string]interface{}{
|
||||
"city": "Wonderland",
|
||||
"postcode": "12345",
|
||||
},
|
||||
"contacts": []map[string]interface{}{
|
||||
{"type": "email", "value": "alice@example.com"},
|
||||
{"type": "phone", "value": "+123456789"},
|
||||
{"type": "fax", "value": "+987654321"},
|
||||
},
|
||||
"friends": []map[string]interface{}{
|
||||
{"name": "Bob", "age": 35, "status": true},
|
||||
{"name": "Charlie", "age": 28, "status": false},
|
||||
},
|
||||
},
|
||||
"settings": map[string]interface{}{
|
||||
"notifications": map[string]interface{}{
|
||||
"email": true,
|
||||
"sms": false,
|
||||
"alerts": []string{"low battery", "door open"},
|
||||
},
|
||||
"theme": "dark",
|
||||
},
|
||||
}
|
||||
|
||||
setCmd := client.JSONSet(ctx, "person:1", ".", jsonDoc)
|
||||
Expect(setCmd.Err()).NotTo(HaveOccurred(), "JSON.SET failed")
|
||||
|
||||
getCmdRaw := client.JSONGet(ctx, "person:1", ".")
|
||||
rawJSON, err := getCmdRaw.Result()
|
||||
Expect(err).NotTo(HaveOccurred(), "JSON.GET (raw) failed")
|
||||
GinkgoWriter.Printf("Raw JSON: %s\n", rawJSON)
|
||||
|
||||
getCmdExpanded := client.JSONGet(ctx, "person:1", ".")
|
||||
expandedJSON, err := getCmdExpanded.Expanded()
|
||||
Expect(err).NotTo(HaveOccurred(), "JSON.GET (expanded) failed")
|
||||
GinkgoWriter.Printf("Expanded JSON: %+v\n", expandedJSON)
|
||||
|
||||
Expect(rawJSON).To(MatchJSON(jsonMustMarshal(expandedJSON)))
|
||||
|
||||
arrAppendCmd := client.JSONArrAppend(ctx, "person:1", "$.person.contacts", `{"type": "social", "value": "@alice_wonder"}`)
|
||||
Expect(arrAppendCmd.Err()).NotTo(HaveOccurred(), "JSON.ARRAPPEND failed")
|
||||
arrLenCmd := client.JSONArrLen(ctx, "person:1", "$.person.contacts")
|
||||
arrLen, err := arrLenCmd.Result()
|
||||
Expect(err).NotTo(HaveOccurred(), "JSON.ARRLEN failed")
|
||||
Expect(arrLen).To(Equal([]int64{4}), "Array length mismatch after append")
|
||||
|
||||
arrInsertCmd := client.JSONArrInsert(ctx, "person:1", "$.person.friends", 1, `{"name": "Diana", "age": 25, "status": true}`)
|
||||
Expect(arrInsertCmd.Err()).NotTo(HaveOccurred(), "JSON.ARRINSERT failed")
|
||||
|
||||
start := 0
|
||||
stop := 1
|
||||
arrTrimCmd := client.JSONArrTrimWithArgs(ctx, "person:1", "$.person.friends", &redis.JSONArrTrimArgs{Start: start, Stop: &stop})
|
||||
Expect(arrTrimCmd.Err()).NotTo(HaveOccurred(), "JSON.ARRTRIM failed")
|
||||
|
||||
mergeData := map[string]interface{}{
|
||||
"status": false,
|
||||
"nickname": "WonderAlice",
|
||||
"lastLogin": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
mergeCmd := client.JSONMerge(ctx, "person:1", "$.person", jsonMustMarshal(mergeData))
|
||||
Expect(mergeCmd.Err()).NotTo(HaveOccurred(), "JSON.MERGE failed")
|
||||
|
||||
typeCmd := client.JSONType(ctx, "person:1", "$.person.nickname")
|
||||
nicknameType, err := typeCmd.Result()
|
||||
Expect(err).NotTo(HaveOccurred(), "JSON.TYPE failed")
|
||||
Expect(nicknameType[0]).To(Equal([]interface{}{"string"}), "JSON.TYPE mismatch for nickname")
|
||||
|
||||
createIndexCmd := client.Do(ctx, "FT.CREATE", "person_idx", "ON", "JSON",
|
||||
"PREFIX", "1", "person:", "SCHEMA",
|
||||
"$.person.name", "AS", "name", "TEXT",
|
||||
"$.person.age", "AS", "age", "NUMERIC",
|
||||
"$.person.address.city", "AS", "city", "TEXT",
|
||||
"$.person.contacts[*].value", "AS", "contact_value", "TEXT",
|
||||
)
|
||||
Expect(createIndexCmd.Err()).NotTo(HaveOccurred(), "FT.CREATE failed")
|
||||
|
||||
searchCmd := client.FTSearchWithArgs(ctx, "person_idx", "@contact_value:(alice\\@example\\.com alice_wonder)", &redis.FTSearchOptions{Return: []redis.FTSearchReturn{{FieldName: "$.person.name"}, {FieldName: "$.person.age"}, {FieldName: "$.person.address.city"}}})
|
||||
searchResult, err := searchCmd.Result()
|
||||
Expect(err).NotTo(HaveOccurred(), "FT.SEARCH failed")
|
||||
GinkgoWriter.Printf("Advanced Search result: %+v\n", searchResult)
|
||||
|
||||
incrCmd := client.JSONNumIncrBy(ctx, "person:1", "$.person.age", 5)
|
||||
incrResult, err := incrCmd.Result()
|
||||
Expect(err).NotTo(HaveOccurred(), "JSON.NUMINCRBY failed")
|
||||
Expect(incrResult).To(Equal("[35]"), "Age increment mismatch")
|
||||
|
||||
delCmd := client.JSONDel(ctx, "person:1", "$.settings.notifications.email")
|
||||
Expect(delCmd.Err()).NotTo(HaveOccurred(), "JSON.DEL failed")
|
||||
|
||||
typeCmd = client.JSONType(ctx, "person:1", "$.settings.notifications.email")
|
||||
typeResult, err := typeCmd.Result()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(typeResult[0]).To(BeEmpty(), "Expected JSON.TYPE to be empty for deleted field")
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Helper function to marshal data into JSON for comparisons
|
||||
func jsonMustMarshal(v interface{}) string {
|
||||
bytes, err := json.Marshal(v)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return string(bytes)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ type SearchCmdable interface {
|
|||
FTAliasAdd(ctx context.Context, index string, alias string) *StatusCmd
|
||||
FTAliasDel(ctx context.Context, alias string) *StatusCmd
|
||||
FTAliasUpdate(ctx context.Context, index string, alias string) *StatusCmd
|
||||
FTAlter(ctx context.Context, index string, skipInitalScan bool, definition []interface{}) *StatusCmd
|
||||
FTAlter(ctx context.Context, index string, skipInitialScan bool, definition []interface{}) *StatusCmd
|
||||
FTConfigGet(ctx context.Context, option string) *MapMapStringInterfaceCmd
|
||||
FTConfigSet(ctx context.Context, option string, value interface{}) *StatusCmd
|
||||
FTCreate(ctx context.Context, index string, options *FTCreateOptions, schema ...*FieldSchema) *StatusCmd
|
||||
|
@ -57,7 +57,7 @@ type FTCreateOptions struct {
|
|||
NoFields bool
|
||||
NoFreqs bool
|
||||
StopWords []interface{}
|
||||
SkipInitalScan bool
|
||||
SkipInitialScan bool
|
||||
}
|
||||
|
||||
type FieldSchema struct {
|
||||
|
@ -70,7 +70,7 @@ type FieldSchema struct {
|
|||
NoIndex bool
|
||||
PhoneticMatcher string
|
||||
Weight float64
|
||||
Seperator string
|
||||
Separator string
|
||||
CaseSensitive bool
|
||||
WithSuffixtrie bool
|
||||
VectorArgs *FTVectorArgs
|
||||
|
@ -285,7 +285,7 @@ type FTSearchSortBy struct {
|
|||
type FTSearchOptions struct {
|
||||
NoContent bool
|
||||
Verbatim bool
|
||||
NoStopWrods bool
|
||||
NoStopWords bool
|
||||
WithScores bool
|
||||
WithPayloads bool
|
||||
WithSortKeys bool
|
||||
|
@ -808,13 +808,13 @@ func (c cmdable) FTAliasUpdate(ctx context.Context, index string, alias string)
|
|||
}
|
||||
|
||||
// FTAlter - Alters the definition of an existing index.
|
||||
// The 'index' parameter specifies the index to alter, and the 'skipInitalScan' parameter specifies whether to skip the initial scan.
|
||||
// The 'index' parameter specifies the index to alter, and the 'skipInitialScan' parameter specifies whether to skip the initial scan.
|
||||
// The 'definition' parameter specifies the new definition for the index.
|
||||
// For more information, please refer to the Redis documentation:
|
||||
// [FT.ALTER]: (https://redis.io/commands/ft.alter/)
|
||||
func (c cmdable) FTAlter(ctx context.Context, index string, skipInitalScan bool, definition []interface{}) *StatusCmd {
|
||||
func (c cmdable) FTAlter(ctx context.Context, index string, skipInitialScan bool, definition []interface{}) *StatusCmd {
|
||||
args := []interface{}{"FT.ALTER", index}
|
||||
if skipInitalScan {
|
||||
if skipInitialScan {
|
||||
args = append(args, "SKIPINITIALSCAN")
|
||||
}
|
||||
args = append(args, "SCHEMA", "ADD")
|
||||
|
@ -907,7 +907,7 @@ func (c cmdable) FTCreate(ctx context.Context, index string, options *FTCreateOp
|
|||
args = append(args, "STOPWORDS", len(options.StopWords))
|
||||
args = append(args, options.StopWords...)
|
||||
}
|
||||
if options.SkipInitalScan {
|
||||
if options.SkipInitialScan {
|
||||
args = append(args, "SKIPINITIALSCAN")
|
||||
}
|
||||
}
|
||||
|
@ -1003,8 +1003,8 @@ func (c cmdable) FTCreate(ctx context.Context, index string, options *FTCreateOp
|
|||
if schema.Weight > 0 {
|
||||
args = append(args, "WEIGHT", schema.Weight)
|
||||
}
|
||||
if schema.Seperator != "" {
|
||||
args = append(args, "SEPERATOR", schema.Seperator)
|
||||
if schema.Separator != "" {
|
||||
args = append(args, "SEPARATOR", schema.Separator)
|
||||
}
|
||||
if schema.CaseSensitive {
|
||||
args = append(args, "CASESENSITIVE")
|
||||
|
@ -1694,7 +1694,7 @@ func FTSearchQuery(query string, options *FTSearchOptions) SearchQuery {
|
|||
if options.Verbatim {
|
||||
queryArgs = append(queryArgs, "VERBATIM")
|
||||
}
|
||||
if options.NoStopWrods {
|
||||
if options.NoStopWords {
|
||||
queryArgs = append(queryArgs, "NOSTOPWORDS")
|
||||
}
|
||||
if options.WithScores {
|
||||
|
@ -1808,7 +1808,7 @@ func (c cmdable) FTSearchWithArgs(ctx context.Context, index string, query strin
|
|||
if options.Verbatim {
|
||||
args = append(args, "VERBATIM")
|
||||
}
|
||||
if options.NoStopWrods {
|
||||
if options.NoStopWords {
|
||||
args = append(args, "NOSTOPWORDS")
|
||||
}
|
||||
if options.WithScores {
|
||||
|
|
|
@ -637,11 +637,11 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
|
|||
|
||||
})
|
||||
|
||||
It("should FTSearch SkipInitalScan", Label("search", "ftsearch"), func() {
|
||||
It("should FTSearch SkipInitialScan", Label("search", "ftsearch"), func() {
|
||||
client.HSet(ctx, "doc1", "foo", "bar")
|
||||
|
||||
text1 := &redis.FieldSchema{FieldName: "foo", FieldType: redis.SearchFieldTypeText}
|
||||
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{SkipInitalScan: true}, text1).Result()
|
||||
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{SkipInitialScan: true}, text1).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(val).To(BeEquivalentTo("OK"))
|
||||
WaitForIndexing(client, "idx1")
|
||||
|
|
|
@ -2,5 +2,5 @@ package redis
|
|||
|
||||
// Version is the current release version.
|
||||
func Version() string {
|
||||
return "9.6.1"
|
||||
return "9.6.2"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue