GEORADIUS: Don't report duplicates when radius is huge.

Georadius works by computing the center + neighbors squares covering all
the area of the specified position and radius. Then a distance filter is
used to remove elements which are actually outside the range.

When a huge radius is used, like 5000 km or more, adjacent neighbors may
collide and be the same, leading to the reporting of the same element
multiple times. This only happens in the edge case of huge radius but is
not ideal.

A robust but slow solution would involve qsorting the range to remove
all the duplicates. However since the collisions are only in adjacent
boxes, for the way they are ordered in the code, it is much faster to
just check if the current box is the same as the previous one processed.

This commit adds a regression test for the bug.

Fixes #2767.
This commit is contained in:
antirez 2015-09-14 23:05:40 +02:00
parent 0a91fc459f
commit 3c23b5ffd0
2 changed files with 16 additions and 1 deletions

View File

@ -319,7 +319,7 @@ int membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, double lon,
/* Search all eight neighbors + self geohash box */
int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, double radius, geoArray *ga) {
GeoHashBits neighbors[9];
unsigned int i, count = 0;
unsigned int i, count = 0, last_processed = 0;
neighbors[0] = n.hash;
neighbors[1] = n.neighbors.north;
@ -336,7 +336,17 @@ int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, d
for (i = 0; i < sizeof(neighbors) / sizeof(*neighbors); i++) {
if (HASHISZERO(neighbors[i]))
continue;
/* When a huge Radius (in the 5000 km range or more) is used,
* adjacent neighbors can be the same, leading to duplicated
* elements. Skip every range which is the same as the one
* processed previously. */
if (last_processed &&
neighbors[i].bits == neighbors[last_processed].bits &&
neighbors[i].step == neighbors[last_processed].step)
continue;
count += membersOfGeoHashBox(zobj, neighbors[i], ga, lon, lat, radius);
last_processed = i;
}
return count;
}

View File

@ -64,6 +64,11 @@ start_server {tags {"geo"}} {
r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC
} {{wtc one} q4}
test {GEORADIUS HUGE, issue #2767} {
r geoadd users -47.271613776683807 -54.534504198047678 user_000000
llength [r GEORADIUS users 0 0 50000 km WITHCOORD]
} {1}
test {GEORADIUSBYMEMBER simple (sorted)} {
r georadiusbymember nyc "wtc one" 7 km
} {{wtc one} {union square} {central park n/q/r} 4545 {lic market}}