В прошлом уроке HyperLogLog укладывал миллион посетителей в 12 КБ. Сегодня ещё одна упаковка. У книжного магазина есть пункты выдачи по Москве. Покупатель оформляет заказ, и сайт должен показать ближайшие к нему пункты и расстояние до каждого. Считать расстояние до всех пунктов на каждый запрос долго, а Redis умеет искать точки в радиусе сам.
Номер квадрата на карте
Вспомни бумажную карту города с сеткой: по краям буквы и цифры, а в указателе написано «Арбат, Г-4». Квадрат Г-4 не даёт точный адрес, но сразу сужает поиск: ищешь улицу только внутри одного квадрата и его соседей.
Теперь представь, что каждый квадрат поделили ещё на четыре, каждый из них снова на четыре, и так много раз. Номер самого мелкого квадрата получается длинным, но у него полезное свойство: у близких точек номера начинаются одинаково. Чем длиннее общее начало, тем ближе точки.
Такой номер называется геохешем. Redis считает его для каждой точки и кладёт как очки в sorted set из урока 10. Sorted set хранит элементы по порядку очков, поэтому близкие точки оказываются рядом, и поиск по радиусу сводится к нескольким диапазонам очков.
Добавить точки
Координаты точки это долгота и широта. GEOADD принимает их именно в таком порядке, сначала долгота, потом имя точки:
127.0.0.1:6379> GEOADD pickup:moscow 37.5915 55.7494 arbat 37.6040 55.7650 tverskaya 37.6340 55.7560 kitay-gorod 37.6790 55.7890 sokolniki
(integer) 4
127.0.0.1:6379> GEOADD pickup:moscow 37.6040 55.7650 tverskaya
(integer) 0
Как ZADD, команда отвечает числом новых элементов. Повторное добавление tverskaya ничего не добавило.
Внимание к порядку. Онлайн-карты обычно показывают координаты наоборот, «55.7494, 37.5915», сначала широту. Если вставить их в GEOADD как есть, Redis не возразит: долгота 55 и широта 37 тоже существуют, это точка в Средней Азии к востоку от Каспийского моря. Такой пункт выдачи окажется в двух с лишним тысячах километров от Москвы:
127.0.0.1:6379> GEOADD pickup:moscow 55.7494 37.5915 wrong
(integer) 1
127.0.0.1:6379> GEODIST pickup:moscow arbat wrong km
"2433.1774"
127.0.0.1:6379> ZREM pickup:moscow wrong
(integer) 1
Последней командой мы убрали ошибочную точку. Почему это ZREM, станет ясно в конце урока.
Ошибку Redis даст, только если число вылезло за допустимые пределы. Широта в Redis ограничена ±85,05 градуса, ближе к полюсам точки не хранятся:
127.0.0.1:6379> GEOADD pickup:moscow 37.6 95 wrong
(error) ERR invalid longitude,latitude pair 37.600000,95.000000
Где точка и сколько до неё
GEOPOS возвращает координаты:
127.0.0.1:6379> GEOPOS pickup:moscow arbat nowhere
1) 1) "37.59149998426437378"
2) "55.74940111916114915"
2) (nil)
Мы записывали 37.5915, а получили 37.59149998… Координаты хранятся в 52-битном геохеше, и при обратном переводе получается центр мелкого квадрата. Разница меньше метра, для пунктов выдачи это неважно. Для nowhere точки нет, ответ (nil).
GEODIST считает расстояние между двумя точками, по умолчанию в метрах. Единицу можно указать: m, km, mi (мили), ft (футы):
127.0.0.1:6379> GEODIST pickup:moscow arbat sokolniki km
"7.0265"
127.0.0.1:6379> GEODIST pickup:moscow arbat tverskaya
"1903.1898"
Это расстояние по прямой над поверхностью Земли, а не путь по улицам. Redis считает Землю шаром, поэтому ошибка доходит до 0,5 %: для поиска «что рядом» хватает с запасом.
урок из курса
Урок из курса «Redis с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.