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::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; }
void test_geo(Ardb& db) { DBID dbid = 0; db.ZClear(dbid, "mygeo"); double x = 300.3; double y = 300.3; double p_x = 1000.0; double p_y = 1000.0; double raius = 1000; GeoPointArray cmp; for (uint32 i = 0; i < 100000; i++) { char name[100]; sprintf(name, "p%u", i); double xx = x + i * 0.1; double yy = y + i * 0.1; if (((xx - p_x) * (xx - p_x) + (yy - p_y) * (yy - p_y)) < raius * raius) { GeoPoint p; p.x = xx; p.y = yy; cmp.push_back(p); } GeoAddOptions add; add.coord_type = 2; add.x = x + i * 0.1; add.y = y + i * 0.1; add.value = name; db.GeoAdd(dbid, "mygeo", add); } if(db.GetL1Cache() != NULL) { db.GetL1Cache()->SyncLoad(dbid, "mygeo"); } GeoSearchOptions options; StringArray args; std::string err; string_to_string_array("MERCATOR 1000.0 1000.0 RADIUS 1000 ASC WITHCOORDINATES WITHDISTANCES", args); options.Parse(args, err); ValueDataDeque result; db.GeoSearch(dbid, "mygeo", options, result); CHECK_FATAL(cmp.size() != result.size() / 4, "Search failed with %u elements while expected %u", result.size() / 4, cmp.size()); uint64 start = get_current_epoch_millis(); for (uint32 i = 0; i < 10000; i++) { result.clear(); options.x = p_x + i * 0.1; options.y = p_y + i * 0.1; options.radius = 100; db.GeoSearch(dbid, "mygeo", options, result); } uint64 end = get_current_epoch_millis(); for (uint32 i = 0; i < result.size(); i += 4) { INFO_LOG("GeoPoint:%s x:%.2f, y:%.2f, distance:%.2f", result[i].bytes_value.c_str(), result[i + 1].NumberValue(), result[i + 2].NumberValue(), result[i + 3].NumberValue()); } INFO_LOG("Found %d points for search while expected %d points", result.size() / 4, cmp.size()); INFO_LOG("Cost %lldms to geo search 100000 zset elements 10000 times", (end - start)); }