7👍
✅
This is the way:
- Be sure you are working with django -gte 1.10
- Be sure you have
'django.contrib.postgres',
on yourINSTALLED_APPS
- Create your both models as in your question.
- Just makemigrations, migrate and populate models with some data:
Populating models with data:
from fts.models import Item, Container
c=Container.objects.create( text = "hello" )
Item.objects.create( text ="Some word", container = c )
- At this point you are ready to make the query:
Querying and checking results:
from django.contrib.postgres.search import SearchVector
>>> ( Container
... .objects
... .annotate(search=SearchVector('text', 'item__text'),)
... .filter(search='Some word')
... .distinct()
... )
Results as expected:
<QuerySet [<Container: Container object>]>
- Just to be sure your query is working using full search postgres capabilities, you can print the underlying sql:
Asking for underlying SQL:
>>> print ( Container
.objects
.annotate(search=SearchVector('text', 'item__text'),)
.filter(search='Some word')
).query
And the result is:
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
In action:
Performance:
I don’t know if postgres is able to take advantage from index on full search capabilities when you are mixing fields from several tables. But is easy to check it. After create full text indexes and ANALYZE your tables you can ask about sql plan:
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)
Source:stackexchange.com