Numerical operation in Redis sort - redis

As in the doc http://redis.io/commands/sort
SORT mylist BY weight_*
What I would want to do is something like
SORT mylist BY (weight_* + vote_*)
Is that possible to do this just by Redis?

You can use Lua to build each sum_* key and sort by those:
redis 127.0.0.1:6379> sadd myset 1 2 3
(integer) 0
redis 127.0.0.1:6379> mset weight_1 1 weight_2 2 weight_3 3
OK
redis 127.0.0.1:6379> mset vote_1 1 vote_2 2 vote_3 0
OK
redis 127.0.0.1:6379> eval "for i in ipairs(redis.call('smembers', KEYS[1])) do redis.call('set', 'sum_' .. i, redis.call('get','weight_' .. i) + redis.call('get', 'vote_' .. i)) end;" 1 myset
(nil)
redis 127.0.0.1:6379> sort myset by sum_*
1) "1"
2) "3"
3) "2"

Related

Redis ZADD: update only when score is lower

I need to store the lowest score for each key I add to the set, but when I do ZADD, Redis overwrites the score with the new value even if the score is higher.
ZADD myorderset 1 'one' 2 'two' 3 'three'
(integer) 3
ZRANGE myorderset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
ZADD myorderset 5 'three'
(integer) 0
ZRANGE myorderset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "5"
In the example case, I need the key 'three' not be updated since the new score is higher (5) than the existing (3). Is there a way to do this natively or do I need to create a script in Lua?
I've been researching ZADD modifiers (XX, NX, CH) but none of them do what I need.
Thank you very much!
A Lua script for this CAS use case would be the simplest and idiomatic solution:
127.0.0.1:6379> ZADD myorderset 1 'one' 2 'two' 3 'three'
(integer) 3
127.0.0.1:6379> EVAL "local s = redis.call('ZSCORE', KEYS[1], ARGV[2]) if not s or s > ARGV[1] then redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2]) end" 1 myorderset 5 'three'
(nil)
127.0.0.1:6379> ZRANGE myorderset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
127.0.0.1:6379> EVAL "local s = redis.call('ZSCORE', KEYS[1], ARGV[2]) if not s or s > ARGV[1] then redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2]) end" 1 myorderset 2 'three'
(nil)
127.0.0.1:6379> ZRANGE myorderset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "three"
4) "2"
5) "two"
6) "2"
There is no single command or command option to do it both. You can either use combination of ZSCORE with ZADD(in lua). Alternatively("It is/looks like over-engineered") you may use ZUNIONSTORE with the aggregate option MIN.
With the AGGREGATE option, it is possible to specify how the results of the union are aggregated. This option defaults to SUM, where the score of an element is summed across the inputs where it exists. When this option is set to either MIN or MAX, the resulting set will contain the minimum or maximum score of an element across the inputs where it exists.
127.0.0.1:6379> ZADD base 1 a 2 b 5 c
(integer) 3
127.0.0.1:6379> ZADD new 3 c
(integer) 1
127.0.0.1:6379> ZUNIONSTORE base 2 base new AGGREGATE MIN
(integer) 3
127.0.0.1:6379> DEL new
(integer) 1
127.0.0.1:6379> ZRANGE base 0 -1 WITHSCORES
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
127.0.0.1:6379> ZADD new 5 b
(integer) 1
127.0.0.1:6379> ZUNIONSTORE base 2 base new AGGREGATE MIN
(integer) 3
127.0.0.1:6379> DEL new
(integer) 1
127.0.0.1:6379> ZRANGE base 0 -1 WITHSCORES
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
127.0.0.1:6379>
If you prefer,
You may generate new set name with random string in application level
Put EXPIRE to this new set, no need to DEL the new key manually after the ZUNIONSTORE, it will be expired eventually.
It can be done in MULTI/EXEC in a single transaction.

How to combine Redis commands 'expire' and 'sadd' into one command?

I need to create a set in Redis:
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SADD myset "World"
(integer) 0
redis> SMEMBERS myset
1) "World"
2) "Hello"
But I need to set expire time for the key myset.
In other words I need a command kind of expire sadd myset... (like SETEX for string values).
Is there any way to execute these commands per one request to Redis server?
There is no built-in command to do this. What you may do is; using transactions. As it is stated in the documentation;
All the commands in a transaction are serialized and executed sequentially. It can never happen that a request issued by another client is served in the middle of the execution of a Redis transaction. This guarantees that the commands are executed as a single isolated operation.
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SADD mynewset a b c d e f g
QUEUED
127.0.0.1:6379> SADD mynewset f g h j k l
QUEUED
127.0.0.1:6379> EXPIRE mynewset 86400
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 7
2) (integer) 4
3) (integer) 1
127.0.0.1:6379> TTL mynewset
(integer) 86394
127.0.0.1:6379>
There is also the possibility of using a Lua script to tie the two commands together:
127.0.0.1:6379> EVAL "redis.call('SADD', KEYS[1], unpack(ARGV)) redis.call('EXPIRE', KEYS[1], 3600)" 1 myset a b c d e
(nil)
127.0.0.1:6379> SMEMBERS myset
1) "c"
2) "d"
3) "a"
4) "b"
5) "e"
127.0.0.1:6379> TTL myset
(integer) 3596

