Background: this command was proposed as LRANK
in PR 7379. I (antirez) modified the implementation to provide the matchpos and ALL option, and renamed the command into LPOS.
This Gist was written as a draft of the command documentation, in order to get feedbacks from the community of Redis users about this command. A note about time complexity. This command is O(N) like a few Redis commadns operating on Redis lists. Normally we don't add commands that have worse than logarithmic complexity in Redis without thinking twice, however there are a few reasons why I'm positive about LPOS:
- List operations are often used against very small lists, making them effectively O(1), because certain applications always have a maximum size.
- It is a fundamental operation. When it's needed, people will resort to Lua scripting which is an order of magnitude slower than a direct implementation.
- The new development branch that shows threaded slow operations, not blocking the server when O(N) commands are called, is very promising. This may be one of the commands implemented in that way.
The implementation of LPOS can be found here: https://github.com/antirez/redis/commits/lpos
If you have feedbacks, please add a comment to this Gist or into the original issue.
The command returns the index of matching elements inside a Redis list. By default, when no options are given, the command will scan the list from head to tail, looking for the first match of "element". If the element is found, its index (the position) is returned, otherwise if no match is found, NULL is returned.
> RPUSH mylist a b c 1 2 3 c c
> LPOS mylist c
2
The optional arguments and options are able to modify the command behavior. The FIRST option specifies the "rank" of the first element to return, in case there are multiple matches. A rank of 1 means to return the first match, 2 to return the second match, and so forth.
For instance in the above example the element "c" is present multiple times, if I want the index of the second match, I'll write:
> LPOS mylist c FIRST 2
6
That is, the second occurrence of "c" is at position 6. A negative "rank" as the FIRST argument tells LPOS to invert the search direction, starting from the tail to the head. However the indexes are normally still reported in the "natural" way, that is, considering the first element starting from the head of the list at index 0, the next element at index 1, and so forth.
So, we want to say, give me the first element starting from the tail of the list:
> LPOS mylist c -1
7
Sometimes we want to return not just the Nth matching element, but the position of all the first N matching elements. This can be achieved using the COUNT option.
> LPOS mylist c COUNT 2
[2,6]
We can combine COUNT and FIRST, so that COUNT will try to return up to the specified number of matches, but starting from the first one specified.
> LPOS mylist c FIRST -1 COUNT 2
[7,6]
When COUNT is used, if the number of matches may be 0, to just tell the command we want all the matches found returned as an array of indexes.
> LPOS mylist COUNT 0
[2,6,7]
When COUNT is used and no match is found, an empty array is returned. However when COUNT is not used and there are no matches, the command returns NULL.
Finally the MAXLEN option, tells the command to compare the provided element only with the given number of list items. So for instance specifying MAXLEN 1000 will make sure that the command performs only 1000 comparison, effectively running the algorithm on a subset of the list. This is useful to limit the maximum complexity of the command.
thoughts: redis uses zero-indexing in many other places -
LINDEX
,ZRANGE
, etc; as such, I would expectLPOS mylist c 0
to return the index of the first match, andLPOS mylist c 2
to return the index of the third match