Fulltextsökning¶
Databasfunktionerna i modulen django.contrib.postgres.search underlättar användningen av PostgreSQLs fulltextsökmotor.
I exemplen i det här dokumentet använder vi de modeller som definieras i Göra förfrågningar.
Se även
För en översikt på hög nivå över sökning, se topic documentation.
Uppslagsordet search¶
Ett vanligt sätt att använda fulltextsökning är att söka en enskild term mot en enskild kolumn i databasen. Till exempel:
>>> Entry.objects.filter(body_text__search="Cheese")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>
Detta skapar en to_tsvector i databasen från fältet body_text och en plainto_tsquery från söktermen 'Cheese', båda med standardkonfigurationen för databassökning. Resultaten erhålls genom att matcha frågan och vektorn.
För att använda search-uppslaget måste 'django.contrib.postgres finnas i din INSTALLED_APPS.
SökVektor¶
Att söka mot ett enda fält är bra men ganska begränsande. De Entry-instanser vi söker tillhör en Blog, som har ett tagline-fält. Om du vill söka mot båda fälten använder du en SearchVector:
>>> from django.contrib.postgres.search import SearchVector
>>> Entry.objects.annotate(
... search=SearchVector("body_text", "blog__tagline"),
... ).filter(search="Cheese")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>
Argumenten till SearchVector kan vara vilken Expression` som helst eller namnet på ett fält. Flera argument kommer att sammanfogas med ett mellanslag så att sökdokumentet innehåller dem alla.
SearchVector-objekt kan kombineras med varandra, vilket gör att du kan återanvända dem. Ett exempel:
>>> Entry.objects.annotate(
... search=SearchVector("body_text") + SearchVector("blog__tagline"),
... ).filter(search="Cheese")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>
Se Ändra sökkonfigurationen och Viktning av frågor för en förklaring av parametrarna config och weight.
Sökfråga¶
SearchQuery översätter de termer som användaren anger till ett sökfrågeobjekt som databasen jämför med en sökvektor. Som standard skickas alla ord som användaren anger genom stamningsalgoritmerna och sedan letar den efter matchningar för alla resulterande termer.
Om search_type är 'plain, vilket är standard, behandlas termerna som separata nyckelord. Om search_type är 'phrase behandlas termerna som en enda fras. Om search_type är 'raw kan du ange en formaterad sökfråga med termer och operatorer. Om search_type är 'websearch kan du ange en formaterad sökfråga som liknar den som används av webbsökmotorer. 'websearch' kräver PostgreSQL ≥ 11. Läs PostgreSQL: s Full Text Search docs för att lära dig mer om skillnader och syntax. Exempel:
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery("red tomato") # two keywords
>>> SearchQuery("tomato red") # same results as above
>>> SearchQuery("red tomato", search_type="phrase") # a phrase
>>> SearchQuery("tomato red", search_type="phrase") # a different phrase
>>> SearchQuery("'tomato' & ('red' | 'green')", search_type="raw") # boolean operators
>>> SearchQuery(
... "'tomato' ('red' OR 'green')", search_type="websearch"
... ) # websearch operators
termerna i SearchQuery kan kombineras logiskt för att ge mer flexibilitet:
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery("meat") & SearchQuery("cheese") # AND
>>> SearchQuery("meat") | SearchQuery("cheese") # OR
>>> ~SearchQuery("meat") # NOT
Se Ändra sökkonfigurationen för en förklaring av parametern config.
SearchRank¶
Hittills har vi returnerat de resultat för vilka någon matchning mellan vektorn och frågan är möjlig. Det är troligt att du kanske vill ordna resultaten efter någon form av relevans. PostgreSQL tillhandahåller en rankningsfunktion som tar hänsyn till hur ofta frågetermerna visas i dokumentet, hur nära varandra termerna är i dokumentet och hur viktig den del av dokumentet är där de förekommer. Ju bättre matchning, desto högre värde på rankningen. För att beställa efter relevans:
>>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
>>> vector = SearchVector("body_text")
>>> query = SearchQuery("cheese")
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).order_by("-rank")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]>
Se Viktning av frågor för en förklaring av parametern weights.
Ställ in parametern cover_density på True för att aktivera täckningsdensitetsrankningen, vilket innebär att närheten mellan matchande söktermer beaktas.
Ange ett heltal till parametern normalization för att styra normalisering av rangordning. Detta heltal är en bitmask, så att du kan kombinera flera beteenden:
>>> from django.db.models import Value
>>> Entry.objects.annotate(
... rank=SearchRank(
... vector,
... query,
... normalization=Value(2).bitor(Value(4)),
... )
... )
PostgreSQL-dokumentationen har mer information om olika rangnormaliseringsalternativ.
Sökhuvud¶
- class SearchHeadline(expression, query, config=None, start_sel=None, stop_sel=None, max_words=None, min_words=None, short_word=None, highlight_all=None, max_fragments=None, fragment_delimiter=None)[source]¶
Accepterar ett enda textfält eller ett uttryck, en fråga, en konfiguration och en uppsättning alternativ. Returnerar markerade sökresultat.
Ställ in parametrarna start_sel och stop_sel till strängvärdena som ska användas för att linda markerade frågetermer i dokumentet. PostgreSQL: s standardvärden är <b> och </b>.
Ge heltalsvärden till parametrarna max_words och min_words för att bestämma de längsta och kortaste rubrikerna. PostgreSQL: s standardvärden är 35 och 15.
Ge ett heltalsvärde till parametern short_word för att kassera ord av denna längd eller mindre i varje rubrik. PostgreSQL: s standard är 3.
Ställ in parametern highlight_all till True för att använda hela dokumentet i stället för ett fragment och ignorera parametrarna max_words, min_words och short_word. Det är inaktiverat som standard i PostgreSQL.
Ge ett heltalsvärde som inte är noll till max_fragments för att ställa in det maximala antalet fragment som ska visas. Det är inaktiverat som standard i PostgreSQL.
Ställ in strängparametern fragment_delimiter för att konfigurera avgränsaren mellan fragment. PostgreSQL: s standard är " ... ".
PostgreSQL-dokumentationen har mer information om upphighlighting search results.
Exempel på användning:
>>> from django.contrib.postgres.search import SearchHeadline, SearchQuery
>>> query = SearchQuery("red tomato")
>>> entry = Entry.objects.annotate(
... headline=SearchHeadline(
... "body_text",
... query,
... start_sel="<span>",
... stop_sel="</span>",
... ),
... ).get()
>>> print(entry.headline)
Sandwich with <span>tomato</span> and <span>red</span> cheese.
Se Ändra sökkonfigurationen för en förklaring av parametern config.
Ändra sökkonfigurationen¶
Du kan ange attributet config till en SearchVector och SearchQuery för att använda en annan sökkonfiguration. Detta gör det möjligt att använda olika språkparsers och ordböcker som definieras av databasen:
>>> from django.contrib.postgres.search import SearchQuery, SearchVector
>>> Entry.objects.annotate(
... search=SearchVector("body_text", config="french"),
... ).filter(search=SearchQuery("œuf", config="french"))
<QuerySet [<Entry: Pain perdu>]>
Värdet på config kan också lagras i en annan kolumn:
>>> from django.db.models import F
>>> Entry.objects.annotate(
... search=SearchVector("body_text", config=F("blog__language")),
... ).filter(search=SearchQuery("œuf", config=F("blog__language")))
<QuerySet [<Entry: Pain perdu>]>
Viktning av frågor¶
Alla fält kanske inte har samma relevans i en fråga, så du kan ställa in vikter för olika vektorer innan du kombinerar dem:
>>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
>>> vector = SearchVector("body_text", weight="A") + SearchVector(
... "blog__tagline", weight="B"
... )
>>> query = SearchQuery("cheese")
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.3).order_by(
... "rank"
... )
Vikten ska vara en av följande bokstäver: D, C, B, A. Som standard hänvisar dessa vikter till siffrorna 0,1, 0,2, 0,4 respektive 1,0. Om du vill vikta dem annorlunda, skicka en lista med fyra flyttal till SearchRank som vikter i samma ordning som ovan:
>>> rank = SearchRank(vector, query, weights=[0.2, 0.4, 0.6, 0.8])
>>> Entry.objects.annotate(rank=rank).filter(rank__gte=0.3).order_by("-rank")
Prestanda¶
Det krävs ingen särskild databaskonfiguration för att använda någon av dessa funktioner, men om du söker efter mer än några hundra poster kommer du sannolikt att stöta på prestandaproblem. Fulltextsökning är en mer intensiv process än att t.ex. jämföra storleken på ett heltal.
Om alla fält som du söker på finns i en viss modell kan du skapa ett funktionellt GIN eller GiST index som matchar den sökvektor du vill använda. Till exempel:
GinIndex(
SearchVector("body_text", "headline", config="english"),
name="search_vector_idx",
)
PostgreSQL-dokumentationen har detaljer om att skapa index för fulltextsökning <https://www.postgresql.org/docs/current/textsearch-tables.html#TEXTSEARCH-TABLES-INDEX>`_.
SökVektorField¶
Om detta tillvägagångssätt blir för långsamt kan du lägga till en SearchVectorField i din modell. Du måste hålla den befolkad med triggers, till exempel, som beskrivs i PostgreSQL-dokumentationen. Du kan sedan fråga fältet som om det var en annoterad SearchVector:
>>> Entry.objects.update(search_vector=SearchVector("body_text"))
>>> Entry.objects.filter(search_vector="cheese")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]>
Likhet mellan trigram¶
En annan metod för sökning är trigramlikhet. Ett trigram är en grupp med tre på varandra följande tecken. Förutom uppslagen trigram_similar, trigram_word_similar och trigram_strict_word_similar kan du använda ett par andra uttryck.
För att använda dem måste du aktivera pg_trgm-tillägget på PostgreSQL. Du kan installera den med hjälp av TrigramExtension migreringsoperation.
TrigramSimilarity¶
Accepterar ett fältnamn eller uttryck och en sträng eller ett uttryck. Returnerar trigramlikheten mellan de två argumenten.
Exempel på användning:
>>> from django.contrib.postgres.search import TrigramSimilarity
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Katie Stephens"
>>> Author.objects.annotate(
... similarity=TrigramSimilarity("name", test),
... ).filter(
... similarity__gt=0.3
... ).order_by("-similarity")
<QuerySet [<Author: Katy Stevens>, <Author: Stephen Keats>]>
Trigramordlikhet¶
Accepterar en sträng eller ett uttryck och ett fältnamn eller ett uttryck. Returnerar trigramordlikheten mellan de två argumenten.
Exempel på användning:
>>> from django.contrib.postgres.search import TrigramWordSimilarity
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Kat"
>>> Author.objects.annotate(
... similarity=TrigramWordSimilarity(test, "name"),
... ).filter(
... similarity__gt=0.3
... ).order_by("-similarity")
<QuerySet [<Author: Katy Stevens>]>
TrigramStrictWordSimilarity¶
Accepterar en sträng eller ett uttryck och ett fältnamn eller ett uttryck. Returnerar den trigramstrikta ordlikheten mellan de två argumenten. Liknar TrigramWordSimilarity(), förutom att den tvingar gränser för utsträckning att matcha ordgränser.
TrigramDistance¶
Accepterar ett fältnamn eller uttryck och en sträng eller ett uttryck. Returnerar trigramavståndet mellan de två argumenten.
Exempel på användning:
>>> from django.contrib.postgres.search import TrigramDistance
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Katie Stephens"
>>> Author.objects.annotate(
... distance=TrigramDistance("name", test),
... ).filter(
... distance__lte=0.7
... ).order_by("distance")
<QuerySet [<Author: Katy Stevens>, <Author: Stephen Keats>]>
TrigramWordDistance¶
Accepterar en sträng eller ett uttryck och ett fältnamn eller ett uttryck. Returnerar trigramordavståndet mellan de två argumenten.
Exempel på användning:
>>> from django.contrib.postgres.search import TrigramWordDistance
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Kat"
>>> Author.objects.annotate(
... distance=TrigramWordDistance(test, "name"),
... ).filter(
... distance__lte=0.7
... ).order_by("distance")
<QuerySet [<Author: Katy Stevens>]>
TrigramStrictWordDistance¶
Accepterar en sträng eller ett uttryck och ett fältnamn eller ett uttryck. Returnerar det trigramstrikta ordavståndet mellan de två argumenten.