How to store sorted set of objects in redis? - redis

I would like to know how to store a list of objects in Redis. That is I have a key like this.
users:pro
{
name: "Bruce", age: "20", score: 100,
name: "Ed", age: "22", score: 80
}
Where I will want to store a list of hashes as value of a particular key. I would like to use the score field as the score field in the sorted set. How could I accomplish this?
I have seen writing a putting a single hash for a key, but what if I want multiple hashes and one of the hash fields must act as a score field for the sorted set?

Using a single key to store all your hashes will require some serialization as Redis doesn't support nested data structures. The result would be the following:
key: users:pro
|
+-----> field value
name:Bruce "age: 20, score: 100"
name:Ed "age: 22, score: 80"
> HMSET users:pro name:Bruce "age: 20, score: 100" name:Ed "age:22, score:80"
The corresponding Sorted Set would be:
key: users:pro.by_scores
|
+---> scores: 80 100
+---> values: "name:Ed" "name:Bruce"
> ZADD users:pro.by_scores 80 "name:Ed" 100 "name:Bruce"
Note 1: this approach mandates a unique ID per-user, currently the name property is used which could be problematic.
Note 2: to avoid the serialization (and deserialization), you can consider using a dedicated key per user. That means doing:
key: users:pro:Bruce
|
+-----> field value
age 20
score 100
key: users:pro:Ed
|
+-----> field value
age 22
score 80
> HMSET users:pro:Bruce age 20 score 100
> HMSET users:pro:Ed age 22 score 80
key: users:pro.by_scores
|
+---> scores: 80 100
+---> values: "users:pro:Ed" "users:pro:Bruce"
> ZADD users:pro.by_scores 80 "users:pro:Ed" 100 "users:pro:Bruce"

Related

redis sets use sort > sorted 4 3 2 12 11 1

mac 10.13
redis 4.0.8
redis-cli
hset info.uesr:1 email ### password ###
hset info.uesr:2 email ### password ###
hset info.uesr:3 email ### password ###
~~~
hset info.uesr:12 email ### password ###
and sadd userList info.user:*
when info.user:9 in to userList
used
sort userList by info.user:* desc
result:
info.user:9
info.user:8
info.user:7
info.user:6
info.user:5
~~~
info.user:1
but now info.user:12 in the list
use
sort userList by info.user:* desc
result:
info.user:9
info.user:8
info.user:7
info.user:6
~~~
info.user:12
info.user:11
info.user:10
info.user:1
i want 12 11 10 9 8 7 6 5 4 3 2 1
how to this?
Redis SORTs lexicographically - you'll have to pad w/ 0's (i.e. "info.user:01") if you insist on going in that direction.
Alternatively, use a Sorted Set for your userList and assign members' scores with the id (with the ZADD command. Then, instead of sorting, just call ZRANGEBYSCORE, or in your case ZREVRANGEBYSCORE.

Redis zrange and zrangebyscore

Are these 2 Redis commands different, except for the second having optional LIMIT argument?
http://redis.io/commands/zrange
http://redis.io/commands/zrangebyscore
They are different:
ZRANGE key start stop ...: start and stop are zero-based indexes (i.e they correspond to a position of an element within the sorted set),
ZRANGEBYSCORE key min max ...: min and max refer to scores (i.e they are used to specify a score range).
So, the first one operates by indexes while the second one (as its name implies) operates by scores. Thus they are used for different purposes.
say, the sorted set is:
value score
tom 0
bob 1
alice 100
lucy 102
when you use zrangebyscores, and the max score is 2, the min score is 0, then, you will get tom and bob;
when you use zrange, and the start is 0, the stop is 2, then you will get tom, bob and alice.
127.0.0.1:6379> zadd example 0 tom 1 bob 100 alice 102 lucy
127.0.0.1:6379> zrange example 0 2 WITHSCORES
1) "tom"
2) "0"
3) "bob"
4) "1"
5) "alice"
6) "100"
127.0.0.1:6379> zrangebyscore example 0 2 WITHSCORES
1) "tom"
2) "0"
3) "bob"
4) "1"
this is the key difference.

Is there a way to return a sorted key list from a hash?

I'm new to redis and reading the documentation I'm not able to find a solution to my problem.
I have a hash with names and phone numbers and I would like to get the a sorted list of the keys in the hash.
So my hash (phonebook) looks like this:
Andrew -> 9999
Sam -> 6666
Eddy -> 5555
If I run hkeys phonebook I get this (keys are returned as they are stored):
Andrew
Sam
Eddy
And I would like to get this (ordered keys):
Andrew
Eddy
Sam
How could I archive this? Am I using the correct data structure?
You can use a sorted set to achieve this, not a hash, and you don't need to maintain a parallel list; it's all contained in a single structure...
Populate the sorted set...
> zadd ss:phonebook 9999 Andrew
> zadd ss:phonebook 4444 Sam
> zadd ss:phonebook 3333 Bob
> zadd ss:phonebook 7777 Maria
> zadd ss:phonebook 8888 Sophia
Since ss:phonebook contains string values (the names), and you want to sort them lexicographically, use the ALPHA modifier:
> SORT ss:phonebook ALPHA
1) "Andrew"
2) "Bob"
3) "Maria"
4) "Sam"
5) "Sophia"
Hope it helps...

