Recherche plein texte¶
Les fonctions de base de données dans le module django.contrib.postgres.search
facilitent lâutilisation du moteur de recherche plein texte de PostgreSQL.
Pour les exemples de ce document, nous utiliserons les modÚles définis dans Making queries.
Voir aussi
Pour un aperçu de haut niveau de la recherche, consultez la documentation thématique.
Lâexpression de recherche search
¶
La façon la plus simple dâutiliser la recherche plein texte est de rechercher un seul terme dans une seule colonne de base de donnĂ©es. Par exemple :
>>> Entry.objects.filter(body_text__search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
Ceci crée un vecteur to_tsvector
dans la base de données à partir du champ body_text
et une requĂȘte plainto_tsquery
Ă partir du terme de recherche 'Cheese'
, les deux utilisant la configuration de recherche par dĂ©faut de la base de donnĂ©es. Les rĂ©sultats sont obtenus en faisant correspondre la requĂȘte et le vecteur.
Pour utiliser lâexpression de recherche search
, 'django.contrib.postgres'
doit figurer dans le réglage INSTALLED_APPS
.
SearchVector
¶
La recherche dans un champ unique fonctionne bien mais est plutÎt limitée. Les instances Entry
que nous recherchons appartiennent Ă un Blog
, qui possĂšde un champ tagline
. Pour interroger les deux champs, utilisez un vecteur 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>]
Les paramĂštres Ă SearchVector
peuvent ĂȘtre nâimporte quelle Expression
ou le nom dâun champ. Plusieurs paramĂštres seront concatĂ©nĂ©s par des espaces afin que le document de recherche les inclue tous.
Les objets SearchVector
peuvent ĂȘtre combinĂ©s ce qui permet de les rĂ©utiliser. Par exemple :
>>> Entry.objects.annotate(
... search=SearchVector('body_text') + SearchVector('blog__tagline'),
... ).filter(search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
Voir Modification de la configuration de recherche et PondĂ©ration des requĂȘtes pour une explication des paramĂštres config
et weight
.
SearchQuery
¶
SearchQuery
traduit les termes fournis par lâutilisateur en un objet requĂȘte de recherche que la base de donnĂ©es va comparer Ă un vecteur de recherche. Par dĂ©faut, tous les mots fournis par lâutilisateur sont passĂ©s par un algorithme de segmentation, puis la recherche de correspondance sâeffectue pour tous les termes rĂ©sultants.
Si search_type
vaut 'plain'
, la valeur par défaut, les termes sont traités comme des mots-clés séparés. Si search_type
vaut 'phrase'
, les termes sont traités comme une phrase unique. Si search_type
vaut 'raw'
, vous pouvez alors fournir un requĂȘte de recherche formatĂ©e avec des termes et des opĂ©rateurs. Lisez la _`documentation de recherche plein texte`_ de PostgreSQL pour explorer les diffĂ©rences et la syntaxe. Exemples :
>>> 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
Les termes SearchQuery
peuvent ĂȘtre combinĂ©s logiquement pour fournir plus de souplesse :
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery('meat') & SearchQuery('cheese') # AND
>>> SearchQuery('meat') | SearchQuery('cheese') # OR
>>> ~SearchQuery('meat') # NOT
Voir Modification de la configuration de recherche pour une explication du paramĂštre config
.
Le paramĂštre search_type
a été ajouté.
SearchRank
¶
Nous nâavons jusquâici que renvoyĂ©s les rĂ©sultats pour lesquels au moins une correspondance entre le vecteur et la requĂȘte est possible. Il est probable que vous souhaitiez trier les rĂ©sultats par une certaine notion de pertinence. PostgreSQL fournit une fonction de classement qui prend en compte la frĂ©quence dâapparition des termes recherchĂ©s dans le document, la proximitĂ© de ces termes dans le document et lâimportance de lâendroit oĂč les termes se trouvent dans le document. Plus la correspondance est bonne, plus le classement sera Ă©levĂ©. Pour trier par pertinence :
>>> 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>]
Voir PondĂ©ration des requĂȘtes pour une explication du paramĂštre weights
.
Modification de la configuration de recherche¶
Il est possible de prĂ©ciser lâattribut config
de SearchVector
et de SearchQuery
afin dâutiliser une configuration de recherche diffĂ©rente. Cela permet dâutiliser des analyseurs et des dictionnaires de langue diffĂ©rents tels que dĂ©finis par la base de donnĂ©es :
>>> 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>]
La valeur de config
peut aussi ĂȘtre stockĂ©e dans une autre colonne :
>>> 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>]
PondĂ©ration des requĂȘtes¶
Les diffĂ©rents champs dâune requĂȘte nâont pas toujours la mĂȘme pertinence, il est donc possible de pondĂ©rer les diffĂ©rents vecteurs avant de les combiner :
>>> 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')
Le poids devrait correspondre Ă lâune des lettres suivantes : D, C, B, A. Par dĂ©faut, ces poids font rĂ©fĂ©rence respectivement aux nombres 0,1
, 0,2
, 0,4
et 1,0
. Si vous souhaitez pondérer de maniÚre différente, passez une liste de quatre nombres flottants à SearchRank
pour le paramĂštre weights
dans le mĂȘme ordre que ci-dessus :
>>> 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')
Performance¶
Il nâest pas nĂ©cessaire de disposer dâune configuration de base de donnĂ©es spĂ©ciale pour utiliser ces fonctions. Cependant, si vous recherchez dans plus de quelques centaines dâenregistrements, vous risquez de rencontrer des problĂšmes de performance. La recherche plein texte est un processus plus intensif que la comparaison de la taille dâun nombre entier, par exemple.
Dans le cas oĂč tous les champs dans lesquels vous recherchez sont contenus dans un modĂšle particulier, vous pouvez crĂ©er un index fonctionnel qui correspond au vecteur de recherche que vous souhaitez utiliser. La documentation de PostgreSQL contient des dĂ©tails sur la crĂ©ation dâindex pour la recherche plein texte.
SearchVectorField
¶
Si cette approche devient trop lente, vous pouvez ajouter un champ SearchVectorField
Ă votre modĂšle. Il faudra assurer son remplissage par des dĂ©clencheurs, par exemple, comme expliquĂ© dans la documentation PostgreSQL. Il est alors possible dâinterroger le champ comme sâil sâagissait dâun vecteur annotĂ© SearchVector
:
>>> Entry.objects.update(search_vector=SearchVector('body_text'))
>>> Entry.objects.filter(search_vector='cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
Similarité par trigramme¶
Une autre approche de la recherche est la similitude par trigramme. Un trigramme est un groupe de trois caractĂšres consĂ©cutifs. En plus de lâexpression de recherche trigram_similar
, il est possible dâutiliser une certain nombre dâautres expressions.
Pour les utiliser, vous devez activer lâextension pg_trgm dans PostgreSQL. Vous pouvez installer lâextension par une opĂ©ration de migration TrigramExtension
.
TrigramSimilarity
¶
Accepte un nom de champ ou une expression, ainsi quâune chaĂźne ou une expression. Renvoie la similitude par trigramme entre les deux paramĂštres.
Exemple dâutilisation :
>>> 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>]
TrigramDistance
¶
Accepte un nom de champ ou une expression, ainsi quâune chaĂźne ou une expression. Renvoie la distance par trigramme entre les deux paramĂštres.
Exemple dâutilisation :
>>> 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>]