mirror of https://github.com/go-redis/redis.git
819 lines
30 KiB
Go
819 lines
30 KiB
Go
package redis_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
. "github.com/bsm/ginkgo/v2"
|
|
. "github.com/bsm/gomega"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
type JSONGetTestStruct struct {
|
|
Hello string `json:"hello"`
|
|
}
|
|
|
|
var _ = Describe("JSON Commands", Label("json"), func() {
|
|
ctx := context.TODO()
|
|
var client *redis.Client
|
|
|
|
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()
|
|
}
|
|
})
|
|
|
|
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() {
|
|
It("should JSONArrAppend", Label("json.arrappend", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "append2", "$", `{"a": [10], "b": {"a": [12, 13]}}`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONArrAppend(ctx, "append2", "$..a", 10)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal([]int64{2, 3}))
|
|
})
|
|
|
|
It("should JSONArrIndex and JSONArrIndexWithArgs", Label("json.arrindex", "json"), func() {
|
|
cmd1, err := client.JSONSet(ctx, "index1", "$", `{"a": [10], "b": {"a": [12, 10]}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd1).To(Equal("OK"))
|
|
|
|
cmd2, err := client.JSONArrIndex(ctx, "index1", "$.b.a", 10).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd2).To(Equal([]int64{1}))
|
|
|
|
cmd3, err := client.JSONSet(ctx, "index2", "$", `[0,1,2,3,4]`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd3).To(Equal("OK"))
|
|
|
|
res, err := client.JSONArrIndex(ctx, "index2", "$", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res[0]).To(Equal(int64(1)))
|
|
|
|
res, err = client.JSONArrIndex(ctx, "index2", "$", 1, 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res[0]).To(Equal(int64(-1)))
|
|
|
|
res, err = client.JSONArrIndex(ctx, "index2", "$", 4).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res[0]).To(Equal(int64(4)))
|
|
|
|
res, err = client.JSONArrIndexWithArgs(ctx, "index2", "$", &redis.JSONArrIndexArgs{}, 4).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res[0]).To(Equal(int64(4)))
|
|
|
|
stop := 5000
|
|
res, err = client.JSONArrIndexWithArgs(ctx, "index2", "$", &redis.JSONArrIndexArgs{Stop: &stop}, 4).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res[0]).To(Equal(int64(4)))
|
|
|
|
stop = -1
|
|
res, err = client.JSONArrIndexWithArgs(ctx, "index2", "$", &redis.JSONArrIndexArgs{Stop: &stop}, 4).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res[0]).To(Equal(int64(-1)))
|
|
})
|
|
|
|
It("should JSONArrIndex and JSONArrIndexWithArgs with $", Label("json.arrindex", "json"), func() {
|
|
doc := `{
|
|
"store": {
|
|
"book": [
|
|
{
|
|
"category": "reference",
|
|
"author": "Nigel Rees",
|
|
"title": "Sayings of the Century",
|
|
"price": 8.95,
|
|
"size": [10, 20, 30, 40]
|
|
},
|
|
{
|
|
"category": "fiction",
|
|
"author": "Evelyn Waugh",
|
|
"title": "Sword of Honour",
|
|
"price": 12.99,
|
|
"size": [50, 60, 70, 80]
|
|
},
|
|
{
|
|
"category": "fiction",
|
|
"author": "Herman Melville",
|
|
"title": "Moby Dick",
|
|
"isbn": "0-553-21311-3",
|
|
"price": 8.99,
|
|
"size": [5, 10, 20, 30]
|
|
},
|
|
{
|
|
"category": "fiction",
|
|
"author": "J. R. R. Tolkien",
|
|
"title": "The Lord of the Rings",
|
|
"isbn": "0-395-19395-8",
|
|
"price": 22.99,
|
|
"size": [5, 6, 7, 8]
|
|
}
|
|
],
|
|
"bicycle": {"color": "red", "price": 19.95}
|
|
}
|
|
}`
|
|
res, err := client.JSONSet(ctx, "doc1", "$", doc).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
resGet, err := client.JSONGet(ctx, "doc1", "$.store.book[?(@.price<10)].size").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal("[[10,20,30,40],[5,10,20,30]]"))
|
|
|
|
resArr, err := client.JSONArrIndex(ctx, "doc1", "$.store.book[?(@.price<10)].size", 20).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resArr).To(Equal([]int64{1, 2}))
|
|
})
|
|
|
|
It("should JSONArrInsert", Label("json.arrinsert", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "insert2", "$", `[100, 200, 300, 200]`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONArrInsert(ctx, "insert2", "$", -1, 1, 2)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal([]int64{6}))
|
|
|
|
cmd3 := client.JSONGet(ctx, "insert2")
|
|
Expect(cmd3.Err()).NotTo(HaveOccurred())
|
|
// RESP2 vs RESP3
|
|
Expect(cmd3.Val()).To(Or(
|
|
Equal(`[100,200,300,1,2,200]`),
|
|
Equal(`[[100,200,300,1,2,200]]`)))
|
|
})
|
|
|
|
It("should JSONArrLen", Label("json.arrlen", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "length2", "$", `{"a": [10], "b": {"a": [12, 10, 20, 12, 90, 10]}}`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONArrLen(ctx, "length2", "$..a")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal([]int64{1, 6}))
|
|
})
|
|
|
|
It("should JSONArrPop", Label("json.arrpop"), func() {
|
|
cmd1 := client.JSONSet(ctx, "pop4", "$", `[100, 200, 300, 200]`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONArrPop(ctx, "pop4", "$", 2)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal([]string{"300"}))
|
|
|
|
cmd3 := client.JSONGet(ctx, "pop4", "$")
|
|
Expect(cmd3.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd3.Val()).To(Equal("[[100,200,200]]"))
|
|
})
|
|
|
|
It("should JSONArrTrim", Label("json.arrtrim", "json"), func() {
|
|
cmd1, err := client.JSONSet(ctx, "trim1", "$", `[0,1,2,3,4]`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd1).To(Equal("OK"))
|
|
|
|
stop := 3
|
|
cmd2, err := client.JSONArrTrimWithArgs(ctx, "trim1", "$", &redis.JSONArrTrimArgs{Start: 1, Stop: &stop}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd2).To(Equal([]int64{3}))
|
|
|
|
res, err := client.JSONGet(ctx, "trim1", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal(`[[1,2,3]]`))
|
|
|
|
cmd3, err := client.JSONSet(ctx, "trim2", "$", `[0,1,2,3,4]`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd3).To(Equal("OK"))
|
|
|
|
stop = 3
|
|
cmd4, err := client.JSONArrTrimWithArgs(ctx, "trim2", "$", &redis.JSONArrTrimArgs{Start: -1, Stop: &stop}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd4).To(Equal([]int64{0}))
|
|
|
|
cmd5, err := client.JSONSet(ctx, "trim3", "$", `[0,1,2,3,4]`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd5).To(Equal("OK"))
|
|
|
|
stop = 99
|
|
cmd6, err := client.JSONArrTrimWithArgs(ctx, "trim3", "$", &redis.JSONArrTrimArgs{Start: 3, Stop: &stop}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd6).To(Equal([]int64{2}))
|
|
|
|
cmd7, err := client.JSONSet(ctx, "trim4", "$", `[0,1,2,3,4]`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd7).To(Equal("OK"))
|
|
|
|
stop = 1
|
|
cmd8, err := client.JSONArrTrimWithArgs(ctx, "trim4", "$", &redis.JSONArrTrimArgs{Start: 9, Stop: &stop}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd8).To(Equal([]int64{0}))
|
|
|
|
cmd9, err := client.JSONSet(ctx, "trim5", "$", `[0,1,2,3,4]`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd9).To(Equal("OK"))
|
|
|
|
stop = 11
|
|
cmd10, err := client.JSONArrTrimWithArgs(ctx, "trim5", "$", &redis.JSONArrTrimArgs{Start: 9, Stop: &stop}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd10).To(Equal([]int64{0}))
|
|
})
|
|
|
|
It("should JSONArrPop", Label("json.arrpop", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "pop4", "$", `[100, 200, 300, 200]`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONArrPop(ctx, "pop4", "$", 2)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal([]string{"300"}))
|
|
|
|
cmd3 := client.JSONGet(ctx, "pop4", "$")
|
|
Expect(cmd3.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd3.Val()).To(Equal("[[100,200,200]]"))
|
|
})
|
|
})
|
|
|
|
Describe("get/set", Label("getset"), func() {
|
|
It("should JSONSet", Label("json.set", "json"), func() {
|
|
cmd := client.JSONSet(ctx, "set1", "$", `{"a": 1, "b": 2, "hello": "world"}`)
|
|
Expect(cmd.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd.Val()).To(Equal("OK"))
|
|
})
|
|
|
|
It("should JSONGet", Label("json.get", "json", "NonRedisEnterprise"), func() {
|
|
res, err := client.JSONSet(ctx, "get3", "$", `{"a": 1, "b": 2}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
res, err = client.JSONGetWithArgs(ctx, "get3", &redis.JSONGetArgs{Indent: "-"}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal(`{-"a":1,-"b":2}`))
|
|
|
|
res, err = client.JSONGetWithArgs(ctx, "get3", &redis.JSONGetArgs{Indent: "-", Newline: `~`, Space: `!`}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal(`{~-"a":!1,~-"b":!2~}`))
|
|
})
|
|
|
|
It("should JSONMerge", Label("json.merge", "json"), func() {
|
|
res, err := client.JSONSet(ctx, "merge1", "$", `{"a": 1, "b": 2}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
res, err = client.JSONMerge(ctx, "merge1", "$", `{"b": 3, "c": 4}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
res, err = client.JSONGet(ctx, "merge1", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal(`[{"a":1,"b":3,"c":4}]`))
|
|
})
|
|
|
|
It("should JSONMSet", Label("json.mset", "json", "NonRedisEnterprise"), func() {
|
|
doc1 := redis.JSONSetArgs{Key: "mset1", Path: "$", Value: `{"a": 1}`}
|
|
doc2 := redis.JSONSetArgs{Key: "mset2", Path: "$", Value: 2}
|
|
docs := []redis.JSONSetArgs{doc1, doc2}
|
|
|
|
mSetResult, err := client.JSONMSetArgs(ctx, docs).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(mSetResult).To(Equal("OK"))
|
|
|
|
res, err := client.JSONMGet(ctx, "$", "mset1").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal([]interface{}{`[{"a":1}]`}))
|
|
|
|
res, err = client.JSONMGet(ctx, "$", "mset1", "mset2").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal([]interface{}{`[{"a":1}]`, "[2]"}))
|
|
|
|
_, err = client.JSONMSet(ctx, "mset1", "$.a", 2, "mset3", "$", `[1]`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("should JSONMGet", Label("json.mget", "json", "NonRedisEnterprise"), func() {
|
|
cmd1 := client.JSONSet(ctx, "mget2a", "$", `{"a": ["aa", "ab", "ac", "ad"], "b": {"a": ["ba", "bb", "bc", "bd"]}}`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
cmd2 := client.JSONSet(ctx, "mget2b", "$", `{"a": [100, 200, 300, 200], "b": {"a": [100, 200, 300, 200]}}`)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal("OK"))
|
|
|
|
cmd3 := client.JSONMGet(ctx, "$..a", "mget2a", "mget2b")
|
|
Expect(cmd3.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd3.Val()).To(HaveLen(2))
|
|
Expect(cmd3.Val()[0]).To(Equal(`[["aa","ab","ac","ad"],["ba","bb","bc","bd"]]`))
|
|
Expect(cmd3.Val()[1]).To(Equal(`[[100,200,300,200],[100,200,300,200]]`))
|
|
})
|
|
|
|
It("should JSONMget with $", Label("json.mget", "json", "NonRedisEnterprise"), func() {
|
|
res, err := client.JSONSet(ctx, "doc1", "$", `{"a": 1, "b": 2, "nested": {"a": 3}, "c": "", "nested2": {"a": ""}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
res, err = client.JSONSet(ctx, "doc2", "$", `{"a": 4, "b": 5, "nested": {"a": 6}, "c": "", "nested2": {"a": [""]}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err := client.JSONMGet(ctx, "$..a", "doc1").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal([]interface{}{`[1,3,""]`}))
|
|
|
|
iRes, err = client.JSONMGet(ctx, "$..a", "doc1", "doc2").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal([]interface{}{`[1,3,""]`, `[4,6,[""]]`}))
|
|
|
|
iRes, err = client.JSONMGet(ctx, "$..a", "non_existing_doc", "non_existing_doc1").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal([]interface{}{nil, nil}))
|
|
})
|
|
})
|
|
|
|
Describe("Misc", Label("misc"), func() {
|
|
It("should JSONClear", Label("json.clear", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "clear1", "$", `[1]`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONClear(ctx, "clear1", "$")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal(int64(1)))
|
|
|
|
cmd3 := client.JSONGet(ctx, "clear1", "$")
|
|
Expect(cmd3.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd3.Val()).To(Equal(`[[]]`))
|
|
})
|
|
|
|
It("should JSONClear with $", Label("json.clear", "json"), func() {
|
|
doc := `{
|
|
"nested1": {"a": {"foo": 10, "bar": 20}},
|
|
"a": ["foo"],
|
|
"nested2": {"a": "claro"},
|
|
"nested3": {"a": {"baz": 50}}
|
|
}`
|
|
res, err := client.JSONSet(ctx, "doc1", "$", doc).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err := client.JSONClear(ctx, "doc1", "$..a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal(int64(3)))
|
|
|
|
resGet, err := client.JSONGet(ctx, "doc1", `$`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal(`[{"nested1":{"a":{}},"a":[],"nested2":{"a":"claro"},"nested3":{"a":{}}}]`))
|
|
|
|
res, err = client.JSONSet(ctx, "doc1", "$", doc).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err = client.JSONClear(ctx, "doc1", "$.nested1.a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal(int64(1)))
|
|
|
|
resGet, err = client.JSONGet(ctx, "doc1", `$`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal(`[{"nested1":{"a":{}},"a":["foo"],"nested2":{"a":"claro"},"nested3":{"a":{"baz":50}}}]`))
|
|
})
|
|
|
|
It("should JSONDel", Label("json.del", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "del1", "$", `[1]`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONDel(ctx, "del1", "$")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal(int64(1)))
|
|
|
|
cmd3 := client.JSONGet(ctx, "del1", "$")
|
|
Expect(cmd3.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd3.Val()).To(HaveLen(0))
|
|
})
|
|
|
|
It("should JSONDel with $", Label("json.del", "json"), func() {
|
|
res, err := client.JSONSet(ctx, "del1", "$", `{"a": 1, "nested": {"a": 2, "b": 3}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err := client.JSONDel(ctx, "del1", "$..a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal(int64(2)))
|
|
|
|
resGet, err := client.JSONGet(ctx, "del1", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal(`[{"nested":{"b":3}}]`))
|
|
|
|
res, err = client.JSONSet(ctx, "del2", "$", `{"a": {"a": 2, "b": 3}, "b": ["a", "b"], "nested": {"b": [true, "a", "b"]}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err = client.JSONDel(ctx, "del2", "$..a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal(int64(1)))
|
|
|
|
resGet, err = client.JSONGet(ctx, "del2", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal(`[{"nested":{"b":[true,"a","b"]},"b":["a","b"]}]`))
|
|
|
|
doc := `[
|
|
{
|
|
"ciao": ["non ancora"],
|
|
"nested": [
|
|
{"ciao": [1, "a"]},
|
|
{"ciao": [2, "a"]},
|
|
{"ciaoc": [3, "non", "ciao"]},
|
|
{"ciao": [4, "a"]},
|
|
{"e": [5, "non", "ciao"]}
|
|
]
|
|
}
|
|
]`
|
|
res, err = client.JSONSet(ctx, "del3", "$", doc).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err = client.JSONDel(ctx, "del3", `$.[0]["nested"]..ciao`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal(int64(3)))
|
|
|
|
resVal := `[[{"ciao":["non ancora"],"nested":[{},{},{"ciaoc":[3,"non","ciao"]},{},{"e":[5,"non","ciao"]}]}]]`
|
|
resGet, err = client.JSONGet(ctx, "del3", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal(resVal))
|
|
})
|
|
|
|
It("should JSONForget", Label("json.forget", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "forget3", "$", `{"a": [1,2,3], "b": {"a": [1,2,3], "b": "annie"}}`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONForget(ctx, "forget3", "$..a")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal(int64(2)))
|
|
|
|
cmd3 := client.JSONGet(ctx, "forget3", "$")
|
|
Expect(cmd3.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd3.Val()).To(Equal(`[{"b":{"b":"annie"}}]`))
|
|
})
|
|
|
|
It("should JSONForget with $", Label("json.forget", "json"), func() {
|
|
res, err := client.JSONSet(ctx, "doc1", "$", `{"a": 1, "nested": {"a": 2, "b": 3}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err := client.JSONForget(ctx, "doc1", "$..a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal(int64(2)))
|
|
|
|
resGet, err := client.JSONGet(ctx, "doc1", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal(`[{"nested":{"b":3}}]`))
|
|
|
|
res, err = client.JSONSet(ctx, "doc2", "$", `{"a": {"a": 2, "b": 3}, "b": ["a", "b"], "nested": {"b": [true, "a", "b"]}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err = client.JSONForget(ctx, "doc2", "$..a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal(int64(1)))
|
|
|
|
resGet, err = client.JSONGet(ctx, "doc2", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal(`[{"nested":{"b":[true,"a","b"]},"b":["a","b"]}]`))
|
|
|
|
doc := `[
|
|
{
|
|
"ciao": ["non ancora"],
|
|
"nested": [
|
|
{"ciao": [1, "a"]},
|
|
{"ciao": [2, "a"]},
|
|
{"ciaoc": [3, "non", "ciao"]},
|
|
{"ciao": [4, "a"]},
|
|
{"e": [5, "non", "ciao"]}
|
|
]
|
|
}
|
|
]`
|
|
res, err = client.JSONSet(ctx, "doc3", "$", doc).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
iRes, err = client.JSONForget(ctx, "doc3", `$.[0]["nested"]..ciao`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(iRes).To(Equal(int64(3)))
|
|
|
|
resVal := `[[{"ciao":["non ancora"],"nested":[{},{},{"ciaoc":[3,"non","ciao"]},{},{"e":[5,"non","ciao"]}]}]]`
|
|
resGet, err = client.JSONGet(ctx, "doc3", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resGet).To(Equal(resVal))
|
|
})
|
|
|
|
It("should JSONNumIncrBy", Label("json.numincrby", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "incr3", "$", `{"a": [1, 2], "b": {"a": [0, -1]}}`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONNumIncrBy(ctx, "incr3", "$..a[1]", float64(1))
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(Equal(`[3,0]`))
|
|
})
|
|
|
|
It("should JSONNumIncrBy with $", Label("json.numincrby", "json"), func() {
|
|
res, err := client.JSONSet(ctx, "doc1", "$", `{"a": "b", "b": [{"a": 2}, {"a": 5.0}, {"a": "c"}]}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
res, err = client.JSONNumIncrBy(ctx, "doc1", "$.b[1].a", 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal(`[7]`))
|
|
|
|
res, err = client.JSONNumIncrBy(ctx, "doc1", "$.b[1].a", 3.5).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal(`[10.5]`))
|
|
|
|
res, err = client.JSONSet(ctx, "doc2", "$", `{"a": "b", "b": [{"a": 2}, {"a": 5.0}, {"a": "c"}]}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
res, err = client.JSONNumIncrBy(ctx, "doc2", "$.b[0].a", 3).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal(`[5]`))
|
|
})
|
|
|
|
It("should JSONObjKeys", Label("json.objkeys", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "objkeys1", "$", `{"a": [1, 2], "b": {"a": [0, -1]}}`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONObjKeys(ctx, "objkeys1", "$..*")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(HaveLen(7))
|
|
Expect(cmd2.Val()).To(Equal([]interface{}{nil, []interface{}{"a"}, nil, nil, nil, nil, nil}))
|
|
})
|
|
|
|
It("should JSONObjKeys with $", Label("json.objkeys", "json"), func() {
|
|
doc := `{
|
|
"nested1": {"a": {"foo": 10, "bar": 20}},
|
|
"a": ["foo"],
|
|
"nested2": {"a": {"baz": 50}}
|
|
}`
|
|
cmd1, err := client.JSONSet(ctx, "objkeys1", "$", doc).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd1).To(Equal("OK"))
|
|
|
|
cmd2, err := client.JSONObjKeys(ctx, "objkeys1", "$.nested1.a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd2).To(Equal([]interface{}{[]interface{}{"foo", "bar"}}))
|
|
|
|
cmd2, err = client.JSONObjKeys(ctx, "objkeys1", ".*.a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd2).To(Equal([]interface{}{"foo", "bar"}))
|
|
|
|
cmd2, err = client.JSONObjKeys(ctx, "objkeys1", ".nested2.a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd2).To(Equal([]interface{}{"baz"}))
|
|
|
|
_, err = client.JSONObjKeys(ctx, "non_existing_doc", "..a").Result()
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
|
|
It("should JSONObjLen", Label("json.objlen", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "objlen2", "$", `{"a": [1, 2], "b": {"a": [0, -1]}}`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONObjLen(ctx, "objlen2", "$..*")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(HaveLen(7))
|
|
Expect(cmd2.Val()[0]).To(BeNil())
|
|
Expect(*cmd2.Val()[1]).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("should JSONStrLen", Label("json.strlen", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "strlen2", "$", `{"a": "alice", "b": "bob", "c": {"a": "alice", "b": "bob"}}`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONStrLen(ctx, "strlen2", "$..*")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(HaveLen(5))
|
|
var tmp int64 = 20
|
|
Expect(cmd2.Val()[0]).To(BeAssignableToTypeOf(&tmp))
|
|
Expect(*cmd2.Val()[0]).To(Equal(int64(5)))
|
|
Expect(*cmd2.Val()[1]).To(Equal(int64(3)))
|
|
Expect(cmd2.Val()[2]).To(BeNil())
|
|
Expect(*cmd2.Val()[3]).To(Equal(int64(5)))
|
|
Expect(*cmd2.Val()[4]).To(Equal(int64(3)))
|
|
})
|
|
|
|
It("should JSONStrAppend", Label("json.strappend", "json"), func() {
|
|
cmd1, err := client.JSONSet(ctx, "strapp1", "$", `"foo"`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd1).To(Equal("OK"))
|
|
cmd2, err := client.JSONStrAppend(ctx, "strapp1", "$", `"bar"`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(*cmd2[0]).To(Equal(int64(6)))
|
|
cmd3, err := client.JSONGet(ctx, "strapp1", "$").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cmd3).To(Equal(`["foobar"]`))
|
|
})
|
|
|
|
It("should JSONStrAppend and JSONStrLen with $", Label("json.strappend", "json.strlen", "json"), func() {
|
|
res, err := client.JSONSet(ctx, "doc1", "$", `{"a": "foo", "nested1": {"a": "hello"}, "nested2": {"a": 31}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
intArrayResult, err := client.JSONStrAppend(ctx, "doc1", "$.nested1.a", `"baz"`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(*intArrayResult[0]).To(Equal(int64(8)))
|
|
|
|
res, err = client.JSONSet(ctx, "doc2", "$", `{"a": "foo", "nested1": {"a": "hello"}, "nested2": {"a": 31}}`).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(res).To(Equal("OK"))
|
|
|
|
intResult, err := client.JSONStrLen(ctx, "doc2", "$.nested1.a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(*intResult[0]).To(Equal(int64(5)))
|
|
})
|
|
|
|
It("should JSONToggle", Label("json.toggle", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "toggle1", "$", `[true]`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONToggle(ctx, "toggle1", "$[0]")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(HaveLen(1))
|
|
Expect(*cmd2.Val()[0]).To(Equal(int64(0)))
|
|
})
|
|
|
|
It("should JSONType", Label("json.type", "json"), func() {
|
|
cmd1 := client.JSONSet(ctx, "type1", "$", `[true]`)
|
|
Expect(cmd1.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd1.Val()).To(Equal("OK"))
|
|
|
|
cmd2 := client.JSONType(ctx, "type1", "$[0]")
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
Expect(cmd2.Val()).To(HaveLen(1))
|
|
// RESP2 v RESP3
|
|
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)
|
|
}
|