GEORADIUSBYMEMBER:查找指定位置半径范围内的其他位置

GEORADIUSBYMEMBER 命令的作用和 GEORADIUS 命令的作用一样,都是找出中心点指定半径范围内的其他位置,这两个命令的主要区别在于 GEORADIUS 命令通过给定经纬度来指定中心点,而 GEORADIUSBYMEMBER 命令则通过选择位置集合中的一个位置作为中心点:

GEORADIUSBYMEMBER location_set name radius unit [WITHDIST] [WITHCOORD] [ASC|DESC]
[COUNT n]

除了指定中心点时使用的参数不一样之外,GEORADIUSBYMEMBER 命令中的其他参数和选项的意义都与 GEORADIUS 命令一样。

作为例子,以下代码展示了如何使用 Guangdong-cities 位置集合中的 "Guangzhou" 作为中心点,查找位于它半径 150km 内的所有城市:

redis> GEORADIUSBYMEMBER Guangdong-cities Guangzhou 150 km WITHDIST ASC
1) 1) "Guangzhou"
2) "0.0000"
2) 1) "Foshan"
2) "17.9819"
3) 1) "Qingyuan"
2) "52.0944"
4) 1) "Dongguan"
2) "60.3114"
5) 1) "Zhongshan"
2) "70.7415"
6) 1) "Shenzhen"
2) "105.8068"

注意,GEORADIUSBYMEMBER 命令在返回结果的时候,会把作为中心点的位置也一并返回。

比如在上面展示的命令调用中,作为中心点的 "Guangzhou" 就出现在了命令的结果里面。

其他信息

  • 复杂度:O(N),其中N为命令实施范围查找时检查的位置数量。

  • 版本要求:GEORADIUSBYMEMBER命令从Redis 3.2.0版本开始可用。

示例:查找附近用户

在前面的内容中,我们学习了如何使用 GEOADD、GEOPOS 等命令去构建一个具有基本功能的用户地理位置程序。在了解了GEORADIUS命令和 GEORADIUSBYMEMBER命令之后,我们可以为这个用户地理位置程序添加一个 “寻找附近用户” 的功能,用户可以通过这个功能来找到所有位于指定半径范围内的其他用户。

除此之外,我们还可以在 “寻找附近用户” 功能的基础上做一些修改,让程序不仅可以返回所有附近用户,还可以在所有附近用户中随机选择一个用户进行返回,类似于微信上的“摇一摇”功能。

代码清单9-2展示了添加新功能之后的用户地理位置程序,其中 find_nearby()和find_random_nearby()方法分别实现了“寻找附近用户”和“随机返回一个附近用户”的功能。

代码清单9-2 添加新功能之后的用户地理位置程序:/geo/location.py
import random

USER_LOCATION_KEY = "user_locations"

class Location:

    def __init__(self, client):
        self.client = client
        self.key = USER_LOCATION_KEY

    def pin(self, user, longitude, latitude):
        """
        记录指定用户的坐标。
        """
        self.client.geoadd(self.key, longitude, latitude, user)

    def get(self, user):
        """
        获取指定用户的坐标。
        """
        position_list = self.client.geopos(self.key, user)
        # geopos() 允许用户输入多个位置,然后以列表形式返回各个位置的坐标。
        # 因为我们这里只传入了一个位置,所以只需要取出列表的第一个元素即可。
        if len(position_list) != 0:
            return position_list[0]

    def calculate_distance(self, user_a, user_b):
        """
        以公里为单位,计算两个用户之间的直线距离。
        """
        return self.client.geodist(self.key, user_a, user_b, unit="km")

    def find_nearby(self, user, radius=1):
        """
        以公里为单位,寻找并返回 user 指定半径范围内的所有其他用户。
        """
        all_nearby_users = self.client.georadiusbymember(self.key, user, radius, unit="km")
        # 因为 georadiusbymember() 方法会把 user 本身也包含在结果里面,
        # 但由于我们并不需要这个用户,所以使用 remove() 方法移除他
        all_nearby_users.remove(user)
        return all_nearby_users

    def find_random_nearby(self, user, radius=1):
        """
        以公里为单位,随机地返回一个位于 user 指定半径范围内的其他用户。
        """
        # random.choice() 方法用于从列表中随机地选择并返回一个项
        return random.choice(self.find_nearby(user, radius))

以下代码简单地展示了 find_nearby() 和 find_random_nearby() 这两个方法的用法:

>>> from redis import Redis
>>> from location import Location
>>> client = Redis(decode_responses=True)
>>> location = Location(client)
>>> location.pin("peter", 113.0419413, 23.6936457) # 添加5个用户的位置
>>> location.pin("jack", 113.0399136, 23.6951166)
>>> location.pin("tom", 113.0398344, 23.6945014)
>>> location.pin("mary", 113.0398344, 23.6945014)
>>> location.pin("david", 113.0398861, 23.6933749)
>>> location.find_nearby("peter") # 寻找peter附近的所有用户
['david', 'mary', 'tom', 'jack']
>>> location.find_random_nearby("peter") # 随机地返回peter附近的一位用户
'mary'
>>> location.find_random_nearby("peter")
'jack'
>>> location.find_random_nearby("peter")
'david'