REDIS - Get value of multiple keys

How do I get the value of multiple keys from redis using a sorted set?
zadd Users 0 David
zadd Users 5 John
zadd Users 15 Linda
zrevrange Users 0 -1 withscores
This will have two users in it.
How can I retrieve the users with key 'David' and 'Linda' in one query?
You can use Redis MGET
redis> MGET key1 key2 nonexisting
1) "Hello"
2) "World"
3) (nil)
More here
http://redis.io/commands/mget
There are multiple ways to do it without introducing a new command in Redis.
For instance, you can fill a temporary set with the names you are interested in, then calculate the intersection between the temporary set and the zset:
multi
sadd tmp David Linda ... and more ...
zinterstore res 2 tmp Users weights 0 1
zrange res 0 -1 withscores
del tmp res
exec
With pipelining, this will only generate one roundtrip and you can fill an arbitrary number of input parameters in tmp.
With Redis 2.6, you can also wrap these lines into a server-side Lua script to finally get a command accepting an input list and returning the result you want:
eval "redis.call( 'sadd', 'tmp', unpack(KEYS) );
redis.call( 'zinterstore', 'res', 2, 'tmp', 'Users', 'weights', 0, 1 );
local res = redis.call( 'zrange', 'res', 0, -1, 'withscores' );
redis.call( 'del', 'res', 'tmp' ) ;
return res
" 2 David Linda
You can safely assume no new command will be added to Redis if it can easily been implemented using scripting.
One uses a sorted set because you want to deal with items that are sorted. What you are asking for is to not use a sorted set as a sorted set. If you don't care about sort order, then perhaps a sorted set is not what you are looking for. You already can retrieve multiple keys, but not arbitrary ones.
If your primary goal is to retrieve multiple arbitrary keys, use a hash and hmget. If your primary need is to access a sorted set, use sorted set and either go the scripting route or pipeline a series of zscore calls.
You cannot get this with one command. The closest you can do to get it in one response:
MULTI
ZSCORE Users David
ZSCORE Users Linda
EXEC
EDIT: Alternatively, you can maintain a parallel hash with users' scores, and query it with
HMGET UserScores David Linda

Is there MGET analog for Redis hashes?

I'm planning to start using hashes insead of regular keys. But I can't find any information about multi get for hash-keys in Redis wiki. Is this kind of command is supported by Redis?
Thank you.
You can query hashes or any keys in pipeline, i.e. in one request to your redis instance. Actual implementation depends on your client, but with redis-py it'd look like this:
pipe = conn.pipeline()
pipe.hgetall('foo')
pipe.hgetall('bar')
pipe.hgetall('zar')
hash1, hash2, hash3 = pipe.execute()
Client will issue one request with 3 commands. This is the same technique that is used to add multiple values to a set at once.
Read more at http://redis.io/topics/pipelining
No MHGETALL but you can Lua it:
local r = {}
for _, v in pairs(KEYS) do
r[#r+1] = redis.call('HGETALL', v)
end
return r
If SORT let you use multiple GETs with the -> syntax, and all your hashes had the same fields, you could get them in a bulk reply by putting their names into a set and sorting that.
SORT names_of_hashes GET *->field1 *->field2 *->field3 *->etc
But it doesn't look like you can do that with the hash access. Plus you'd have to turn the return list back into hashes yourself.
UPDATE: Redis seems to let you fetch multiple fields if you name your hashes nicely:
redis> hset hash:1 name fish
(integer) 1
redis> hset hash:2 name donkey
(integer) 1
redis> hset hash:3 name horse
(integer) 1
redis> hset hash:1 type fish
(integer) 1
redis> hset hash:2 type mammal
(integer) 1
redis> hset hash:3 type mammal
(integer) 1
redis> sadd animals 1
(integer) 1
redis> sadd animals 2
(integer) 1
redis> sadd animals 3
(integer) 1
redis> sort animals get # get hash:*->name get hash:*->type
1. "1"
2. "fish"
3. "fish"
4. "2"
5. "donkey"
6. "mammal"
7. "3"
8. "horse"
9. "mammal"
There is no command to do it on one shot, but there is a way to do it "nicely", using a list (or sorted set) where you would store you hashKeys, and then retrieve them as bulk using multi.
In PHP:
$redis->zAdd("myHashzSet", 1, "myHashKey:1");
$redis->zAdd("myHashzSet", 2, "myHashKey:2");
$redis->zAdd("myHashzSet", 3, "myHashKey:3");
$members = $redis->zRange("myHashzSet", 0, -1);
$redis->multi();
foreach($members as $hashKey) {
$redis->hGetAll($hashKey);
}
$results = $redis->exec();
I recommand using a sorted set, where you use the score as an ID for your hash, it allows to take advantages of all score based command.
Redis has a HMGET command, which returns the values of several hash keys with one command.