Skip to content

Commit

Permalink
add r.min/r.max command (#58)
Browse files Browse the repository at this point in the history
* add r.min/r.max command

* add integration tests/performance tests/documentation on README

* Small fix intergation tests
  • Loading branch information
yihuang authored and aviggiano committed Jun 29, 2019
1 parent 2a9844f commit 6a55910
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 3 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The performance tests can take a while, since they run on a real dataset of inte

## API

Currently the following operations are supported
The following operations are supported

- `R.SETBIT` (same as [SETBIT](https://redis.io/commands/setbit))
- `R.GETBIT` (same as [GETBIT](https://redis.io/commands/getbit))
Expand All @@ -56,14 +56,16 @@ Currently the following operations are supported
- `R.SETBITARRAY` (create a roaring bitmap from a bit array string)
- `R.GETBITARRAY` (get a bit array string from a roaring bitmap)

<a id="newAPI">new added API</a>
Additional commands

- `R.APPENDINTARRAY` (append integers to a roaring bitmap)
- `R.RANGEINTARRAY` (get an integer array from a roaring bitmap with `start` and `end`, so can implements paging)
- `R.SETRANGE` (set or append integer range to a roaring bitmap)
- `R.SETFULL` (fill up a roaring bitmap in integer)
- `R.STAT` (get statistical information of a roaring bitmap)
- `R.OPTIMIZE` (optimize a roaring bitmap)
- `R.MIN` (get minimal integer from a roaring bitmap, if key is not exists or bitmap is empty, return -1)
- `R.MAX` (get maximal integer from a roaring bitmap, if key is not exists or bitmap is empty, return -1)

Missing commands:

Expand Down
60 changes: 60 additions & 0 deletions src/redis-roaring.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,60 @@ int RBitPosCommand(RedisModuleCtx* ctx, RedisModuleString** argv, int argc) {
return REDISMODULE_OK;
}

int RMinCommand(RedisModuleCtx* ctx, RedisModuleString** argv, int argc) {
if (argc != 2) {
return RedisModule_WrongArity(ctx);
}

RedisModuleKey* key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
int type = RedisModule_KeyType(key);
if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != BitmapType) {
return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
}

long long result;
if (type == REDISMODULE_KEYTYPE_EMPTY) {
result = -1;
} else {
Bitmap* bitmap = RedisModule_ModuleTypeGetValue(key);
if (roaring_bitmap_is_empty(bitmap)) {
result = -1;
} else {
result = roaring_bitmap_minimum(bitmap);
}
}

RedisModule_ReplyWithLongLong(ctx, result);
return REDISMODULE_OK;
}

int RMaxCommand(RedisModuleCtx* ctx, RedisModuleString** argv, int argc) {
if (argc != 2) {
return RedisModule_WrongArity(ctx);
}

RedisModuleKey* key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
int type = RedisModule_KeyType(key);
if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != BitmapType) {
return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
}

long long result;
if (type == REDISMODULE_KEYTYPE_EMPTY) {
result = -1;
} else {
Bitmap* bitmap = RedisModule_ModuleTypeGetValue(key);
if (roaring_bitmap_is_empty(bitmap)) {
result = -1;
} else {
result = roaring_bitmap_maximum(bitmap);
}
}

RedisModule_ReplyWithLongLong(ctx, result);
return REDISMODULE_OK;
}

int RedisModule_OnLoad(RedisModuleCtx* ctx) {
// Register the module itself
if (RedisModule_Init(ctx, "REDIS-ROARING", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) {
Expand Down Expand Up @@ -673,6 +727,12 @@ int RedisModule_OnLoad(RedisModuleCtx* ctx) {
if (RedisModule_CreateCommand(ctx, "R.BITPOS", RBitPosCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
if (RedisModule_CreateCommand(ctx, "R.MIN", RMinCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
if (RedisModule_CreateCommand(ctx, "R.MAX", RMaxCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}

return REDISMODULE_OK;
}
50 changes: 50 additions & 0 deletions tests/integration_1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,55 @@ function test_getbitarray_setbitarray()
EXPECTED=""
[ "$FOUND" == "$EXPECTED" ]
}
function test_min_max()
{
echo "test_min_max"

FOUND=$(echo "R.MIN test_min_max" | redis-cli)
EXPECTED="-1"
[ "$FOUND" == "$EXPECTED" ]

FOUND=$(echo "R.MAX test_min_max" | redis-cli)
EXPECTED="-1"
[ "$FOUND" == "$EXPECTED" ]

echo "R.SETBIT test_min_max 100 1" | redis-cli

FOUND=$(echo "R.MIN test_min_max" | redis-cli)
EXPECTED="100"
[ "$FOUND" == "$EXPECTED" ]

FOUND=$(echo "R.MAX test_min_max" | redis-cli)
EXPECTED="100"
[ "$FOUND" == "$EXPECTED" ]

echo "R.SETBIT test_min_max 0 1" | redis-cli

FOUND=$(echo "R.MIN test_min_max" | redis-cli)
EXPECTED="0"
[ "$FOUND" == "$EXPECTED" ]

FOUND=$(echo "R.MAX test_min_max" | redis-cli)
EXPECTED="100"
[ "$FOUND" == "$EXPECTED" ]

FOUND=$(echo "R.SETBIT test_min_max 0 0" | redis-cli)
EXPECTED="1"
[ "$FOUND" == "$EXPECTED" ]

FOUND=$(echo "R.SETBIT test_min_max 100 0" | redis-cli)
EXPECTED="1"
[ "$FOUND" == "$EXPECTED" ]

FOUND=$(echo "R.MIN test_min_max" | redis-cli)
EXPECTED="-1"
[ "$FOUND" == "$EXPECTED" ]

FOUND=$(echo "R.MAX test_min_max" | redis-cli)
EXPECTED="-1"
[ "$FOUND" == "$EXPECTED" ]

}
function test_del()
{
echo "test_del"
Expand All @@ -221,5 +270,6 @@ test_bitcount
test_bitpos
test_getintarray_setintarray
test_getbitarray_setbitarray
test_min_max
test_del
test_save
40 changes: 39 additions & 1 deletion tests/performance.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,44 @@ int main(int argc, char* argv[]) {
}
}

{
const char* ops[] = {
"R.MIN",
"MIN"
};

for (size_t op = 0; op < sizeof(ops) / sizeof(*ops); op++) {
Statistics statistics = statistics_init(ops[op]);
for (size_t i = 0; i < count; i++) {
timer(&statistics);
redisReply* reply = redisCommand(c, "%s %d-%d", ops[op], op, i);
timer(&statistics);
debug("reply %s %s %lld\n", ops[op], reply->str, reply->integer);
freeReplyObject(reply);
}
statistics_print(&statistics);
}
}

{
const char* ops[] = {
"R.MAX",
"MAX"
};

for (size_t op = 0; op < sizeof(ops) / sizeof(*ops); op++) {
Statistics statistics = statistics_init(ops[op]);
for (size_t i = 0; i < count; i++) {
timer(&statistics);
redisReply* reply = redisCommand(c, "%s %d-%d", ops[op], op, i);
timer(&statistics);
debug("reply %s %s %lld\n", ops[op], reply->str, reply->integer);
freeReplyObject(reply);
}
statistics_print(&statistics);
}
}

printf("\n");

for (size_t i = 0; i < count; i++) {
Expand All @@ -279,4 +317,4 @@ int main(int argc, char* argv[]) {
redisFree(c);

return 0;
}
}

0 comments on commit 6a55910

Please sign in to comment.