Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command Migration: JSON. ARRAPPEND, ARRPOP, ARRLEN #1062

Merged
merged 24 commits into from
Oct 27, 2024
Merged
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
36b70a9
JSON ARRAPPEN ARRLEN ARRPOP refractor
srivastava-yash Oct 10, 2024
b429543
commands meta update
srivastava-yash Oct 10, 2024
fc3c00a
minor changes
srivastava-yash Oct 11, 2024
e64303e
ARRAPPEND, ARRLEN, ARRPOP unittests migrated
srivastava-yash Oct 11, 2024
a5b533d
minor addition
srivastava-yash Oct 13, 2024
95b8376
Json.arrpop integration test fix
srivastava-yash Oct 13, 2024
8bf907e
substituting nil with clientio.NIL
srivastava-yash Oct 16, 2024
c6ff6f1
unit tests update
srivastava-yash Oct 16, 2024
9a5d306
Merge branch 'master' into cmd-migration/json-arr
srivastava-yash Oct 18, 2024
578d2af
merge issues resolved
srivastava-yash Oct 18, 2024
ccfa8c9
integration tests added to RESP
srivastava-yash Oct 18, 2024
51d8c30
Documentation update
srivastava-yash Oct 18, 2024
50feddd
Merge branch 'master' into cmd-migration/json-arr
srivastava-yash Oct 20, 2024
f4ea388
Merge branch 'master' into cmd-migration/json-arr
srivastava-yash Oct 21, 2024
2e4ad38
merge error fix
srivastava-yash Oct 21, 2024
4aa734d
JSON.ARRPOP integration tests fixed
srivastava-yash Oct 21, 2024
7593147
review comments
srivastava-yash Oct 24, 2024
5ed8711
Merge branch 'master' into cmd-migration/json-arr
srivastava-yash Oct 24, 2024
b384f74
documentation review comments
srivastava-yash Oct 25, 2024
7917c09
minor refractor
srivastava-yash Oct 25, 2024
309fd9e
docs update
srivastava-yash Oct 25, 2024
d92391c
websocket integration tests
srivastava-yash Oct 25, 2024
e433b4a
Merge branch 'master' into cmd-migration/json-arr
srivastava-yash Oct 25, 2024
557d98e
Removing WS tests
lucifercr07 Oct 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Merge branch 'master' into cmd-migration/json-arr
  • Loading branch information
srivastava-yash authored Oct 24, 2024
commit 5ed871175854e167e533fe9b937dce861e987e45
288 changes: 287 additions & 1 deletion internal/eval/store_eval.go
Original file line number Diff line number Diff line change
@@ -1647,6 +1647,292 @@ func evalZPOPMIN(args []string, store *dstore.Store) *EvalResponse {
}
}

// evalHLEN returns the number of fields contained in the hash stored at key.
//
// If key doesn't exist, it returns 0.
//
// Usage: HLEN key
func evalHLEN(args []string, store *dstore.Store) *EvalResponse {
if len(args) != 1 {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongArgumentCount("HLEN"),
}
}

key := args[0]

obj := store.Get(key)

if obj == nil {
return &EvalResponse{
Result: clientio.IntegerZero,
Error: nil,
}
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongTypeOperation,
}
}

hashMap := obj.Value.(HashMap)
return &EvalResponse{
Result: len(hashMap),
Error: nil,
}
}

// evalHSTRLEN returns the length of value associated with field in the hash stored at key.
//
// This command returns 0, if the specified field doesn't exist in the key
//
// If key doesn't exist, it returns 0.
//
// Usage: HSTRLEN key field value
func evalHSTRLEN(args []string, store *dstore.Store) *EvalResponse {
if len(args) != 2 {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongArgumentCount("HSTRLEN"),
}
}

key := args[0]
hmKey := args[1]
obj := store.Get(key)

var hashMap HashMap

if obj != nil {
if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongTypeOperation,
}
}
hashMap = obj.Value.(HashMap)
} else {
return &EvalResponse{
Result: clientio.IntegerZero,
Error: nil,
}
}

val, ok := hashMap.Get(hmKey)
// Return 0, if specified field doesn't exist in the HashMap.
if ok {
return &EvalResponse{
Result: len(*val),
Error: nil,
}
}
return &EvalResponse{
Result: clientio.IntegerZero,
Error: nil,
}
}

// evalHSCAN return a two element multi-bulk reply, where the first element is a string representing the cursor,
// and the second element is a multi-bulk with an array of elements.
//
// The array of elements contain two elements, a field and a value, for every returned element of the Hash.
//
// If key doesn't exist, it returns an array containing 0 and empty array.
//
// Usage: HSCAN key cursor [MATCH pattern] [COUNT count]
func evalHSCAN(args []string, store *dstore.Store) *EvalResponse {
if len(args) < 2 {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongArgumentCount("HSCAN"),
}
}

