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::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::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::GeoSearchByOptions(Context& ctx, ValueObject& meta, GeoSearchOptions& options) { uint64 start_time = get_current_epoch_micros(); int ret = 0; double x = options.x, y = options.y; if (options.by_member) { Data element, score; element.SetString(options.member, true); ret = ZSetScore(ctx, meta, element, score); if (0 != ret || score.IsNil()) { return -1; } GeoHashHelper::GetMercatorXYByHash(score.value.iv, x, y); //GeoHashHelper::GetXYByHash(score.value.iv, x, y); } else { if (options.coord_type != GEO_MERCATOR_TYPE) { x = GeoHashHelper::GetMercatorX(options.x); y = GeoHashHelper::GetMercatorY(options.y); } } DEBUG_LOG("####Step1: Cost %lluus", get_current_epoch_micros() - start_time); GeoPointArray points; ZSetRangeByScoreOptions fetch_options; fetch_options.withscores = false; fetch_options.op = OP_GET; fetch_options.fill_reply = false; fetch_options.fetch_geo_location = true; if (options.in_members) { StringSet::iterator it = options.submembers.begin(); while (it != options.submembers.end()) { Data element, score; element.SetString(*it, true); Location loc; ret = ZSetScore(ctx, meta, element, score, &loc); if (0 == ret) { fetch_options.results.push_back(element); fetch_options.locs.push_back(loc); } it++; } } else { GeoHashBitsSet ress; GeoHashHelper::GetAreasByRadiusV2(GEO_MERCATOR_TYPE, y, x, options.radius, ress); /* * Merge areas if possible to avoid disk search */ std::vector<ZRangeSpec> range_array; GeoHashBitsSet::iterator rit = ress.begin(); typedef TreeMap<uint64, uint64>::Type HashRangeMap; HashRangeMap tmp; while (rit != ress.end()) { GeoHashBits& hash = *rit; GeoHashBits next = hash; next.bits++; tmp[GeoHashHelper::Allign60Bits(hash)] = GeoHashHelper::Allign60Bits(next); rit++; } HashRangeMap::iterator tit = tmp.begin(); HashRangeMap::iterator nit = tmp.begin(); nit++; while (tit != tmp.end()) { ZRangeSpec range; range.contain_min = true; range.contain_max = true; range.min.SetInt64(tit->first); range.max.SetInt64(tit->second); while (nit != tmp.end() && nit->first == range.max.value.iv) { range.max.SetInt64(nit->second); nit++; tit++; } range_array.push_back(range); nit++; tit++; } DEBUG_LOG("After areas merging, reduce searching area size from %u to %u", ress.size(), range_array.size()); std::vector<ZRangeSpec>::iterator hit = range_array.begin(); ZSetIterator* iter = NULL; while (hit != range_array.end()) { ZRangeSpec& range = *hit; uint64 t1 = get_current_epoch_millis(); ZSetRangeByScore(ctx, meta, range, fetch_options, iter); uint64 t2 = get_current_epoch_millis(); DEBUG_LOG("####Cost %llums to range fetch", t2 - t1); hit++; } DELETE(iter); } DEBUG_LOG("####Step2: Cost %lluus", get_current_epoch_micros() - start_time); uint32 outrange = 0; LocationDeque::iterator lit = fetch_options.locs.begin(); DataArray::iterator vit = fetch_options.results.begin(); while (vit != fetch_options.results.end()) { Location& loc = *lit; GeoPoint point; point.x = loc.x; point.y = loc.y; /* * distance accuracy is 0.2m */ if (GeoHashHelper::GetDistanceSquareIfInRadius(GEO_MERCATOR_TYPE, x, y, point.x, point.y, options.radius, point.distance, 0.2)) { vit->GetDecodeString(point.value); /* * filter by exclude/include */ if (!options.includes.empty() || !options.excludes.empty()) { Data subst; subst.SetString(point.value, false); bool matched = options.includes.empty() ? true : false; if (!options.includes.empty()) { StringStringMap::const_iterator sit = options.includes.begin(); while (sit != options.includes.end()) { Data mv; if (0 != MatchValueByPattern(ctx, sit->first, sit->second, subst, mv)) { matched = false; break; } else { matched = true; } sit++; } } if (matched && !options.excludes.empty()) { StringStringMap::const_iterator sit = options.excludes.begin(); while (sit != options.excludes.end()) { Data mv; if (0 == MatchValueByPattern(ctx, sit->first, sit->second, subst, mv)) { matched = false; break; } else { matched = true; } sit++; } } if (matched) { points.push_back(point); } } else { points.push_back(point); } } else { outrange++; } vit++; lit++; } DEBUG_LOG("###Result size:%d, outrange:%d", points.size(), outrange); DEBUG_LOG("####Step3: Cost %lluus", get_current_epoch_micros() - start_time); if (!options.nosort) { std::sort(points.begin(), points.end(), options.asc ? less_by_distance : great_by_distance); } DEBUG_LOG("####Step3.5: Cost %lluus", get_current_epoch_micros() - start_time); if (options.offset > 0) { if ((uint32) options.offset > points.size()) { points.clear(); } else { GeoPointArray::iterator start = points.begin() + options.offset; points.erase(points.begin(), start); } } if (options.limit > 0) { if ((uint32) options.limit < points.size()) { GeoPointArray::iterator end = points.begin() + options.limit; points.erase(end, points.end()); } } DEBUG_LOG("####Step4: Cost %lluus", get_current_epoch_micros() - start_time); ValueObjectMap meta_cache; GeoPointArray::iterator pit = points.begin(); while (pit != points.end()) { RedisReply& r = ctx.reply.AddMember(); fill_str_reply(r, pit->value); GeoGetOptionArray::const_iterator ait = options.get_patterns.begin(); while (ait != options.get_patterns.end()) { if (ait->get_distances) { RedisReply& rr = ctx.reply.AddMember(); rr.type = REDIS_REPLY_STRING; char dbuf[128]; int dlen = snprintf(dbuf, sizeof(dbuf), "%.2f", sqrt(pit->distance)); rr.str.assign(dbuf, dlen); } else if (ait->get_coodinates) { if (options.coord_type == GEO_WGS84_TYPE) { pit->x = GeoHashHelper::GetWGS84X(pit->x); pit->y = GeoHashHelper::GetWGS84Y(pit->y); } RedisReply& rr1 = ctx.reply.AddMember(); RedisReply& rr2 = ctx.reply.AddMember(); if (options.coord_type == GEO_WGS84_TYPE) { fill_double_reply(rr1, pit->x); fill_double_reply(rr2, pit->y); } else { char dbuf[128]; int dlen = snprintf(dbuf, sizeof(dbuf), "%.2f", pit->x); rr1.type = REDIS_REPLY_STRING; rr1.str.assign(dbuf, dlen); dlen = snprintf(dbuf, sizeof(dbuf), "%.2f", pit->y); rr2.type = REDIS_REPLY_STRING; rr2.str.assign(dbuf, dlen); } } else if (ait->hgetall) { std::string keystr(ait->get_pattern.data(), ait->get_pattern.size()); string_replace(keystr, "*", pit->value); RedisReply& rr = ctx.reply.AddMember(); rr.type = REDIS_REPLY_ARRAY; HashGetAll(ctx, keystr, rr); } else { Data v, attr; v.SetString(pit->value, false); GetValueByPattern(ctx, ait->get_pattern, v, attr, &meta_cache); RedisReply& rr = ctx.reply.AddMember(); fill_value_reply(rr, attr); } ait++; } pit++; } DEBUG_LOG("####Step5: Cost %lluus", get_current_epoch_micros() - start_time); uint64 end_time = get_current_epoch_micros(); DEBUG_LOG("Cost %llu microseconds to search.", end_time - start_time); return points.size(); }
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::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::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; } }