Reputation: 46493
Let's create 5 items with :
HMSET id1 x 0 y 1 z 3
HMSET id2 x 2 y 3 z 5
HMSET id3 x 8 y 4 z 3
HMSET id4 x 9 y 8 z 6
HMSET id5 x 1 y 3 z 5
How to find the elements such that 0 < x < 2
, 2 < y < 5
and 4 < z < 8
?
More generally, how to perform a multiple-criteria search with redis ?
Sidenote: In this nice tutorial, I read the following notice, is that true?
Redis Querying
In Redis, data can only be queried by its key. Even if we use a hash, we can't say get me the keys wherever the field ... is equal to ...
Upvotes: 4
Views: 2504
Reputation: 49962
The tutorial is right to an extent - Redis does not offer functionality for searching values out of the box... but you can use Redis' data structures to maintain indices for just about any value.
Continuing your example, since you are looking to do range queries, you'll want to use sorted sets to store index data. For each indexed hash field, maintain a separate sorted set where the members are the key names and the score is the field value. Therefore, for your data, do the following:
ZADD _x 0 id1 2 id2 8 id3 9 id4 1 id5
ZADD _y 1 id1 3 id2 4 id3 8 id4 3 id5
ZADD _z 3 id1 5 id2 3 id3 6 id4 5 id5
To perform the actual query, use ZRANGEBYSCORE on each sorted set, and intersect the results to apply the logical and operator. This could be executed in your application's code or via the use of a server-side Lua script.
EDIT: here's a Redis Lua script that performs this type of query. I chose to use Lua both because my Node.js skills are sub-par (:)) and also because of efficiency - this approach will save round trips of the data to the client.:
local tmps = {}
for i = 1, #KEYS / 2, 1 do
local range
range = redis.call("ZRANGEBYSCORE", KEYS[i], "(" .. ARGV[i*2-1], "(" .. ARGV[i*2])
local tmp = KEYS[#KEYS / 2 + i]
redis.call("DEL", tmp)
for k, v in ipairs(range) do
redis.call("SADD", tmp, v)
end
tmps[#tmps + 1] = tmp
end
return redis.call("SINTER", unpack(tmps))
Invoke this as follows:
redis-cli --eval query.lua _x _y _z __x __y __z , 0 2 2 5 4 8
Note that the script accepts 6 keys - the 3 indices as well as 3 temporary variable names (this is required per the spec for cluster compatibility).
Answer to comment #1: yes - Redis' sorted set scores can be floats and there shouldn't be any issue with thousands of values
Answer to comment #2: without benchmarking this I can't be certain but I have good reason to believe this will be at least as performant as any SQL solution.
Lastly, you may find this presentation by yours truely helpful in this context: http://www.slideshare.net/itamarhaber/20140922-redis-tlv-redis-indices
Upvotes: 4