Anthony Kong
Anthony Kong

Reputation: 40624

Is it possible to store a hash to a sorted set in redis?

I want to keep some user feedbacks in redis. Some users may give multiple feedbacks. The users are assigned numerical user id

Here is an example:

zadd feedbacks 1 feedback1 2 feedback2 3 feedback3 1 feedback4

In this case user #1 gave feedbacks feedback1 and feedback4, #2 feedback2 and #3 feedback3.

If I use ZRANGEBYSCORE feedbacks 1 1

I will be able to see the feedback from user #1:

1) "feedback1"
2) "feedback4"

However I want to store more than just the text. I want to be able to retrieve the timestamp for example. Is there any way I can insert a hash vale to the key feedbacks above?

Something likes zadd feedbacks 1 text:feedback1 timestamp:123456

Upvotes: 1

Views: 2172

Answers (1)

LeoMurillo
LeoMurillo

Reputation: 6754

No, you cannot. Lists, sets, hashes and sorted sets only support Redis' string data type for the values.

You can stringify your field-value pairs though, using JSON or your preferred format.

ZADD feedbacks 1 "{\"text\":\"feedback1\",\"timestamp\":\"123456\""

Unless you need to modify a given field atomically, this approach should do.

And even in that case, you can use Lua scripts to achieve server-side JSON manipulation and updates. See How to nest a list into a structure in Redis to reduce top level? for a similar solution.

Secondary indexes

But you may want to query multiple ways: by user id, by timestamp, etc.

In that case, consider using regular keys to store the feedback object, say as a hash.

HSET feedbacks:feedback1 text feedback1 timestamp 123456 user 1 ...

And your indexes:

ZADD feedbacks-by-user 1 feedback1
ZADD feedbacks-by-timestamp 123456 feedback1
...

Say you want all feedbacks of a given user:

ZRANGEBYSCORE feedbacks-by-user 1 1

Then you go get the values for the returned keys. Of course, you may want to avoid the two round trips. Again, Lua script.

The script:

local keys = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[1])
for i, v in ipairs(keys) do
    local k = {}
    k[1] = v
    k[2] = redis.call('HGETALL', 'feedbacks:'..v)
    keys[i] = k
end
return keys

Usage:

> EVAL "local keys = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[1]) \n for i, v in ipairs(keys) do \n     local k = {} \n k[1] = v \n k[2] = redis.call('HGETALL', 'feedbacks:'..v) \n keys[i] = k \n end \n return keys" 1 feedbacks-by-user 1
1) 1) "feedback1"
   2) 1) "text"
      2) "feedback1"
      3) "timestamp"
      4) "123456"
      5) "user"
      6) "1"
2) 1) "feedback4"
   2) 1) "text"
      2) "feedback4"
      3) "timestamp"
      4) "465465"
      5) "user"
      6) "1"

You can similarly query for a range of timestamps.

You can mix and match your queries using ZINTERSTORE or ZUNIONSTORE.

You may be interested in How to store in Redis sorted set with server-side timestamp as score?. You can make a nice Lua script to take care of creating the hash, and the secondary index entries, all in one go with redis-server-side timestamping.

Finally, whenever using Lua on Redis, consider to load the script and use EVALSHA.

Upvotes: 4

Related Questions