Reset value for key in redis

I am storing some values in redis like for key: 1 the value will be
{"counter":1,"counter1":2}
Now I need to reset value of counter while the counter1 should be remaining same.
To increase counter I am use the command SETEX mykey 60 redis .
But it will also reset the value of counter1. So is there any way I can reset one value for a single key.
Let me know if I need to add some more info.
Instead of string you may use hash, then it will be easy. you can increment by some other value, delete counter etc etc. Each key in your json will be hash field.
127.0.0.1:6379> hset mykey counter 1 counter1 2
(integer) 2
127.0.0.1:6379> hgetall mykey
1) "counter"
2) "1"
3) "counter1"
4) "2"
127.0.0.1:6379> hset mykey counter 25
(integer) 0
127.0.0.1:6379> hgetall mykey
1) "counter"
2) "25"
3) "counter1"
4) "2"
127.0.0.1:6379> HINCRBY mykey counter 15
(integer) 40
127.0.0.1:6379> hgetall mykey
1) "counter"
2) "40"
3) "counter1"
4) "2"
127.0.0.1:6379>

How to get number of elements in a redis hash map?

Assuming that myhash is like:
redis 127.0.0.1:6379> HSET myhash field1 "foo"
(integer) 1
redis 127.0.0.1:6379> HSET myhash field2 "bar"
(integer) 1
redis 127.0.0.1:6379> HGETALL myhash
1) "field1"
2) "Hello"
3) "field2"
4) "World"
How can I get number of myhash elements, that is 2, from redis-cli?
I'm learning redis from this tutorial but could not find my answer there.
You can use the HLEN command. Taken directly from the documentation at redis.io:
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HLEN myhash
(integer) 2
redis>

Direct search for a key-value pair in redis

In a redis-cli session:
127.0.0.1:6379> SET somekey "Greetings"
OK
127.0.0.1:6379> SET somekey "Mortal"
OK
127.0.0.1:6379> EXISTS somekey
(integer) 1
I am looking for a function SOMEFUNC that searches for a key and a value. Perhaps something such as:
127.0.0.1:6379> SOMEFUNC "somekey:Greetings"
(integer) 1
127.0.0.1:6379> SOMEFUNC "somekey:Ave"
(integer) 0
It's all driven by a program, so I could use SSCAN on the key and compare, but I'm wondering if there is SOMEFUNC that will do this directly, as the statements above illustrate.
As noted by #MrWiggles, there is no such SOMEFUNC for Strings. On top of the two alternatives that he suggests, another that could get you pretty close is using Hashes instead. Consider the following:
127.0.0.1:6379> HSET somehash "Greetings" ""
(integer) 1
127.0.0.1:6379> HEXISTS somehash "Greetings"
(integer) 1
127.0.0.1:6379> HEXISTS somehash "Mortal"
(integer) 0
127.0.0.1:6379> HEXISTS someotherhash "Ave"
(integer) 0
Sets can also do the same trick:
127.0.0.1:6379> SADD someset "Greetings"
(integer) 1
127.0.0.1:6379> SISMEMBER someset "Greetings"
(integer) 1
127.0.0.1:6379> SISMEMBER someset "Mortal"
(integer) 0
127.0.0.1:6379> SISMEMBER someotherset "Ave"
(integer) 0
That said, note that Hashes & Sets have memory overheads (just like Strings). For 100,000 String/Hash/Set keys, here's what my local Redis' INFO MEMRORY reports for used_memory(_human):
Strings: 9594616 (9.15M)
Hashes: 11194616 (10.68M)
Sets: 25594616 (24.1M)
The real question, however, is why you want to keep everything in separate keys. Unless there's a compelling reason not to do so in your use case, consider a single Hash as a mini-store for all of your "keys" (fields) and values, e.g.:
127.0.0.1:6379> HSET minikv somekey Greetings
(integer) 1
127.0.0.1:6379> HSET minikv anotherkey Human
(integer) 1
...
In terms of your original question, this will bring you back to square one - meaning you'll have to read (HGET minikv somekey) and compare in your app or, my favorite, wrap it in Lua to do it in one call, perhaps like this:
127.0.0.1:6379> EVAL "return redis.call('HGET', KEYS[1], ARGV[1]) == ARGV[2] and 1 or 0" 1 minikv somekey Greetings
(integer) 1
127.0.0.1:6379> EVAL "return redis.call('HGET', KEYS[1], ARGV[1]) == ARGV[2] and 1 or 0" 1 minikv somekey Mortal
(integer) 0
127.0.0.1:6379> EVAL "return redis.call('HGET', KEYS[1], ARGV[1]) == ARGV[2] and 1 or 0" 1 minikv anotherkey Mortal
(integer) 0
127.0.0.1:6379> EVAL "return redis.call('HGET', KEYS[1], ARGV[1]) == ARGV[2] and 1 or 0" 1 minikv anotherkey Human
(integer) 1
The nice thing about using this minikv Hash is the footprint - a 100K Hash INFO memory:
used_memory:8519088
used_memory_human:8.12M
There is nothing built in to Redis that will do this for you.
A couple of ways of achieving this are:
Fetch the value of the key back into your application and then check the value to see if it equals what you want it to
Use a Lua script to do this on the redis server