2016-11-03 5 views
2

Ich verwende https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/search/ in meinem Django-Projekt. Wie man umgekehrt in Verbindung stehende Modelle zum Suchvektor hinzufügt?Django Postgres Volltext-Suche rückwärts verwandte Modelle

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

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

ich in beiden Item und Container Modelle text Felder durchsuchen möchten und QuerySet von Container Modelle zurück, wenn Item Zusammenhang enthält Mustersuche

Antwort

3

Dies ist die Art und Weise:

  1. Seien Sie sicher, dass Sie Arbeiten mit Django -gte 1.10
  2. Seien Sie sicher, Sie haben 'django.contrib.postgres', auf Ihrem INSTALLED_APPS
  3. Erstellen Sie Ihre beiden Modelle wie in Ihrer Frage.
  4. Nur makemigrations, wandern und bevölkern Modelle mit einigen Daten:

Bestücken Modelle mit Daten:

from fts.models import Item, Container  
c=Container.objects.create(text = "hello") 
Item.objects.create(text ="Some word", container = c) 
  1. An dieser Stelle Sie bereit sind, die machen query:

Abfragen und Kontrolle Ergebnisse:

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

Ergebnisse wie erwartet:

<QuerySet [<Container: Container object>]> 
  1. Nur um sicherzugehen Ihre Abfrage vollständige Suche Postgres Fähigkeiten mit arbeitet, können Sie die zugrunde liegende SQL ausdrucken:

Bitte um darunter liegende SQL:

>>> print (Container 
      .objects 
      .annotate(search=SearchVector('text', 'item__text'),) 
      .filter(search='Some word') 
     ).query 

und das Ergebnis ist:

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 Aktion:

django and postgres fts

Performance:

Ich weiß nicht, ob Postgres der Lage ist, auf volle Suchfunktionen Vorteil aus dem Index zu nehmen, wenn Sie Mischfelder aus mehreren Tabellen. Aber es ist einfach, es zu überprüfen. Nach dem Erstellen Volltextindizes und ANALYZE Ihre Tabellen können Sie über SQL-Plan fragen:

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

denke ich, das sollte funktionieren, wenn Sie '' 'haben item = models.ForeignKey (Item)' '' in Containern. Aber in meinem Snippet habe ich einen Fremdschlüssel für Container im Item-Modell, der eine umgekehrte Relation '' 'item_set''' in Container erzeugt, also muss SearchVector diese' '' item_set''' Einträge enthalten. – svfat

+1

Wenn Sie nach etwas fragen und jemand mit 22K Ihnen eine Antwort vorschlägt, ist es eine gute Idee, es stattdessen zu versuchen, um zu sagen: "Ich denke, das wird nicht funktionieren".Ich postete die Antwort und überprüfte sie Schritt für Schritt in der realen Umgebung. – danihp

+0

o_O Whoa, das ist eine tolle Antwort, ich brauche etwas Zeit um zu verstehen, wie es funktioniert. Danke für die Lösung! – svfat

Verwandte Themen