Skip to content

Commit

Permalink
(#175) ENG 3108: Implement ZSCORE
Browse files Browse the repository at this point in the history
Summary: ZSCORE key member for sorted sets. Github issue #175.

Test Plan: c++ and jedis client tests.

Reviewers: pritam.damania, amitanand, hector

Reviewed By: hector

Subscribers: kannan, ybase

Differential Revision: https://phabricator.dev.yugabyte.com/D4785
  • Loading branch information
rahuldesirazu committed May 9, 2018
1 parent cd8ef78 commit 06f1feb
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,36 @@ public void testBasicCommands() throws Exception {
assertEquals("v1", jedis_client.get("k1"));
}

@Test
public void testZScore() throws Exception {
Map<String, Double> pairs = new HashMap<String, Double>();
pairs.put("v0", 0.0);
pairs.put("v_neg", -5.0);
pairs.put("v1", 1.0);
pairs.put("v2", 2.5);
assertEquals(4L, jedis_client.zadd("z_key", pairs).longValue());

assertEquals(0.0, jedis_client.zscore("z_key", "v0"));
assertEquals(-5.0, jedis_client.zscore("z_key", "v_neg"));
assertEquals(1.0, jedis_client.zscore("z_key", "v1"));
assertEquals(2.5, jedis_client.zscore("z_key", "v2"));

assertNull(jedis_client.zscore("z_key", "v3"));
assertNull(jedis_client.zscore("z_no_exist", "v0"));

TSValuePairs tsPairs = new TSValuePairs((1));
assertEquals("OK", jedis_client.tsadd("ts_key", tsPairs.pairs));

try {
double d = jedis_client.zscore("ts_key", "v0");
} catch (Exception e) {
assertTrue(e.getMessage().contains(
"WRONGTYPE Operation against a key holding the wrong kind of value"));
return;
}
assertTrue(false);
}

@Test
public void testSetCommandWithOptions() throws Exception {
// Test with lowercase "ex" option.
Expand Down
1 change: 1 addition & 0 deletions src/yb/common/redis_protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ message RedisGetRequestPB {
ZCARD = 15;
TSGET = 14;
TSCARD = 16;
ZSCORE = 17;
UNKNOWN = 99;
}

Expand Down
27 changes: 27 additions & 0 deletions src/yb/docdb/doc_operation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,8 @@ Status RedisReadOperation::ExecuteGet() {
expected_type = REDIS_TYPE_HASH; break;
case RedisGetRequestPB_GetRequestType_SISMEMBER:
expected_type = REDIS_TYPE_SET; break;
case RedisGetRequestPB_GetRequestType_ZSCORE:
expected_type = REDIS_TYPE_SORTEDSET; break;
default:
expected_type = REDIS_TYPE_NONE;
}
Expand All @@ -1414,6 +1416,31 @@ Status RedisReadOperation::ExecuteGet() {
}
return Status::OK();
}
case RedisGetRequestPB_GetRequestType_ZSCORE: {
auto type = GetValueType();
RETURN_NOT_OK(type);
// If wrong type, we set the error code in the response.
if (!VerifyTypeAndSetCode(expected_type, *type, &response_, VerifySuccessIfMissing::kTrue)) {
return Status::OK();
}
SubDocKey key_reverse = SubDocKey(
DocKey::FromRedisKey(request_.key_value().hash_code(), request_.key_value().key()),
PrimitiveValue(ValueType::kSSReverse),
PrimitiveValue(request_.key_value().subkey(0).string_subkey()));
SubDocument subdoc_reverse;
bool subdoc_reverse_found = false;
auto encoded_key_reverse = key_reverse.EncodeWithoutHt();
GetSubDocumentData get_data = { encoded_key_reverse, &subdoc_reverse, &subdoc_reverse_found };
RETURN_NOT_OK(GetSubDocument(db_, get_data, redis_query_id(),
boost::none /* txn_op_context */, read_time_));
if (subdoc_reverse_found) {
double score = subdoc_reverse.GetDouble();
response_.set_string_response(std::to_string(score));
} else {
response_.set_code(RedisResponsePB_RedisStatusCode_NIL);
}
return Status::OK();
}
case RedisGetRequestPB_GetRequestType_HEXISTS: FALLTHROUGH_INTENDED;
case RedisGetRequestPB_GetRequestType_SISMEMBER: {
auto type = GetValueType();
Expand Down
1 change: 1 addition & 0 deletions src/yb/yql/redis/redisserver/redis_commands.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ namespace redisserver {
((zrangebyscore, ZRangeByScore, -4, READ)) \
((zrevrange, ZRevRange, -4, READ)) \
((zrange, ZRange, -4, READ)) \
((zscore, ZScore, 3, READ)) \
((tsrem, TsRem, -3, WRITE)) \
((zrem, ZRem, -3, WRITE)) \
((zadd, ZAdd, -4, WRITE)) \
Expand Down
13 changes: 13 additions & 0 deletions src/yb/yql/redis/redisserver/redis_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,19 @@ CHECKED_STATUS ParseTsGet(YBRedisReadOp* op, const RedisClientCommand& args) {
return Status::OK();
}

CHECKED_STATUS ParseZScore(YBRedisReadOp* op, const RedisClientCommand& args) {
op->mutable_request()->set_allocated_get_request(new RedisGetRequestPB());
op->mutable_request()->mutable_get_request()->set_request_type(
RedisGetRequestPB_GetRequestType_ZSCORE);

const auto& key = args[1];
op->mutable_request()->mutable_key_value()->set_key(key.cdata(), key.size());
auto member = args[2].ToBuffer();
op->mutable_request()->mutable_key_value()->add_subkey()->set_string_subkey(member);

return Status::OK();
}

CHECKED_STATUS ParseHStrLen(YBRedisReadOp* op, const RedisClientCommand& args) {
return ParseHGetLikeCommands(op, args, RedisGetRequestPB_GetRequestType_HSTRLEN);
}
Expand Down
30 changes: 30 additions & 0 deletions src/yb/yql/redis/redisserver/redisserver-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ class TestRedisService : public RedisTableTestBase {
);
}

void DoRedisTestDouble(int line, const std::vector<std::string>& command, double expected) {
DoRedisTest(line, command, RedisReplyType::kString,
[line, expected](const RedisReply& reply) {
std::string::size_type sz;
double reply_score = std::stod(reply.as_string(), &sz);
ASSERT_EQ(reply_score, expected) << "Originator: " << __FILE__ << ":" << line;
}
);
}

// Used to check pairs of doubles and strings, for range scans withscores.
void DoRedisTestScoreValueArray(int line,
const std::vector<std::string>& command,
Expand Down Expand Up @@ -1700,6 +1710,26 @@ TEST_F(TestRedisService, TestZRange) {
VerifyCallbacks();
}

TEST_F(TestRedisService, TestZScore) {
// The default value is true, but we explicitly set this here for clarity.
FLAGS_emulate_redis_responses = true;
DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "0", "v0", "0", "v0_copy", "1", "v1",
"2", "v2", "3", "v3", "4.5", "v4"}, 6);
SyncClient();

DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v0"}, 0.0);
DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v0_copy"}, 0.0);
DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v1"}, 1.0);
DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v2"}, 2.0);
DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v3"}, 3.0);
DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v4"}, 4.5);

DoRedisTestNull(__LINE__, {"ZSCORE", "z_no_exist", "v4"});

SyncClient();
VerifyCallbacks();
}

TEST_F(TestRedisService, TestTimeSeriesTTL) {
int64_t ttl_sec = 10;
TestTSTtl("EXPIRE_IN", ttl_sec, ttl_sec, "test_expire_in");
Expand Down

0 comments on commit 06f1feb

Please sign in to comment.