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")
[<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")
[<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")
[<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")
[<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"))
[<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")))
[<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")
[<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")
[<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")
[<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")
[<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")
[<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.