在geodjango中按距离(整个table)排序的效率如何

How efficient is it to order by distance (entire table) in geodjango

假设我有以下数据模型

Person(models.Model):
    id       = models.BigAutoField(primary_key=True)
    name     = models.CharField(max_length=50)
    location = models.PointField(srid=4326)

还假设我有一个应用程序可以查询这个 django 后端,这个应用程序的唯一目的是return一个(分页的)注册用户列表,从最近到最远。

目前我有这个查询:

# here we are obtaining all users in ordered form
current_location = me.location
people = Person.objects.distance(current_location).order_by('distance')

# here we are obtaining the first X through pagination
start_index = a
end_index = b

people = people[a:b]

虽然这有效,但速度没有我想要的那么快。

我对这个查询的速度有些担心。如果 table 很大(超过 100 万),那么数据库(Postgres SQL w/ PostGIS)是否不必测量 current_location 和每个 location 之间的距离在对随后的 100 万行执行 order_by 之前的数据库?

有人可以建议如何正确 return 附近的用户以有效的方式按距离排序吗?

如果您想按距离对 table 上的每个条目进行排序,那么它会像预期的那样慢并且没有什么可以做的(我在这个时间点和我的知识知道.)!

您可以按照以下步骤并做出一些假设来提高计算效率:

  1. 启用 spatial indexing on your tables. To do that in GeoDjango, follow the doc instructions 并使其适合您的模型:

    Note

    In PostGIS, ST_Distance_Sphere does not limit the geometry types geographic distance queries are performed with. [4] However, these queries may take a long time, as great-circle distances must be calculated on the fly for every row in the query. This is because the spatial index on traditional geometry fields cannot be used.

    For much better performance on WGS84 distance queries, consider using geography columns in your database instead because they are able to use their spatial index in distance queries. You can tell GeoDjango to use a geography column by setting geography=True in your field definition.

  2. 现在您可以使用一些逻辑约束来缩小查询范围:

    例如:我的用户不会寻找距离他当前位置超过 50 公里的人。

  3. 使用dwithin spatial lookup which utilizes the above mentioned spatial indexing缩小搜索范围,因此速度非常快。

  4. 最后对剩余的行应用 distance 顺序。

最终查询可能如下所示:

current_location = me.location
people = People.objects.filter(
    location__dwithin=(current_location, D(km=50))
).annotate(
    distance=Distance('location', current_location)
).order_by('distance')

P.S: 与其创建自定义分页尝试,不如利用为 django 视图提供的分页方法更有效:

或者您可以使用 Django Rest Framework 并使用它的分页功能: