I have a redis list I have created, I am using it as a queue at the moment that reverses once in a while. My problem is that I would like to be able to get the index of an i
As you may tell by now, Redis don't support such operation (sad face).
Although someone made some pretty good remarks on why such operation would make sense, looks like Salvatore won't implemented it any time soon.
There are basically two workarounds (as pointed out by the other answers):
ZRANK
for the index.Since the first is O(N)
and the latter just O(log(N))
you can probably tell which one outperforms the other.
Anyway I decided to put to the test*:
╔════════════════╦═══════════════════════╦══════════════════════╗
║ ║ Sorted Set with ZRANK ║ List with lua script ║
╠════════════════╬═══════════════════════╬══════════════════════╣
║ 1000 elements ║ 0.0638264 seconds ║ 0.2723238 seconds ║
╠════════════════╬═══════════════════════╬══════════════════════╣
║ 10000 elements ║ 00.4484714 seconds ║ 41.0661683 seconds ║
╚════════════════╩═══════════════════════╩══════════════════════╝
Yup, that's staggering 41 seconds for just ten thousand elements.
* On Windows 7, Redis 2.8 (MSOpenTech port), .NET 4 with compiler optimizations turned on and StackExchange.Redis 1.0.488.
I don't know the nodejs client details for this, but the following is an implementation of a very simple indexOf command in lua.
In a my file indexof.lua
i have the following code:
local key = KEYS[1]
local obj = ARGV[1]
local items = redis.call('lrange', key, 0, -1)
for i=1,#items do
if items[i] == obj then
return i - 1
end
end
return -1
Lets push a few values to a mylist
.
> rpush mylist foo bar baz qux
(integer) 4
We can use the lua script to find the index of any value within the list. The command is O(N).
$ redis-cli --eval indexof.lua mylist , bar
(integer) 1
index of bar
was 1
> lindex mylist 1
"bar"
index of nil
is -1
$ redis-cli --eval indexof.lua mylist , nil
(integer) -1
Look at the http://redis.io/commands/eval further documentation on EVAL command.
Using sorted sets (ZADD, etc) you can use ZRANK.
Edit: My old answer below doesn't work, because your list changes, although it does, with a list that only grows using RPUSH.
You could store the index with the value (or its hash) as a key:
set listvalue listindex
In order to keep your redis organised, you could prefix those keys with the listname:
set listname:listvalue listindex
As per ticket 140 on the list of issues of redis.io
Feature Request: lRank
"Hi, this command will likely not be implemented as it is both an O(N) command and one that usually is felt as needed only when there is some error in the data layout design." by Salvatore Sanfilippo on https://github.com/antirez/redis/issues/140.
I am not quite sure why and how wanting to find out the index of an item by value could be an error in the data design. However he makes clear that you can use lua code and/or sorted sets.
So the up shot of it is that there is no way to find out the index of an item in a list other then using a lua script.
However depending on the implementation i.e. data design it may be better to consider sorted sets instead of lists.
Use sorted sets to implement a queue.
Add members and use timestamp as score.
> ZADD queue 1326990501 foo 1326990502 bar 1326990503 baz 1326990504 qux
(integer) 4
You can return members in FIFO and LIFO order by the use of ZRANGE and ZREVRANGE respectively.
FIFO:
> ZRANGE queue 0 0
"foo"
LIFO:
> ZREVRANGE queue 0 0
"qux"
To find the index of a member use ZRANK. ZRANK op is O(log(N))
> ZRANK queue bar
(integer) 1
Getting the index of an element in a redis list is possible with LPOS command (available since version 6.0.6)
From the documentation,
LPOS key element [FIRST rank] [COUNT num-matches] [MAXLEN len]
So in you case, for the list {"dan","eduardo","pedro"}
with key users
LPOS users eduardo
Must return 1. I personally haven't tried using it in a lua script hence not providing the script but should be straight forward I believe.