NXL Forge.Pendant que le marché rédige — nous livrons
Technique Odoo··5 min de lecture

Odoo 19 : trois types d'index sur vos champs, lequel choisir

Beaucoup de bases Odoo ralentissent sur des recherches qu'un bon index réglerait en une ligne. L'attribut index de vos champs propose trois modes, dont un pour la recherche textuelle et un pour les colonnes souvent vides. Voici comment les choisir sans indexer à l'aveugle.

Odoo 19PostgreSQLPerformanceORMIndex

Une base Odoo qui a quelques années finit toujours par avoir une poignée de recherches lentes. Un champ de référence client, un numéro de lot, un libellé libre que les utilisateurs tapent dans la barre de recherche. Le réflexe le plus courant consiste à ajouter index=True sur le champ, puis à passer à autre chose. Ce réflexe fonctionne, mais il ignore que l'ORM d'Odoo 19 sait créer trois familles d'index différentes, et que le bon choix change souvent tout.

L'attribut index d'un champ n'est pas un simple booléen. Dans le code d'Odoo 19, il accepte quatre valeurs utiles : True (ou "btree"), "btree_not_null", "trigram", et False par défaut 1. Chacune produit un objet PostgreSQL distinct, avec un profil de performance différent. Ces options ne sont pas nouvelles, elles existent depuis plusieurs versions 2, mais elles restent largement sous exploitées dans les modules métier que nous reprenons.

btree : le choix par défaut, et ses limites

index=True crée un index B-tree classique. Il est adapté aux clés étrangères (many2one), aux dates et aux comparaisons d'égalité ou d'intervalle. C'est le bon outil pour accélérer un filtre du type partner_id = 42 ou date_order >= '2026-01-01'.

Là où il déçoit, c'est sur la recherche textuelle partielle. Une requête ILIKE '%dupont%', celle que déclenche la barre de recherche quand l'utilisateur tape au milieu d'un mot, ne peut pas utiliser un index B-tree. PostgreSQL retombe alors sur un parcours séquentiel de la table. Sur quelques milliers de lignes cela ne se voit pas. Sur une table de plusieurs millions d'écritures comptables ou de lignes de mouvement de stock, la latence devient perceptible.

btree_not_null : pour les colonnes souvent vides

Beaucoup de champs many2one ne sont renseignés que sur une fraction des enregistrements. Un lien vers une campagne marketing, un contact de livraison alternatif, un projet analytique optionnel. Indexer ce type de colonne avec un B-tree standard revient à stocker une entrée d'index pour chaque ligne, y compris les millions de valeurs nulles qui ne seront jamais recherchées.

La valeur "btree_not_null" crée un index partiel qui exclut les valeurs nulles. L'index est plus petit, donc plus rapide à parcourir et moins coûteux à maintenir en écriture. La règle empirique retenue à l'origine par Odoo était de basculer sur ce mode quand plus de 90 pour cent des valeurs sont nulles 2. En pratique, c'est un bon candidat dès qu'une colonne facultative dépasse la moitié de valeurs vides.

trigram : la vraie réponse à la recherche libre

Pour les champs texte interrogés en ILIKE avec un joker en tête, Odoo 19 propose index="trigram". Il s'appuie sur un index GIN et l'extension trigramme de PostgreSQL, conçue pour la recherche approximative sur les chaînes 3. Concrètement, PostgreSQL découpe la valeur en séquences de trois caractères et indexe ces fragments, ce qui lui permet de retrouver une sous chaîne sans parcourir toute la table.

C'est le mode à réserver aux champs réellement cherchés en plein texte : une référence interne, un nom de produit, un libellé de note. L'index trigramme a un coût d'écriture supérieur au B-tree et pèse davantage sur le disque, il ne se justifie donc pas sur un champ que personne ne cherche par fragment.

Comment l'appliquer proprement

Le changement se fait dans la définition du champ, pas dans une migration SQL manuelle :

reference = fields.Char(string="Référence", index="trigram")
campaign_id = fields.Many2one("marketing.campaign", index="btree_not_null")

L'index est créé ou remplacé au chargement du module. Une simple mise à jour du module concerné (-u mon_module) suffit pour qu'Odoo aligne l'objet PostgreSQL sur la déclaration. Avant de généraliser, mesurez : un EXPLAIN ANALYZE sur la requête réelle vous dira si PostgreSQL utilise vraiment l'index, ou s'il l'ignore parce que la table est trop petite pour que cela vaille la peine.

Les pièges à connaître

Un index trigramme n'accélère pas les comparaisons d'égalité stricte. Un champ à la fois filtré sur = et cherché en ILIKE peut donc justifier deux index, ce point avait d'ailleurs été soulevé dans la discussion d'origine chez Odoo 2. Par ailleurs, chaque index ajouté ralentit les écritures et occupe de l'espace. Sur une table à fort volume d'insertion, un index de trop se paie à chaque création d'enregistrement.

Enfin, ces réglages agissent au niveau du champ ORM, ce qui les rend cohérents avec les autres travaux de performance de la couche d'accès aux données dans cette version. Si vos lenteurs viennent d'agrégations plutôt que de recherches, le sujet se déplace vers la façon dont l'ORM construit ses requêtes de regroupement, que nous avons traitée ailleurs dans notre article sur les pivots en Odoo 19.

Notre lecture

Sur le terrain, la lenteur d'une base Odoo vient rarement d'un manque de puissance serveur. Elle vient d'index absents ou mal choisis sur trois ou quatre champs très sollicités. L'intérêt de l'attribut index d'Odoo 19, c'est qu'il rend ce choix déclaratif et versionné dans le module, au lieu d'un index posé à la main que la prochaine migration oubliera. Notre recommandation tient en une phrase de méthode : ne jamais indexer à l'aveugle, identifier d'abord les deux ou trois requêtes qui font mal avec un EXPLAIN ANALYZE, puis poser le type d'index qui correspond au motif de recherche réel. Un trigram sur un champ jamais cherché en plein texte est du poids mort, un btree sur une recherche ILIKE ne sert à rien. Le bon index est celui qui colle à l'usage, pas celui qu'on ajoute par prudence.

Sources

Footnotes

  1. Le docstring de l'attribut index dans odoo/orm/fields.py (branche 19.0) documente les valeurs True/"btree" (index standard, adapté aux many2one), "btree_not_null" (B-tree excluant les valeurs nulles), "trigram" (index GIN par trigrammes pour la recherche plein texte) et False (aucun index, valeur par défaut). L'attribut n'a aucun effet sur les champs non stockés.

  2. La gestion des trois types d'index a été introduite par la Pull Request #83015 « Better handling of indexes », fusionnée début 2022. Sa description mentionne le basculement vers un index « not null » au delà de 90 pour cent de valeurs nulles, et la discussion associée note qu'un index trigramme ne couvre pas les comparaisons d'égalité. 2 3

  3. Le module pg_trgm de PostgreSQL indexe les chaînes par groupes de trois caractères et permet, via un index GIN, d'accélérer les recherches LIKE et ILIKE comportant un joker en tête, que les index B-tree ne peuvent pas exploiter.

Sur le même thème

Votre idée mérite un vrai logiciel

Une question, un projet Odoo ?

Modules préconfigurés, intégration, sur-mesure — parlons-en sans engagement.