mirror of https://github.com/go-redis/redis.git
Support ReJSON resp 2 && Test ReJSON against RESP 2 and 3 && Add complex search and json test
This commit is contained in:
parent
9e3709c404
commit
56bcaaa351
3
go.mod
3
go.mod
|
@ -7,9 +7,10 @@ require (
|
|||
github.com/bsm/gomega v1.27.10
|
||||
github.com/cespare/xxhash/v2 v2.2.0
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
)
|
||||
|
||||
retract (
|
||||
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
||||
v9.5.4 // This version was accidentally released. Please use version 9.6.0 instead.
|
||||
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
||||
)
|
||||
|
|
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, // Setting RESP2 or RESP3 protocol
|
||||
UnstableResp3: true, // Enable RESP3 features
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue