就是这样:
- 确保您使用的是 django -gte 1.10
- 确保您的
INSTALLED_APPS 上有'django.contrib.postgres',
- 按照您的问题创建两个模型。
- 只需进行迁移、迁移并使用一些数据填充模型:
用数据填充模型:
from fts.models import Item, Container
c=Container.objects.create( text = "hello" )
Item.objects.create( text ="Some word", container = c )
- 此时您已准备好进行查询:
查询和检查结果:
from django.contrib.postgres.search import SearchVector
>>> ( Container
... .objects
... .annotate(search=SearchVector('text', 'item__text'),)
... .filter(search='Some word')
... .distinct()
... )
结果如预期:
<QuerySet [<Container: Container object>]>
- 为了确保您的查询使用完整的搜索 postgres 功能正常工作,您可以打印底层 sql:
询问底层SQL:
>>> print ( Container
.objects
.annotate(search=SearchVector('text', 'item__text'),)
.filter(search='Some word')
).query
结果是:
SELECT
"fts_container".
"id", "fts_container".
"text",
to_tsvector(COALESCE("fts_container"."text", )
|| ' ' ||
COALESCE("fts_item"."text", )) AS "search"
FROM
"fts_container"
LEFT OUTER JOIN
"fts_item"
ON("fts_container"."id" = "fts_item"."container_id")
WHERE to_tsvector(
COALESCE("fts_container"."text", )
|| ' ' ||
COALESCE("fts_item"."text", )
)@@(plainto_tsquery(Some word)) = true
实际操作:
性能:
我不知道当您混合来自多个表的字段时,postgres 是否能够利用索引的完整搜索功能。但是很容易检查它。创建全文索引和ANALYZE您的表后,您可以询问sql计划:
fts=> EXPLAIN SELECT
fts-> "fts_container".
fts-> "id", "fts_container".
fts-> "text",
fts-> to_tsvector(COALESCE("fts_container"."text", '' )
fts(> || ' ' ||
fts(> COALESCE("fts_item"."text", '' )) AS "search"
fts-> FROM
fts-> "fts_container"
fts-> LEFT OUTER JOIN
fts-> "fts_item"
fts-> ON("fts_container"."id" = "fts_item"."container_id")
fts-> WHERE to_tsvector(
fts(> COALESCE("fts_container"."text", '' )
fts(> || ' ' ||
fts(> COALESCE("fts_item"."text",'' )
fts(> )@@(plainto_tsquery('Some word')) = true
fts-> ;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Hash Right Join (cost=1.04..2.15 rows=1 width=68)
Hash Cond: (fts_item.container_id = fts_container.id)
Filter: (to_tsvector(((COALESCE(fts_container.text, ''::text) || ' '::text) || COALESCE(fts_item.text, ''::text))) @@ plainto_tsquery('Some word'::text))
-> Seq Scan on fts_item (cost=0.00..1.04 rows=4 width=36)
-> Hash (cost=1.02..1.02 rows=2 width=36)
-> Seq Scan on fts_container (cost=0.00..1.02 rows=2 width=36)
(6 rows)