Get the index of an item by value in a redis list

前端 未结 6 1944
臣服心动
臣服心动 2020-12-31 08:32

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

相关标签:
6条回答
  • 2020-12-31 09:14

    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):

    • Use a custom lua script to find the index in the list;
    • Use a sorted set (instead of a list) with a timestamp as score and 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.

    0 讨论(0)
  • 2020-12-31 09:16

    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.

    0 讨论(0)
  • 2020-12-31 09:18

    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
    
    0 讨论(0)
  • 2020-12-31 09:19

    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.

    0 讨论(0)
  • 2020-12-31 09:24

    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
    
    0 讨论(0)
  • 2020-12-31 09:31

    Getting the index of an element in a redis list is possible with LPOS command (available since version 6.0.6)

    From the documentation,

    • Command - LPOS key element [FIRST rank] [COUNT num-matches] [MAXLEN len]
    • The command returns the index of matching elements inside a Redis list.
    • By default, when no options are given, it will scan the list from head to tail, looking for the first match of "element". If the element is found, its index (the zero-based position in the list) is returned. Otherwise, if no match is found, NULL is returned.

    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.

    0 讨论(0)
提交回复
热议问题