【发布时间】:2015-12-30 16:41:55
【问题描述】:
我正在尝试使用 PostgreSQL 中的ltree 扩展来构建全文地址搜索引擎。
我的模型看起来像这样(稍微简化了):
from django.db import models
class Addresses(models.Model):
name = models.CharField(max_length=255)
path = models.CharField(max_length=255)
因此,此表中的数据将如下所示:
id | name | path
----------------------------
1 | USA | 1
2 | California | 1.2
3 | Los Angeles | 1.2.3
我想对每个实体的聚合名称进行全文搜索。 基本上我需要将表格中的每一行转换为下一种格式来进行搜索:
id | full_name | path
-------------------------------------------------
1 | USA | 1
2 | California USA | 1.2
3 | Los Angeles California USA | 1.2.3
我正在这样做,因此用户可以执行诸如“los ang cali”之类的查询或类似的查询。 我对 raw PostgreSQL 查询没有任何问题:
SELECT *, ts_rank_cd(to_tsvector('english', full_address), query) AS rank
FROM (SELECT s.id, s.path, array_to_string(array_agg(a.name ORDER BY a.path DESC), ' ') AS full_address
FROM "Addresses" AS s INNER JOIN "Addresses" AS a
ON (a.path @> s.path) GROUP BY s.id, s.path, s.name
) AS subquery, to_tsquery('english', %s) as query WHERE to_tsvector('english', full_address) @@ query
ORDER BY rank DESC;
这很好,但是在使用 RawQuerySet 时,我无法使用 .filter()、.group_by()、分页等内容。
在 Django 中重现它的主要限制是 JOIN:
JOIN "Addresses" AS a ON (a.path @> s.path)
它用于连接每个元素的所有祖先,然后使用array_agg()、array_to_string 函数聚合它们,因此这些函数的输出可以在全文搜索中进一步使用。
如果有人对如何使用 Django ORM 实现这种事情有更好的想法,请指教。
【问题讨论】:
-
我可能会建议为此使用 django-mptt 包。它将使您的模型具有树结构,因此您可以将 USA 作为父节点,将所有州作为它的子树,等等。使用该包,您可以在一个查询中获取子/父级,而无需向下/向上移动树。我不是 100% 确定这是您想要的,但请查看 github.com/django-mptt/django-mptt
-
如果不是通过已知字段或使用额外/原始的东西,Django 没有办法像那样加入。
-
我有一个想法,但我不知道@> 运算符。它在我的 postgresql 安装 (9.5) 中不存在。您的意思是 >= 吗?使用此运算符,您可以创建上面提到的表结构。
-
它实际上是 ltree 的特定运算符 - postgresql.org/docs/9.5/static/ltree.html。这是 Django 使用 ltree 的一些解决方法 - github.com/whitglint/ltreefield
-
您对我的回答中的非托管表有任何运气吗?
标签: python django postgresql