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/bsm/gomega v1.27.10
|
||||||
github.com/cespare/xxhash/v2 v2.2.0
|
github.com/cespare/xxhash/v2 v2.2.0
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
|
||||||
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
)
|
)
|
||||||
|
|
||||||
retract (
|
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.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 {
|
type JSONCmd struct {
|
||||||
baseCmd
|
baseCmd
|
||||||
val string
|
val string
|
||||||
expanded []interface{}
|
expanded interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Cmder = (*JSONCmd)(nil)
|
var _ Cmder = (*JSONCmd)(nil)
|
||||||
|
@ -100,11 +100,11 @@ func (cmd *JSONCmd) Result() (string, error) {
|
||||||
return cmd.Val(), cmd.Err()
|
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 {
|
if len(cmd.val) != 0 && cmd.expanded == nil {
|
||||||
err := json.Unmarshal([]byte(cmd.val), &cmd.expanded)
|
err := json.Unmarshal([]byte(cmd.val), &cmd.expanded)
|
||||||
if err != nil {
|
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.
|
// 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 {
|
func (c cmdable) JSONNumIncrBy(ctx context.Context, key, path string, value float64) *JSONCmd {
|
||||||
args := []interface{}{"JSON.NUMINCRBY", key, path, value}
|
args := []interface{}{"JSON.NUMINCRBY", key, path, value}
|
||||||
cmd := newJSONCmd(ctx, args...)
|
cmd := newJSONCmd(ctx, args...)
|
||||||
|
|
166
json_test.go
166
json_test.go
|
@ -2,6 +2,8 @@ package redis_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
. "github.com/bsm/ginkgo/v2"
|
. "github.com/bsm/ginkgo/v2"
|
||||||
. "github.com/bsm/gomega"
|
. "github.com/bsm/gomega"
|
||||||
|
@ -17,13 +19,27 @@ var _ = Describe("JSON Commands", Label("json"), func() {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
var client *redis.Client
|
var client *redis.Client
|
||||||
|
|
||||||
BeforeEach(func() {
|
setupRedisClient := func(protocolVersion int) *redis.Client {
|
||||||
client = redis.NewClient(&redis.Options{Addr: ":6379"})
|
return redis.NewClient(&redis.Options{
|
||||||
Expect(client.FlushAll(ctx).Err()).NotTo(HaveOccurred())
|
Addr: "localhost:6379",
|
||||||
|
DB: 0,
|
||||||
|
Protocol: protocolVersion, // Setting RESP2 or RESP3 protocol
|
||||||
|
UnstableResp3: true, // Enable RESP3 features
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
AfterEach(func() {
|
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() {
|
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")))
|
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