我试图在PostgreSQL中使用{"Key":{"key":"someNumber"},"keys":[{"key":"10"},{"key":"11"},{"key":"12"},{"key":"13"},{"key":"14"}]}
#######
someNumber
10
11
12
13
14
扩展来构建全文地址搜索引擎。
我的模型看起来像这样(它稍微简化了一下):
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
我以这种方式做到这一点,因此用户可以执行像“洛杉矶”这样的查询。或类似的。 使用原始 PostgreSQL查询我没有问题:
id | full_name | path
-------------------------------------------------
1 | USA | 1
2 | California USA | 1.2
3 | Los Angeles California USA | 1.2.3
效果很好,但在使用 RawQuerySet 时,我无法使用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;
,.filter()
,分页等内容。
在Django中重现它的主要限制是 JOIN :
.group_by()
它用于连接每个元素的所有祖先,然后使用JOIN "Addresses" AS a ON (a.path @> s.path)
,array_agg()
函数聚合它们,因此这些函数的输出可以在 full-中进一步使用文字搜索。
如果有人有更好的想法如何使用 Django ORM 来实现这类事情,请提供建议。
答案 0 :(得分:2)
您需要一个由VIEW支持的非托管模型。
通过将模型的managed元选项设置为false,可以创建非托管模型。
如果为False,则不会创建或删除数据库表操作 为这个模型执行。如果模型代表一个,这很有用 现有表格或其他人创建的数据库视图 手段。当managed = False时,这是唯一的区别。所有其他 模型处理的各个方面与正常情况完全相同。这个 包括
强调我的。
因此,如果您创建一个非托管模型,它可以由数据库上的视图表示,并且您可以访问.group_by()
,CREATE OR REPLACE view full_address_tree AS
SELECT a.*, 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
。
视图是您的查询。
class FullAddressTree(models.Model):
# copy paste the fields from your Addresses model here
sid = models.IntegerField()
sid = models.CharField()
class Meta:
# this is the most important part
managed = False
db_table = 'full_address_tree' # the name of the view
operations
因此,现在您有了一个可用于进行全文搜索的模型,而无需求助于原始查询。因此,您可以随意使用Django ORM的全部功能。
如果您想要迁移,您会发现./manage.py makemigrations会导致虚拟迁移。 ./manage.py sqlmigrate将显示没有为此迁移执行sql查询。
要修复它并自动创建视图,请将RunSQL调用添加到该迁移中的migrations.RunSQL(''' COPY PASTE SQL QUERY FROM ABOVE ''')
列表中。
$user = $this->find($id);
$user->passports()->delete();
$new[] = new UserPassport(['passport' => '123456789', 'created_by' => $user->id, 'updated_by' => $user->id]);
$new[] = new UserPassport(['passport' => '111111111', 'created_by' => $user->id, 'updated_by' => $user->id]);
$user->passports()->saveMany($new);
您创建的非托管模型是只读的。尝试创建,替换,更新或删除将失败。如果您需要此功能,则需要INSTEAD触发器。
答案 1 :(得分:1)
如此大的+1 @ shang-wang对django-mptt的建议。使用它可以解决您的问题,因为MPTT中的所有树操作都是常规QuerySet
,因此可以链接到annotate
和aggregate
。我唯一不确定的是你的问题是否插入沉重。如果您只是计划将大量数据转储到表中,那么没什么大不了的。如果您要经常修改树,那么可能会出现问题。有关MPTT及其工作原理的详细说明http://www.sitepoint.com/hierarchical-data-database-2/
无论如何,你获得一个节点的所有祖先的原始问题就变成了
la_node.get_ancestors()
。这解决了你提到的连接约束,它可以重新构造查询的其余部分。