Reputation: 5040
In my application I need have a set of values and I need to check how many of these values are present in a set in Redis.
Just to make it simple, What I want to do is something like :
> Sadd myset field1
(integer) 1
> Sadd myset field2
(integer) 1
> Sadd myset field4
(integer) 1
> Sismember myset field1 field4 // which is not possible as of now.
Since I can not give multiple parameters for SISMEMBER
, I might have to multiple redis call which is very time consuming.
I was thinking about alternatives like pipelining
but then I thought this will be a good (hacky) way of achieving it :
> Hset myhash field1 "true"
(integer) 0
> Hset myhash field2 "true"
(integer) 0
> Hset myhash field4 "true"
(integer) 1
> Hmget myhash field1 field2 field3
1) "true"
2) "true"
3) (nil)
> Hmget myhash field1 field2 field3 field4
1) "true"
2) "true"
3) (nil)
4) "true"
Redis
HMGET page says the following :
Available since 2.0.0.
Time complexity: O(N) where N is the number of fields being requested.
Which is really good when compared to making multiples calls for SADD
, But I am really not sure If I am 100% correct and I also don't know if there is any serious drawback of using hmget
this way.
So I just wanted to the drawbacks of using hmget
this way and any better approaches to solve this.
Upvotes: 6
Views: 4350
Reputation: 9586
Starting from Redis 6.2 you can use SMISMEMBER command check the existence of multiple element is the given set. It will return the array reply of the elements given in the same order as they are requested. You can get the sum of the array on the client side to get how many of these values are present in the set.
redis> SADD myset foo bar zet que
(integer) 4
redis> SMISMEMBER myset foo bar not
1) (integer) 1
2) (integer) 1
3) (integer) 0
Upvotes: 2
Reputation: 10763
Here is much easier way to do it on Java with Redisson framework:
RSet<String> set = redisson.getSet("mySet");
// uses lua-script under the hood
if (set.containsAll(Arrays.asList("obj1", "obj2", "obj3", "obj4"))) {
// ...
}
Upvotes: 1
Reputation: 5040
Following @itamar 's answer, I was able to do this using lua script.
I used the following script:
local r = {}
for i, m in pairs(KEYS) do
r[i] = redis.call('SISMEMBER',ARGV[1],m)
end
return r
May be this will be useful for someone in future, so just writing how to call this script from java
(Spring Data Redis 1.5.0
& jedis 2.6.2
)
redisTemplate.opsForSet().add("mySet","3");
redisTemplate.opsForSet().add("mySet","4");
redisTemplate.opsForSet().add("mySet","35");
redisTemplate.opsForSet().add("mySet", "6");
List<String> list = new LinkedList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("35");
list.add("6");
System.out.println(redisTemplate.execute(script, list,"mySet"));
prints the following :
[0, 0, 1, 1, 1]
EDIT I am not sure everybody is saying that mySet
is KEYS and list
is ARGV, but in SPRING DATA REDIS
, execute
function is defined in the following way :
public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
return this.scriptExecutor.execute(script, keys, args);
}
Upvotes: 1
Reputation: 49932
This is certainly a valid solution, although a little wasteful as you'll be maintaining a bunch of true
values - a RAM overhead. FYI, Redis' Sets are implemented internally using the same hash table structs as Hashes are, so you're not that far off :)
While there is no variadic form of SISMEMBER
, it as an easily scriptable flow with Lua so you may want to consider that as well. For example something like the following:
local r = {}
for _, m in pairs(ARGV) do
r[#r+1] = redis.call('SISMEMBER', KEYS[1], m)
end
return r
Upvotes: 2