2016-11-03 24 views
2

我在我的Django項目中使用https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/search/。如何添加反向相關模型來搜索矢量?Django postgres反向相關模型中的全文搜索

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

class Item(models.Model): 
    container = models.ForeignKey(Container) 
    text = models.TextField() 

我想在這兩個ItemContainer模型text字段進行搜索和返回的Container模式QuerySet如果涉及Item包含搜索模式

回答

3

這是這樣的:

  1. 確保你使用django -gte 1.10
  2. 確保您的'django.contrib.postgres',在您的INSTALLED_APPS
  3. 在你的問題中創建你的兩個模型。
  4. 只是makemigrations,一些數據遷移和填充模型:數據

填充型號:

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 
    

    在行動:

    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) 
    
    +0

    我覺得如果你有'''項目= models.ForeignKey(項目)''集裝箱'應該工作。但在我的片段中,我在Item模型中有Container的外鍵,它在Container中創建了反向關係'''item_set'',因此SearchVector必須包含這些''''item_set'''條目。 – svfat

    +1

    如果您要求某件事,而22K的某個人向您建議了一個答案,那麼一個好主意就是嘗試一下,而不是說「我認爲這不會起作用」。我發佈了在真實環境中逐步檢查它的答案。 – danihp

    +0

    o_O哇,這真棒回答,我需要一些時間來了解它是如何工作的。感謝您的解決方案! – svfat