Since redis does not support expiring set members, one common pattern is to use ordered sets instead and ZREMRANGEBYSCORE -inf $now to delete expired keys periodically (see #135).
My use case requires choosing N distinct elements from an ordered set at random, which is not supported by ordered sets currently, i.e. ZRANDMEMBER doesn't exist.
I think ZRANDMEMBER shall have the following signature:
ZRANDMEMBER key [count] [min max]
where min and max are both inclusive and allow minus/plus infinity (which are also their default values, respectively).
ZRANDMEMBER shall return abs(count) elements (whose semantics are the same as SRANDMEMBER) from the ordered set key where each returned element has a score s between min and max (inclusive).
I'd be happy to implement it too if anyone is willing to supervise.
Comment From: bionicles
yes please, need this today
Comment From: oranagra
Sounds like a good addition. Anyone wanna step in to code it?
Comment From: bionicles
I would love to but I don't know C and I'm not familiar with the codebase.
What's the most efficient way to learn to add new commands to Redis?
Comment From: oranagra
@bionicles this one is quite simple since it's easy to just follow the footsteps of SRANDMEMBER and do something similar for sorted sets. But if you don't know C, you might lose a lot of time on things that might be trivial for others, so maybe if you don't feel like playing, you can leave it for someone else.
Comment From: bionicles
https://github.com/redis/redis/blob/51eb0da824d2dae45bf5966e83f2089fba8d9c24/src/t_set.c#L794
void srandmemberCommand(client *c) {
robj *set;
sds ele;
int64_t llele;
int encoding;
if (c->argc == 3) {
srandmemberWithCountCommand(c);
return;
} else if (c->argc > 3) {
addReply(c,shared.syntaxerr);
return;
}
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]))
== NULL || checkType(c,set,OBJ_SET)) return;
encoding = setTypeRandomElement(set,&ele,&llele);
if (encoding == OBJ_ENCODING_INTSET) {
addReplyBulkLongLong(c,llele);
} else {
addReplyBulkCBuffer(c,ele,sdslen(ele));
}
}
Comment From: bionicles
https://github.com/redis/redis/blob/51eb0da824d2dae45bf5966e83f2089fba8d9c24/src/t_set.c#L195
/* Return random element from a non empty set.
* The returned element can be an int64_t value if the set is encoded
* as an "intset" blob of integers, or an SDS string if the set
* is a regular set.
*
* The caller provides both pointers to be populated with the right
* object. The return value of the function is the object->encoding
* field of the object and is used by the caller to check if the
* int64_t pointer or the redis object pointer was populated.
*
* Note that both the sdsele and llele pointers should be passed and cannot
* be NULL since the function will try to defensively populate the non
* used field with values which are easy to trap if misused. */
int setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele) {
if (setobj->encoding == OBJ_ENCODING_HT) {
dictEntry *de = dictGetFairRandomKey(setobj->ptr);
*sdsele = dictGetKey(de);
*llele = -123456789; /* Not needed. Defensive. */
} else if (setobj->encoding == OBJ_ENCODING_INTSET) {
*llele = intsetRandom(setobj->ptr);
*sdsele = NULL; /* Not needed. Defensive. */
} else {
serverPanic("Unknown set encoding");
}
return setobj->encoding;
}
Comment From: bionicles
Does dictGetFairRandomKey work for sorted sets?
Comment From: oranagra
sorted sets can be encoded with two encoding types: 1. a combination of a dict (for fast lookup) and a skiplist (for the sorting part). 2. ziplist. a blob of bytes containing the elements (sorted) one after the other.
so if it's "skiplist" encoded, you can get random elements form the dict.
and if it's "ziplist" encoded (usually when there are not many elements), you can just use random by using the number of elements (ziplistLen), and then ziplistIndex and ziplistGet.
Comment From: bionicles
I'd love to add it, but I think I ought to study C for a bit first.
Would it be possible for someone familiar with C and Redis to show how it's done?
This function could dramatically simplify our project code and save a lot of memory
Comment From: madolson
It looks like we didn't implement the exact signature. The general idea is implemented though, so it's probably reasonable to close this. We can re-evaluate if someone wants this in the future.