int Ardb::HashGetAll(Context& ctx, const Slice& key, RedisReply& reply) { ValueObject meta; int err = GetMetaValue(ctx, key, HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); reply.type = REDIS_REPLY_ARRAY; if (0 != err) { return 0; } HashIterator iter; err = HashIter(ctx, meta, "", iter, true); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); reply.type = REDIS_REPLY_ARRAY; while (iter.Valid()) { const Data* field = iter.Field(); Data* value = iter.Value(); RedisReply& r = reply.AddMember(); fill_value_reply(r, *field); RedisReply& r1 = reply.AddMember(); fill_value_reply(r1, *value); iter.Next(); } return 0; }
int Ardb::HMGet(Context& ctx, RedisCommandFrame& cmd) { ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; if (err == 0) { for (uint32 i = 1; i < cmd.GetArguments().size(); i++) { Data value; Data field(cmd.GetArguments()[i]); RedisReply& r = ctx.reply.AddMember(); if (0 == HashGet(ctx, meta, field, value)) { r.type = REDIS_REPLY_STRING; value.GetDecodeString(r.str); } else { r.type = REDIS_REPLY_NIL; } } } else { ctx.reply.ReserveMember(cmd.GetArguments().size()); } return 0; }
int Ardb::HashLen(Context& ctx, const Slice& key) { ValueObject meta; int err = GetMetaValue(ctx, key, HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (err == ERR_NOT_EXIST) { fill_int_reply(ctx.reply, 0); } else { if (meta.meta.Length() < 0) { HashIterator iter; meta.meta.len = 0; HashIter(ctx, meta, "", iter, true); while (iter.Valid()) { meta.meta.len++; iter.Next(); } SetKeyValue(ctx, meta); } fill_int_reply(ctx.reply, meta.meta.Length()); } return 0; }
int Ardb::HIncrbyFloat(Context& ctx, RedisCommandFrame& cmd) { double increment, val = 0; if (!GetDoubleValue(ctx, cmd.GetArguments()[2], increment)) { return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); Data value; Data field(cmd.GetArguments()[1]); if (0 == err) { HashGet(ctx, meta, field, value); if (!value.GetDouble(val)) { fill_error_reply(ctx.reply, "value is not a float or out of range"); return 0; } } val += increment; std::string tmp; fast_dtoa(val, 10, tmp); value.SetString(tmp, false); HashSet(ctx, meta, field, value); fill_str_reply(ctx.reply, tmp); return 0; }
int Ardb::HIncrby(Context& ctx, RedisCommandFrame& cmd) { int64 increment, val = 0; if (!GetInt64Value(ctx, cmd.GetArguments()[2], increment)) { return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); Data field(cmd.GetArguments()[1]); Data value; if (err == 0) { err = HashGet(ctx, meta, field, value); } if (err == ERR_NOT_EXIST) { value.SetInt64(increment); } else { if (!value.GetInt64(val)) { fill_error_reply(ctx.reply, "value is not a integer or out of range"); return 0; } value.SetInt64(val + increment); } HashSet(ctx, meta, field, value); fill_int_reply(ctx.reply, val + increment); return 0; }
int Ardb::HMSet(Context& ctx, RedisCommandFrame& cmd) { if (m_cfg.replace_for_hmset) { HReplace(ctx, cmd); return 0; } if ((cmd.GetArguments().size() - 1) % 2 != 0) { fill_error_reply(ctx.reply, "wrong number of arguments for HMSet"); return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); DataMap fs; for (uint32 i = 1; i < cmd.GetArguments().size(); i += 2) { Data f(cmd.GetArguments()[i]), v(cmd.GetArguments()[i + 1]); fs[f] = v; } HashMultiSet(ctx, meta, fs); fill_status_reply(ctx.reply, "OK"); return 0; }
/* * GEOADD key MERCATOR|WGS84 x y value [x y value....] */ int Ardb::GeoAdd(Context& ctx, RedisCommandFrame& cmd) { GeoAddOptions options; std::string err; if (0 != options.Parse(cmd.GetArguments(), err, 1)) { fill_error_reply(ctx.reply, "%s", err.c_str()); return 0; } GeoHashRange lat_range, lon_range; GeoHashHelper::GetCoordRange(options.coord_type, lat_range, lon_range); KeyLockerGuard keylock(m_key_lock, ctx.currentDB, cmd.GetArguments()[0]); ValueObject meta; int ret = GetMetaValue(ctx, cmd.GetArguments()[0], ZSET_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, ret); RedisCommandFrame zadd("zadd"); zadd.AddArg(cmd.GetArguments()[0]); BatchWriteGuard guard(GetKeyValueEngine()); GeoPointArray::iterator git = options.points.begin(); uint32 count = 0; while (git != options.points.end()) { GeoPoint& point = *git; if (point.x < lon_range.min || point.x > lon_range.max || point.y < lat_range.min || point.y > lat_range.max) { guard.MarkFailed(); break; } GeoHashBits hash; geohash_encode(&lat_range, &lon_range, point.y, point.x, 30, &hash); GeoHashFix60Bits score = hash.bits; Data score_value; score_value.SetInt64((int64) score); Data element; element.SetString(point.value, true); count += ZSetAdd(ctx, meta, element, score_value, NULL); std::string tmp; score_value.GetDecodeString(tmp); zadd.AddArg(tmp); element.GetDecodeString(tmp); zadd.AddArg(tmp); git++; } if (guard.Success()) { SetKeyValue(ctx, meta); RewriteClientCommand(ctx, zadd); fill_int_reply(ctx.reply, count); } else { fill_error_reply(ctx.reply, "Invalid arguments"); } return 0; }
int Ardb::ListLen(Context& ctx, const Slice& key) { ValueObject meta; int err = GetMetaValue(ctx, key, LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); fill_int_reply(ctx.reply, meta.meta.Length()); return 0; }
int Ardb::HSet(Context& ctx, RedisCommandFrame& cmd) { ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); Data field(cmd.GetArguments()[1]), value(cmd.GetArguments()[2]); HashSet(ctx, meta, field, value); //fill_int_reply(ctx.reply, err); return 0; }
int Ardb::PExpire(Context& ctx, RedisCommandFrame& cmd) { uint32 v = 0; if (!check_uint32_arg(ctx.reply, cmd.GetArguments()[1], v)) { return 0; } ValueObject meta; int err = GenericExpire(ctx, cmd.GetArguments()[0], v + get_current_epoch_millis()); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); fill_int_reply(ctx.reply, err == 0 ? 1 : 0); return 0; }
int Ardb::HDel(Context& ctx, RedisCommandFrame& cmd) { ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); int count = 0; if (err == 0) { BatchWriteGuard guard(GetKeyValueEngine()); for (uint32 i = 1; i < cmd.GetArguments().size(); i++) { Data field(cmd.GetArguments()[i]); Data value; err = HashGet(ctx, meta, field, value); if (err == 0) { if (meta.meta.encoding != COLLECTION_ECODING_ZIPMAP) { KeyObject k; k.db = ctx.currentDB; k.key = cmd.GetArguments()[0]; k.type = HASH_FIELD; k.element = field; DelKeyValue(ctx, k); if (meta.meta.len > 0) { meta.meta.len--; } } else { meta.meta.zipmap.erase(field); } count++; } } if (count > 0) { if (meta.meta.Length() != 0) { SetKeyValue(ctx, meta); } else { DelKeyValue(ctx, meta.key); } } } fill_int_reply(ctx.reply, count); return 0; }
int Ardb::HVals(Context& ctx, RedisCommandFrame& cmd) { ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; if (0 != err) { return 0; } HashIterator iter; err = HashIter(ctx, meta, "", iter, true); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; while (iter.Valid()) { Data* value = iter.Value(); RedisReply& r = ctx.reply.AddMember(); fill_value_reply(r, *value); iter.Next(); } return 0; }
int Ardb::LPush(Context& ctx, RedisCommandFrame& cmd) { ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); KeyLockerGuard lock(m_key_lock, ctx.currentDB, cmd.GetArguments()[0]); BatchWriteGuard guard(GetKeyValueEngine(), cmd.GetArguments().size() > 2); for (uint32 i = 1; i < cmd.GetArguments().size(); i++) { ListInsert(ctx, meta, NULL, cmd.GetArguments()[i], true, false); } SetKeyValue(ctx, meta); return 0; }
int ArdbServer::GeoSearch(ArdbConnContext& ctx, RedisCommandFrame& cmd) { GeoSearchOptions options; std::string err; if (0 != options.Parse(cmd.GetArguments(), err, 1)) { fill_error_reply(ctx.reply, "%s", err.c_str()); return 0; } ValueDataDeque res; int ret = m_db->GeoSearch(ctx.currentDB, cmd.GetArguments()[0], options, res); CHECK_ARDB_RETURN_VALUE(ctx.reply, ret); fill_array_reply(ctx.reply, res); return 0; }
int Ardb::HKeys(Context& ctx, RedisCommandFrame& cmd) { ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; if (0 != err) { return 0; } HashIterator iter; err = HashIter(ctx, meta, "", iter, true); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; while (iter.Valid()) { const Data* value = iter.Field(); RedisReply& r = ctx.reply.AddMember(); std::string tmp; fill_str_reply(r, value->GetDecodeString(tmp)); iter.Next(); } return 0; }
int Ardb::ListInsert(Context& ctx, const std::string& key, const std::string* match, const std::string& value, bool head, bool abort_nonexist) { ValueObject meta; int err = GetMetaValue(ctx, key, LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); BatchWriteGuard guard(GetKeyValueEngine(), meta.meta.Encoding() != COLLECTION_ENCODING_ZIPLIST); if (0 != err && abort_nonexist) { fill_int_reply(ctx.reply, 0); return 0; } ListInsert(ctx, meta, match, value, head, abort_nonexist); SetKeyValue(ctx, meta); return 0; }
int Ardb::RenameList(Context& ctx, DBID srcdb, const std::string& srckey, DBID dstdb, const std::string& dstkey) { Context tmpctx; tmpctx.currentDB = srcdb; ValueObject v; int err = GetMetaValue(tmpctx, srckey, LIST_META, v); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (0 != err) { fill_error_reply(ctx.reply, "no such key or some error"); return 0; } if (v.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { DelKeyValue(tmpctx, v.key); v.key.encode_buf.Clear(); v.key.db = dstdb; v.key.key = dstkey; v.meta.expireat = 0; SetKeyValue(ctx, v); } else { ListIterator iter; ListIter(ctx, v, iter, false); tmpctx.currentDB = dstdb; ValueObject dstmeta; dstmeta.key.type = KEY_META; dstmeta.key.key = dstkey; dstmeta.type = LIST_META; dstmeta.meta.SetFlag(COLLECTION_FLAG_SEQLIST); dstmeta.meta.SetEncoding(COLLECTION_ENCODING_ZIPLIST); BatchWriteGuard guard(GetKeyValueEngine()); while (iter.Valid()) { std::string tmpstr; ListInsert(tmpctx, dstmeta, NULL, iter.Element()->GetDecodeString(tmpstr), false, false); iter.Next(); } SetKeyValue(tmpctx, dstmeta); tmpctx.currentDB = srcdb; DeleteKey(tmpctx, srckey); } ctx.data_change = true; return 0; }
int Ardb::RenameHash(Context& ctx, DBID srcdb, const std::string& srckey, DBID dstdb, const std::string& dstkey) { Context tmpctx; tmpctx.currentDB = srcdb; ValueObject v; int err = GetMetaValue(tmpctx, srckey, HASH_META, v); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (0 != err) { fill_error_reply(ctx.reply, "no such key or some error"); return 0; } if (v.meta.encoding == COLLECTION_ECODING_ZIPMAP) { DelKeyValue(tmpctx, v.key); v.key.encode_buf.Clear(); v.key.db = dstdb; v.key.key = dstkey; v.meta.expireat = 0; SetKeyValue(ctx, v); } else { HashIterator iter; HashIter(ctx, v, "", iter, false); tmpctx.currentDB = dstdb; ValueObject dstmeta; dstmeta.key.type = KEY_META; dstmeta.key.key = dstkey; dstmeta.type = HASH_META; dstmeta.meta.encoding = COLLECTION_ECODING_ZIPMAP; BatchWriteGuard guard(GetKeyValueEngine()); while (iter.Valid()) { HashSet(tmpctx, dstmeta, *(iter.Field()), *(iter.Value())); iter.Next(); } SetKeyValue(tmpctx, dstmeta); tmpctx.currentDB = srcdb; DeleteKey(tmpctx, srckey); } ctx.data_change = true; return 0; }
int Ardb::HMIncrby(Context& ctx, RedisCommandFrame& cmd) { if ((cmd.GetArguments().size() - 1) % 2 != 0) { fill_error_reply(ctx.reply, "wrong number of arguments for HMIncrby"); return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; DataMap fs; Int64Array vs; for (uint32 i = 1; i < cmd.GetArguments().size(); i += 2) { int64 inc = 0; if (!GetInt64Value(ctx, cmd.GetArguments()[i + 1], inc)) { return 0; } Data field(cmd.GetArguments()[i]); if (err == ERR_NOT_EXIST) { fs[field].SetInt64(inc); vs.push_back(inc); } else { Data value; HashGet(ctx, meta, field, value); int64 val = 0; if (!value.GetInt64(val)) { fill_error_reply(ctx.reply, "value is not a float or out of range"); return 0; } fs[field].SetInt64(inc + val); vs.push_back(inc + val); } } HashMultiSet(ctx, meta, fs); fill_int_array_reply(ctx.reply, vs); return 0; }
int Ardb::HashGet(Context& ctx, const std::string& key, const std::string& field, Data& v) { ValueObject meta; int err = GetMetaValue(ctx, key, HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); Data f; f.SetString(field, true); err = HashGet(ctx, meta, f, v); if (err == ERR_NOT_EXIST) { ctx.reply.type = REDIS_REPLY_NIL; } else { ctx.reply.type = REDIS_REPLY_STRING; v.GetDecodeString(ctx.reply.str); } return 0; }
/* * GEOADD key MERCATOR|WGS84 x y value [attr_name attr_value ...] */ int ArdbServer::GeoAdd(ArdbConnContext& ctx, RedisCommandFrame& cmd) { GeoAddOptions options; std::string err; if (0 != options.Parse(cmd.GetArguments(), err, 1)) { fill_error_reply(ctx.reply, "%s", err.c_str()); return 0; } int ret = m_db->GeoAdd(ctx.currentDB, cmd.GetArguments()[0], options); CHECK_ARDB_RETURN_VALUE(ctx.reply, ret); if (ret >= 0) { fill_status_reply(ctx.reply, "OK"); } else { fill_error_reply(ctx.reply, "Failed to %s", cmd.ToString().c_str()); } return 0; }
int Ardb::HSetNX(Context& ctx, RedisCommandFrame& cmd) { ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); Data value; Data field(cmd.GetArguments()[1]); err = HashGet(ctx, meta, field, value); if (err == ERR_NOT_EXIST) { value.SetString(cmd.GetArguments()[2], true); HashSet(ctx, meta, field, value); err = 1; } else { err = 0; } fill_int_reply(ctx.reply, err); return 0; }
int Ardb::LIndex(Context& ctx, RedisCommandFrame& cmd) { int64 index; if (!GetInt64Value(ctx, cmd.GetArguments()[1], index)) { return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (0 != err) { ctx.reply.type = REDIS_REPLY_NIL; return 0; } if (meta.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { Data* entry = GetZipEntry(meta.meta.ziplist, index); if (NULL != entry) { fill_value_reply(ctx.reply, *entry); } else { ctx.reply.type = REDIS_REPLY_NIL; } return 0; } else { if ((index >= 0 && index >= meta.meta.Length()) || (index < 0 && (meta.meta.Length() + index) < 0)) { ctx.reply.type = REDIS_REPLY_NIL; return 0; } ListIterator iter; if (meta.meta.IsSequentialList()) { err = SequencialListIter(ctx, meta, iter, index); if (0 == err) { if (iter.Valid()) { fill_value_reply(ctx.reply, *(iter.Element())); return 0; } } } else { err = ListIter(ctx, meta, iter, index < 0); uint32 cursor = index < 0 ? 1 : 0; while (iter.Valid()) { if (cursor == std::abs(index)) { fill_value_reply(ctx.reply, *(iter.Element())); return 0; } cursor++; if (index >= 0) { iter.Next(); } else { iter.Prev(); } } } ctx.reply.type = REDIS_REPLY_NIL; } return 0; }
int Ardb::LSet(Context& ctx, RedisCommandFrame& cmd) { int64 index; if (!GetInt64Value(ctx, cmd.GetArguments()[1], index)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (0 != err) { fill_error_reply(ctx.reply, "no such key"); return 0; } if (meta.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { Data* entry = GetZipEntry(meta.meta.ziplist, index); if (NULL == entry) { fill_error_reply(ctx.reply, "index out of range"); return 0; } else { entry->SetString(cmd.GetArguments()[2], true); SetKeyValue(ctx, meta); fill_status_reply(ctx.reply, "OK"); return 0; } } else { if (index >= meta.meta.Length() || (-index) > meta.meta.Length()) { fill_error_reply(ctx.reply, "index out of range"); return 0; } if (meta.meta.IsSequentialList()) { ValueObject list_element; list_element.key.db = meta.key.db; list_element.key.key = meta.key.key; list_element.key.type = LIST_ELEMENT; list_element.key.score = meta.meta.min_index; if (index >= 0) { list_element.key.score.IncrBy(index); } else { list_element.key.score.IncrBy(index + meta.meta.Length()); } if (0 == GetKeyValue(ctx, list_element.key, &list_element)) { list_element.element.SetString(cmd.GetArguments()[2], true); SetKeyValue(ctx, list_element); fill_status_reply(ctx.reply, "OK"); return 0; } } else { ListIterator iter; ListIter(ctx, meta, iter, index < 0); int64 cursor = index >= 0 ? 0 : -1; while (iter.Valid()) { if (cursor == index) { ValueObject v; v.key.db = meta.key.db; v.key.key = meta.key.key; v.key.type = LIST_ELEMENT; v.key.score = *(iter.Score()); v.type = LIST_ELEMENT; v.element.SetString(cmd.GetArguments()[2], true); SetKeyValue(ctx, v); fill_status_reply(ctx.reply, "OK"); return 0; } if (cursor >= 0) { cursor++; } else { cursor--; } if (index < 0) { iter.Prev(); } else { iter.Next(); } } } fill_error_reply(ctx.reply, "index out of range"); } return 0; }
int Ardb::LTrim(Context& ctx, RedisCommandFrame& cmd) { int64 start, end; if (!GetInt64Value(ctx, cmd.GetArguments()[1], start) || !GetInt64Value(ctx, cmd.GetArguments()[2], end)) { return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (0 != err) { fill_status_reply(ctx.reply, "OK"); return 0; } /* convert negative indexes */ if (start < 0) start = meta.meta.Length() + start; if (end < 0) end = meta.meta.Length() + end; if (start < 0) start = 0; if (end >= meta.meta.Length()) end = meta.meta.Length() - 1; /* Invariant: start >= 0, so this test will be true when end < 0. * The range is empty when start > end or start >= length. */ if (start > end || start >= meta.meta.Length()) { /* Out of range start or start > end result in empty list */ DeleteKey(ctx, cmd.GetArguments()[0]); return 0; } if (meta.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { DataArray newzip; for (int64 i = start; i <= end; i++) { newzip.push_back(meta.meta.ziplist[i]); } meta.meta.ziplist = newzip; SetKeyValue(ctx, meta); } else { BatchWriteGuard guard(GetKeyValueEngine()); if (meta.meta.IsSequentialList()) { int64 listlen = meta.meta.Length(); for (int64 s = 0; s < listlen; s++) { if (s == start) { s = end; continue; } KeyObject lk; lk.db = meta.key.db; lk.key = meta.key.key; lk.type = LIST_ELEMENT; lk.score = meta.meta.min_index.IncrBy(s); meta.meta.len--; DelKeyValue(ctx, lk); } meta.meta.max_index = meta.meta.min_index; meta.meta.min_index.IncrBy(start); meta.meta.max_index.IncrBy(end); } else { ListIterator iter; ListIter(ctx, meta, iter, false); int64 cursor = 0; while (iter.Valid()) { if (cursor < start || cursor > end) { DelRaw(ctx, iter.CurrentRawKey()); meta.meta.len--; } if (cursor == start) { meta.meta.min_index = *(iter.Element()); } else if (cursor == end) { meta.meta.max_index = *(iter.Element()); } cursor++; iter.Next(); } } SetKeyValue(ctx, meta); } return 0; }
int Ardb::ListPop(Context& ctx, const std::string& key, bool lpop) { ValueObject meta; KeyLockerGuard keylock(m_key_lock, ctx.currentDB, key); int err = GetMetaValue(ctx, key, LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (0 != err) { ctx.reply.type = REDIS_REPLY_NIL; return 0; } BatchWriteGuard guard(GetKeyValueEngine(), meta.meta.Encoding() != COLLECTION_ENCODING_ZIPLIST); if (meta.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { if (!meta.meta.ziplist.empty()) { if (lpop) { Data& data = meta.meta.ziplist.front(); fill_value_reply(ctx.reply, data); meta.meta.ziplist.pop_front(); } else { Data& data = meta.meta.ziplist.back(); fill_value_reply(ctx.reply, data); meta.meta.ziplist.pop_back(); } if (meta.meta.ziplist.empty()) { DelKeyValue(ctx, meta.key); } else { SetKeyValue(ctx, meta); } } else { ctx.reply.type = REDIS_REPLY_NIL; } return 0; } else { bool poped = false; if (meta.meta.IsSequentialList()) { if (meta.meta.Length() > 0) { ValueObject lkv; lkv.key.type = LIST_ELEMENT; lkv.key.key = meta.key.key; lkv.key.db = ctx.currentDB; lkv.key.score = lpop ? meta.meta.min_index : meta.meta.max_index; if (0 == GetKeyValue(ctx, lkv.key, &lkv)) { DelKeyValue(ctx, lkv.key); if (lpop) { meta.meta.min_index.IncrBy(1); } else { meta.meta.max_index.IncrBy(-1); } meta.meta.len--; poped = true; fill_value_reply(ctx.reply, lkv.element); } } } else { ListIterator iter; err = ListIter(ctx, meta, iter, !lpop); while (iter.Valid()) { if (!poped) { fill_value_reply(ctx.reply, *(iter.Element())); poped = true; meta.meta.len--; KeyObject k; k.type = LIST_ELEMENT; k.key = meta.key.key; k.db = ctx.currentDB; k.score = *(iter.Score()); DelKeyValue(ctx, k); } else { if (lpop) { meta.meta.min_index = *(iter.Score()); } else { meta.meta.max_index = *(iter.Score()); } break; } if (lpop) { iter.Next(); } else { iter.Prev(); } } } if (poped) { if (meta.meta.Length() > 0) { SetKeyValue(ctx, meta); } else { DelKeyValue(ctx, meta.key); } } else { ctx.reply.type = REDIS_REPLY_NIL; } return 0; } }
int Ardb::HScan(Context& ctx, RedisCommandFrame& cmd) { std::string pattern; uint32 limit = 10000; //return max 10000 keys one time if (cmd.GetArguments().size() > 2) { for (uint32 i = 2; i < cmd.GetArguments().size(); i++) { if (!strcasecmp(cmd.GetArguments()[i].c_str(), "count")) { if (i + 1 >= cmd.GetArguments().size() || !string_touint32(cmd.GetArguments()[i + 1], limit)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } i++; } else if (!strcasecmp(cmd.GetArguments()[i].c_str(), "match")) { if (i + 1 >= cmd.GetArguments().size()) { fill_error_reply(ctx.reply, "'MATCH' need one args followed"); return 0; } pattern = cmd.GetArguments()[i + 1]; i++; } else { fill_error_reply(ctx.reply, "Syntax error, try scan 0 "); return 0; } } } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); RedisReply& r1 = ctx.reply.AddMember(); RedisReply& r2 = ctx.reply.AddMember(); r2.type = REDIS_REPLY_ARRAY; if (0 != err) { fill_str_reply(r1, "0"); return 0; } const std::string& cursor = cmd.GetArguments()[1]; HashIterator iter; HashIter(ctx, meta, cursor == "0" ? "" : cursor, iter, true); while (iter.Valid()) { const Data* field = iter.Field(); std::string tmp; field->GetDecodeString(tmp); if ((pattern.empty() || fnmatch(pattern.c_str(), tmp.c_str(), 0) == 0)) { RedisReply& rr1 = r2.AddMember(); RedisReply& rr2 = r2.AddMember(); fill_str_reply(rr1, tmp); fill_value_reply(rr2, *(iter.Value())); } if (r2.MemberSize() >= (limit * 2)) { break; } iter.Next(); } if (iter.Valid()) { iter.Next(); const Data* next_field = iter.Field(); std::string tmp; fill_str_reply(r1, next_field->GetDecodeString(tmp)); } else { fill_str_reply(r1, "0"); } return 0; }
int Ardb::ListRange(Context& ctx, const Slice& key, int64 start, int64 end) { ValueObject meta; int err = GetMetaValue(ctx, key, LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); /* convert negative indexes */ if (start < 0) start = meta.meta.Length() + start; if (end < 0) end = meta.meta.Length() + end; if (start < 0) start = 0; /* Invariant: start >= 0, so this test will be true when end < 0. * The range is empty when start > end or start >= length. */ ctx.reply.type = REDIS_REPLY_ARRAY; if (start > end || start >= meta.meta.Length()) { return 0; } if (end >= meta.meta.Length()) end = meta.meta.Length() - 1; int64 rangelen = (end - start) + 1; if (meta.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { uint32 i = start; while (rangelen-- && i < meta.meta.ziplist.size()) { RedisReply& r = ctx.reply.AddMember(); fill_value_reply(r, meta.meta.ziplist[i++]); } } else { ListIterator iter; if (meta.meta.IsSequentialList()) { SequencialListIter(ctx, meta, iter, start); uint32 count = 0; while (iter.Valid() && count < rangelen) { RedisReply& r = ctx.reply.AddMember(); fill_value_reply(r, iter.Element()); count++; iter.Next(); } } else { if (start > meta.meta.len / 2) { start -= meta.meta.len; end -= meta.meta.len; } ListIter(ctx, meta, iter, start < 0); int64 cursor = start < 0 ? -1 : 0; while (iter.Valid()) { if (cursor >= start && cursor <= end) { RedisReply& r = ctx.reply.AddMember(); fill_value_reply(r, iter.Element()); } if (start < 0) { if (cursor < start) { break; } cursor--; } else { if (cursor > end) { break; } cursor++; } iter.Next(); } } } return 0; }
int Ardb::GeoSearch(Context& ctx, RedisCommandFrame& cmd) { GeoSearchOptions options; std::string err; if (0 != options.Parse(cmd.GetArguments(), err, 1)) { fill_error_reply(ctx.reply, "%s", err.c_str()); return 0; } ValueObject meta; int ret = GetMetaValue(ctx, cmd.GetArguments()[0], ZSET_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, ret); ctx.reply.type = REDIS_REPLY_ARRAY; if (0 != ret) { return 0; } uint64 t1 = get_current_epoch_millis(); uint32 min_radius = 75; //magic number if (options.asc && options.limit > 0 && options.offset == 0 && options.radius > min_radius) { uint32 old_radius = options.radius; options.radius = min_radius; do { ctx.reply.Clear(); int point_num = GeoSearchByOptions(ctx, meta, options); if(point_num < 0) { break; } if (options.radius == old_radius) { break; } if (point_num > 0) { if (point_num >= options.limit) { break; } options.radius *= sqrt((options.limit / point_num) + 1); } else { options.radius *= 8; } if (options.radius > old_radius) { options.radius = old_radius; } } while (options.radius <= old_radius); } else { GeoSearchByOptions(ctx, meta, options); } ctx.reply.type = REDIS_REPLY_ARRAY; uint64 t2 = get_current_epoch_millis(); DEBUG_LOG("####Cost %llums to range geosearch", t2 - t1); return 0; }
int Ardb::LRem(Context& ctx, RedisCommandFrame& cmd) { int64 count; if (!GetInt64Value(ctx, cmd.GetArguments()[1], count)) { return 0; } int64 toremove = std::abs(count); ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (0 != err) { fill_int_reply(ctx.reply, 0); return 0; } Data element; element.SetString(cmd.GetArguments()[2], true); KeyLockerGuard lock(m_key_lock, ctx.currentDB, cmd.GetArguments()[0]); if (meta.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { uint32 oldlen = meta.meta.ziplist.size(); int64 removed = 0; DataArray newzip; if (count >= 0) { for (uint32 i = 0; i < oldlen; i++) { if (meta.meta.ziplist[i] == element) { if (toremove == 0 || removed < toremove) { removed++; continue; } } newzip.push_back(meta.meta.ziplist[i]); } } else { for (uint32 i = 0; i < oldlen; i++) { if (meta.meta.ziplist[oldlen - 1 - i] == element) { if (toremove == 0 || removed < toremove) { removed++; continue; } } newzip.push_front(meta.meta.ziplist[i]); } } if (removed > 0) { meta.meta.ziplist = newzip; SetKeyValue(ctx, meta); } fill_int_reply(ctx.reply, removed); return 0; } BatchWriteGuard guard(GetKeyValueEngine()); ListIterator iter; ListIter(ctx, meta, iter, count < 0); int64 remove = 0; while (iter.Valid()) { if (iter.Element()->Compare(element) == 0) { meta.meta.len--; meta.meta.SetFlag(COLLECTION_FLAG_NORMAL); DelRaw(ctx, iter.CurrentRawKey()); //DelKeyValue(ctx, k); remove++; if (remove == toremove) { break; } } if (count < 0) { iter.Prev(); } else { iter.Next(); } } if (remove > 0) { SetKeyValue(ctx, meta); } fill_int_reply(ctx.reply, remove); return 0; }