Django postgres 反向相关模型中的全文搜索

svf*_*fat 3 django postgresql

我在 Django 项目中使用https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/search/ 。如何将反向相关模型添加到搜索向量中?

class Container(models.Model):
    text = models.TextField()

class Item(models.Model):
    container = models.ForeignKey(Container)
    text = models.TextField()
Run Code Online (Sandbox Code Playgroud)

我想在ItemContainer模型text字段中进行搜索,并返回QuerySet模型Container(如果相关Item包含搜索模式)

dan*_*era 7

方法是这样的:

  1. 确保您使用的是 django -gte 1.10
  2. 请确保您'django.contrib.postgres',INSTALLED_APPS
  3. 按照您的问题创建两个模型。
  4. 只需进行迁移、迁移并使用一些数据填充模型即可:

用数据填充模型:

from fts.models import Item, Container    
c=Container.objects.create( text = "hello" )  
Item.objects.create( text ="Some word", container = c ) 
Run Code Online (Sandbox Code Playgroud)
  1. 此时您已准备好进行查询:

查询和检查结果:

from django.contrib.postgres.search import SearchVector
>>> ( Container
...       .objects
...       .annotate(search=SearchVector('text', 'item__text'),)
...       .filter(search='Some word')
...       .distinct()
...      )
Run Code Online (Sandbox Code Playgroud)

结果符合预期:

<QuerySet [<Container: Container object>]>
Run Code Online (Sandbox Code Playgroud)
  1. 为了确保您的查询正在使用完整的搜索 postgres 功能,您可以打印底层 sql:

询问底层SQL:

>>> print ( Container
            .objects
            .annotate(search=SearchVector('text', 'item__text'),)
            .filter(search='Some word')
          ).query
Run Code Online (Sandbox Code Playgroud)

结果是:

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
Run Code Online (Sandbox Code Playgroud)

行动中:

Django 和 Postgres fts

表现:

我不知道当您混合多个表中的字段时,postgres 是否能够利用完整搜索功能的索引。但检查起来很容易。创建全文索引并分析表后,您可以询问 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)
Run Code Online (Sandbox Code Playgroud)

  • 如果您提出某种要求,而某个拥有 22K 的人向您建议了答案,那么最好尝试一下,而不是说“我认为这行不通”。我发布了答案,在真实环境中一步步检查它。 (2认同)