Recherche¶
Un tâche courante des applications Web est de rechercher des données dans la base de données en fonction de la saisie d’utilisateurs. Dans un cas simple, cela peut se limiter à filtrer une liste d’objets par catégorie. Un cas d’utilisation plus complexe pourrait impliquer une recherche avec pondération, catégorisation, mise en évidence, multilingue, etc. Ce document présente certains cas d’utilisation possibles et les outils pouvant être utilisés.
Nous ferons référence aux mêmes modèles que dans Création de requêtes.
Cas d’utilisation¶
Requêtes textuelles standard¶
Les champs basés sur du texte offrent quelques opérations de correspondance. Par exemple, vous pourriez vouloir permettre de rechercher un auteur comme ceci :
>>> Author.objects.filter(name__contains='Terry')
[<Author: Terry Gilliam>, <Author: Terry Jones>]
Il s’agit d’une solution très fragile car elle exige que l’utilisateur connaisse une sous-chaîne exacte du nom de l’auteur. Une meilleure approche pourrait faire appel à une recherche indépendante de la casse (icontains
), mais ce n’est qu’une petite amélioration.
Des fonctions comparatives de base de données plus avancées¶
Si vous utilisez PostgreSQL, Django fournit une sélection d’outils spécifiques à cette base de données pour permettre d’exploiter des options de requête plus complexes. D’autres bases de données ont des outils différents, parfois via des greffons ou des fonctions définies par les utilisateurs. Django ne les prend pas en charge pour le moment. Nous utiliserons quelques exemples tirés de PostgreSQL pour démontrer le genre de fonctionnalités que les bases de données peuvent offrir.
Recherche avec d’autres bases de données
Tous les outils de recherche proposés par django.contrib.postgres
sont construits entièrement sur des API publiques telles que les expressions de recherche personnalisées et les fonctions de base de données. En fonction de la base de données, vous devriez pouvoir construire des requêtes en approchant des API similaires. Si des aspects spécifiques ne peuvent pas être résolus de cette façon, merci de bien vouloir ouvrir un ticket.
Dans l’exemple ci-dessus, nous avons déterminé qu’une recherche non sensible à la casse serait plus utile. Quand on est confronté à des noms sortant de l’alphabet anglais, une autre amélioration possible est d’utiliser les comparaisons sans accents
:
>>> Author.objects.filter(name__unaccent__icontains='Helen')
[<Author: Helen Mirren>, <Author: Helena Bonham Carter>, <Author: Hélène Joy>]
Cela fait apparaître un autre problème, où nous cherchons des correspondances avec une autre orthographe du nom. Dans ce cas, nous obtenons toutefois une asymétrie, une recherche pour Helen
trouvera Helena
ou Hélène
, mais pas l’inverse. Une autre option serait d’utiliser une comparaison par trigram_similar
qui compare des suites de lettres.
Par exemple :
>>> Author.objects.filter(name__unaccent__lower__trigram_similar='Hélène')
[<Author: Helen Mirren>, <Author: Hélène Joy>]
Nous rencontrons maintenant un problème différent : le nom plus long « Helena Bonham Carter » n’apparaît pas car il est bien plus long. Les recherches par trigramme envisagent toutes les combinaisons de trois lettres et comparent combien apparaissent à la fois dans les chaînes source et de recherche. En ce qui concerne le nom plus long, il y a plus de combinaisons apparaissant dans la chaîne source, ce qui explique qu’il n’est plus considéré comme une correspondance proche.
Le choix correct de fonctions de comparaison dépend ici du jeu de données particulier, par exemple en fonction des langues utilisées et du type de texte à rechercher. Tous les exemples que nous avons examinés portent sur des chaînes courtes et où l’utilisateur va probablement saisir un terme proche (selon plusieurs définitions) des données sources.
Recherche basée sur des documents¶
Les opérations de base de données standard atteignent leurs limites lorsqu’on est confronté à de gros blocs de texte. Alors que les exemples précédents peuvent être considérés comme des opérations sur une chaîne de caractères, la recherche plein texte examine les mots réels. En fonction du système utilisé, il est probable que certains des concepts suivants seront utilisés :
- Ignorance des « mots vides » tels que « a », « le », « et ».
- Racines de mots, afin que « cheval » et « chevaux » soient considérés identiques.
- Pondération des mots basée sur différents critères tels que la fréquence d’apparition dans le texte ou l’importance des champs (titre, mots-clés, etc.) dans lesquels ils apparaissent.
Il existe de nombreuses alternatives de logiciels spécialisés pour la recherche, dont Elastic et Solr sont parmi les plus célèbres. Ce sont des solutions de recherche basées sur des documents complets. Pour les utiliser avec des modèles Django, il sera nécessaire d’ajouter une couche de traduction des données en documents textuels, y compris des références inverses contenant les identifiants de base de données. Lorsqu’une recherche utilisant ces moteurs renvoie un certain document, il est alors possible de l’extraire de la base de données. Plusieurs bibliothèques tierces sont conçues pour vous assister dans cette tâche.
Prise en charge des outils PostgreSQL¶
PostgreSQL possède sa propre implémentation de recherche plein texte. Même si elle n’est pas autant performante que certains autres moteurs de recherche, elle a l’avantage de se trouver dans la base de données elle-même et peut donc être facilement combinée à d’autres requêtes relationnelles telles que la catégorisation.
Le module django.contrib.postgres
fournit certains utilitaires pour préparer ces requêtes. Par exemple, une requête pourrait sélectionner tous les articles de blog qui mentionnent « cheese »:
>>> Entry.objects.filter(body_text__search='cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
Il est aussi possible de filtrer sur une combinaison de champs et de modèles liés :
>>> Entry.objects.annotate(
... search=SearchVector('blog__tagline', 'body_text'),
... ).filter(search='cheese')
[
<Entry: Cheese on Toast recipes>,
<Entry: Pizza Recipes>,
<Entry: Dairy farming in Argentina>,
]
Voir le document Recherche plein texte de contrib.postgres
pour de plus amples détails.