key := args[0]
cursor, err := strconv.ParseInt(args[1], 10, 64)
if err != nil {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrIntegerOutOfRange,
}
}

obj := store.Get(key)
if obj == nil {
return &EvalResponse{
Result: []interface{}{"0", []string{}},
Error: nil,
}
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongTypeOperation,
}
}

hashMap := obj.Value.(HashMap)
pattern := "*"
count := 10

// Parse optional arguments
for i := 2; i < len(args); i += 2 {
switch strings.ToUpper(args[i]) {
case "MATCH":
if i+1 < len(args) {
pattern = args[i+1]
}
case CountConst:
if i+1 < len(args) {
parsedCount, err := strconv.Atoi(args[i+1])
if err != nil || parsedCount < 1 {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrIntegerOutOfRange,
}
}
count = parsedCount
}
}
}

// Note that this implementation has a time complexity of O(N), where N is the number of keys in 'hashMap'.
// This is in contrast to Redis, which implements HSCAN in O(1) time complexity by maintaining a cursor.
keys := make([]string, 0, len(hashMap))
for k := range hashMap {
keys = append(keys, k)
}
sort.Strings(keys)

matched := 0
results := make([]string, 0, count*2)
newCursor := 0

g, err := glob.Compile(pattern)
if err != nil {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrGeneral(fmt.Sprintf("Invalid glob pattern: %s", err)),
}
}

// Scan the keys and add them to the results if they match the pattern
for i := int(cursor); i < len(keys); i++ {
if g.Match(keys[i]) {
results = append(results, keys[i], hashMap[keys[i]])
matched++
if matched >= count {
newCursor = i + 1
break
}
}
}

// If we've scanned all keys, reset cursor to 0
if newCursor >= len(keys) {
newCursor = 0
}

return &EvalResponse{
Result: []interface{}{strconv.Itoa(newCursor), results},
Error: nil,
}
}

// evalBF.RESERVE evaluates the BF.RESERVE command responsible for initializing a
// new bloom filter and allocation it's relevant parameters based on given inputs.
// If no params are provided, it uses defaults.
func evalBFRESERVE(args []string, store *dstore.Store) *EvalResponse {
if len(args) < 3 {
return makeEvalError(diceerrors.ErrWrongArgumentCount("BF.RESERVE"))
}

opts, err := newBloomOpts(args[1:])
if err != nil {
return makeEvalError(err)
}

_, err = CreateBloomFilter(args[0], store, opts)
if err != nil {
return makeEvalError(err)
}
return makeEvalResult(clientio.OK)
}

// evalBFADD evaluates the BF.ADD command responsible for adding an element to a bloom filter. If the filter does not
// exist, it will create a new one with default parameters.
func evalBFADD(args []string, store *dstore.Store) *EvalResponse {
if len(args) != 2 {
return makeEvalError(diceerrors.ErrWrongArgumentCount("BF.ADD"))
}

bloom, err := getOrCreateBloomFilter(args[0], store, nil)
if err != nil {
return makeEvalError(err)
}

result, err := bloom.add(args[1])
if err != nil {
return makeEvalError(err)
}

return makeEvalResult(result)
}

// evalBFEXISTS evaluates the BF.EXISTS command responsible for checking existence of an element in a bloom filter.
func evalBFEXISTS(args []string, store *dstore.Store) *EvalResponse {
// todo must work with objects of
if len(args) != 2 {
return makeEvalError(diceerrors.ErrWrongArgumentCount("BF.EXISTS"))
}

bloom, err := GetBloomFilter(args[0], store)
if err != nil {
return makeEvalError(err)
}
if bloom == nil {
return makeEvalResult(clientio.IntegerZero)
}
result, err := bloom.exists(args[1])
if err != nil {
return makeEvalError(err)
}
return makeEvalResult(result)
}

// evalBFINFO evaluates the BF.INFO command responsible for returning the
// parameters and metadata of an existing bloom filter.
func evalBFINFO(args []string, store *dstore.Store) *EvalResponse {
if len(args) < 1 || len(args) > 2 {
return makeEvalError(diceerrors.ErrWrongArgumentCount("BF.INFO"))
}

bloom, err := GetBloomFilter(args[0], store)

if err != nil {
return makeEvalError(err)
}

if bloom == nil {
return makeEvalError(diceerrors.ErrGeneral("not found"))
}
opt := ""
if len(args) == 2 {
opt = args[1]
}
result, err := bloom.info(opt)

if err != nil {
return makeEvalError(err)
}

return makeEvalResult(result)
}

// evalJSONARRAPPEND appends the value(s) provided in the args to the given array path
// in the JSON object saved at key in arguments.
// Args must contain atleast a key, path and value.
@@ -2009,4 +2295,4 @@ func evalJSONARRPOP(args []string, store *dstore.Store) *EvalResponse {
Result: popArr,
Error: nil,
}
}
}
You are viewing a condensed version of this merge commit. You can view the full changes here.