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

时间:2016-11-03 15:07:52

标签: 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()

如果相关Item包含搜索,我想在Containertext个模型QuerySet字段中进行搜索,并返回ContainerItem个模型图案

1 个答案:

答案 0 :(得分:3)

这是方式:

  1. 请务必使用django -gte 1.10
  2. 请确保'django.contrib.postgres',
  3. 上有INSTALLED_APPS
  4. 按照您的问题创建两个模型。
  5. 只需使用一些数据进行迁移,迁移和填充模型:
  6. 使用数据填充模型:

    from fts.models import Item, Container    
    c=Container.objects.create( text = "hello" )  
    Item.objects.create( text ="Some word", container = c ) 
    
    1. 此时您已准备好进行查询:
    2. 查询和检查结果:

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

      预期结果:

      <QuerySet [<Container: Container object>]>
      
      1. 为了确保您的查询使用完整搜索postgres功能,您可以打印底层的SQL:
      2. 要求底层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
        

        在行动中:

        django and postgres fts

        <强>性能:

        当您混合来自多个表的字段时,我不知道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)