如何将空间 JOIN 翻译成 Django 查询语言?
Posted
技术标签:
【中文标题】如何将空间 JOIN 翻译成 Django 查询语言?【英文标题】:How could I translate a spatial JOIN into Django query language? 【发布时间】:2021-08-23 09:58:59 【问题描述】:上下文
我有两个表 app_area
和 app_point
以任何方式不相关(没有外键)期望每个表都有一个几何字段,因此我们可以分别在空间上查询它们 polygon
和 point
类型。裸模型看起来像:
from django.contrib.gis.db import models
class Point(models.Model):
# ...
geom = models.PointField(srid=4326)
class Area(models.Model):
# ...
geom = models.PolygonField(srid=4326)
我想创建一个过滤掉多边形中不包含的点的查询。 如果我必须用 Postgis/SQL 语句编写它来执行这个任务,我会发出这种查询:
SELECT
P.*
FROM
app_area AS A JOIN app_point AS P ON ST_Contains(A.geom, P.geom);
在定义空间索引时,这既简单又高效。
我关心的是在我的 Django 应用程序中编写这个查询而不用硬编码 SQL。因此,我想使用经典的 Django 查询语法将其委托给 ORM。
问题
我在互联网上找不到这种查询的明确示例,我找到了解决方案:
要么依赖使用ForeignKeyField
或prefetch_related
的预定义关系(但在我的情况下不存在这种关系);
或者使用单个手工制作的几何图形来表示多边形(但这不是我的用例,因为我想依赖另一个表作为多边形源)。
我觉得使用 Django 绝对可以实现,但也许我对这个框架太陌生,或者它没有足够的文档记录,或者我没有找到合适的关键字来搜索它。
我可以在官方文档中找到的最好的对象是 FilteredRelation
对象,它似乎可以满足我的要求:定义 JOIN
子句的 ON
部分,但我无法正确设置,主要是我没有不了解如何填写其他表格并指向正确的字段。
from django.db.models import F, Q, FilteredRelation
query = Location.objects.annotate(
campus=FilteredRelation(<relation_name>, condition=Q(geom__contains=F("geom")))
)
主要是relation_name
这个字段让我困惑。我希望它是我要加入的表(这里是Area
),但它似乎是一个预期的列名。
django.core.exceptions.FieldError: Cannot resolve keyword 'Area' into field. Choices are: created, geom, id, ...
但是这个字段列表来自Point
表。
我的问题是:如何将我的空间 JOIN
翻译成 Django 查询语言?
注意:不需要依赖FilteredRelation
对象,这只是我目前找到的最佳匹配!
更新
我可以使用extra
模拟预期的输出:
results = models.Point.objects.extra(
where=["ST_intersects(app_area.geom, app_point.geom)"],
tables=["app_area"]
)
它返回一个QuerySet
,但它仍然需要注入普通的SQL语句,然后生成的SQL在子句方面是不等价的:
SELECT "app_point"."id", "app_point"."geom"::bytea
FROM "app_point", "app_area"
WHERE (ST_intersects(app_area.geom, app_point.geom))
还有EXPLAIN
的表演。
【问题讨论】:
【参考方案1】:我认为,最好的解决方案是聚合这些区域,然后与这些点进行交叉。
from django.db.models import Q
from django.contrib.gis.db.models.aggregates import Union
multipolygon_area = Area.objects.aggregate(area=Union("geom"))["area"]
# Get all points inside areas
Points.objects.filter(geom__intersects=multipolygon_area)
# Get all points outside areas
Points.objects.filter(~Q(geom__intersects=multipolygon_area))
这非常有效,因为它完全是在数据库级别计算的。
The idea was found here
【讨论】:
以上是关于如何将空间 JOIN 翻译成 Django 查询语言?的主要内容,如果未能解决你的问题,请参考以下文章
将 mongodb 聚合查询翻译成 Java/Kotlin Spring Data