Réponse rapide
FTS5 est l’extension de recherche full-text de SQLite. Il s’agit d’un module de table virtuelle : on crée une table avec CREATE VIRTUAL TABLE ... USING fts5(...), on y insère du texte comme dans une table classique, puis on l’interroge avec l’opérateur MATCH plutôt qu’avec LIKE. Sous le capot, FTS5 découpe le texte en tokens et construit un index inversé à partir d’un ensemble de B-trees. Il peut classer les résultats par pertinence avec sa fonction BM25 intégrée, extraire des snippets et mettre en évidence les termes trouvés. FTS5 succède à FTS3 et FTS4, mais la migration d’une application existante nécessite quelques adaptations du schéma et des requêtes.
Comment ça marche
Les bases de données relationnelles sont efficaces pour les correspondances exactes et les recherches par plage de valeurs, mais un WHERE body LIKE '%motcle%' sur une colonne de texte volumineuse impose généralement de parcourir chaque ligne. Un index classique ne peut pas accélérer un motif qui commence par un joker. La recherche full-text reprend le principe de l’index d’un livre : plutôt que de parcourir chaque page, elle cherche le mot dans l’index et accède directement à ses occurrences.
FTS5 construit automatiquement cet index. À chaque insertion, le texte passe par un tokenizer. Le tokenizer unicode61, utilisé par défaut, considère les lettres et les chiffres Unicode comme des caractères de token ; par défaut, les recherches ne sont pas sensibles à la casse et la plupart des signes diacritiques des alphabets latins sont supprimés. Le tokenizer ascii applique des règles ASCII plus simples, porter ajoute du stemming, ou racinisation (« running » et « run » peuvent ainsi être ramenés à la même racine), et trigram indexe des séquences de trois caractères qui se chevauchent afin de permettre la recherche par sous-chaîne, et pas seulement par token. Chaque token obtenu est associé dans l’index à sa ligne et à sa colonne d’origine. FTS5 stocke ces entrées dans des segments organisés en B-trees, qui sont fusionnés périodiquement selon une logique proche du chemin d’écriture d’un LSM-tree.
Une fois le contenu indexé, SELECT * FROM docs WHERE docs MATCH 'sqlite AND search' trouve les lignes correspondantes dans l’index, sans parcourir l’ensemble du texte source. FTS5 fournit également des fonctions auxiliaires utilisables dans les requêtes : bm25() attribue à chaque ligne un score calculé selon l’algorithme de pertinence BM25 (plus le score est bas, meilleure est la correspondance ; on utilise donc généralement ORDER BY bm25(docs)), snippet() renvoie un court extrait autour d’une occurrence et highlight() entoure les termes trouvés avec le balisage choisi, par exemple <b>...</b>.
Par défaut, une table FTS5 conserve une copie du texte indexé en plus de l’index. Si le texte source se trouve déjà dans une autre table, on peut configurer une table à contenu externe avec content='matable'. Cela évite de stocker une seconde copie des colonnes source, mais l’application doit maintenir l’index FTS synchronisé avec les insertions, les mises à jour et les suppressions, généralement à l’aide de triggers. FTS5 propose aussi un mode sans contenu (content='') pour les cas où seul l’index est nécessaire et où les valeurs des colonnes d’origine n’ont pas à être relues. Ces tables imposent des contraintes supplémentaires pour les mises à jour et les suppressions.
FTS5 ne remplace pas FTS3 ou FTS4 à l’identique : la déclaration des tables virtuelles, la syntaxe des requêtes et les fonctions auxiliaires présentent certaines différences. SQLite décrit FTS5 comme une nouvelle version de FTS4 qui corrige des problèmes impossibles à résoudre sans rompre la compatibilité. FTS5 propose également un scoring BM25 intégré et de nombreuses options de configuration : tokenizers personnalisés, index prefix= pour accélérer les requêtes par préfixe, ou modes detail= pour réduire la taille de l’index lorsque les requêtes de phrase ou NEAR ne sont pas nécessaires.
Exemples
Une configuration minimale :
CREATE VIRTUAL TABLE docs USING fts5(title, body);
INSERT INTO docs(title, body) VALUES
('Bases de SQLite', 'SQLite est un moteur de base de données SQL embarqué, petit et rapide.'),
('Recherche full-text', 'FTS5 permet de chercher du texte efficacement grâce à un index.');
SELECT title, bm25(docs) AS rank, snippet(docs, 1, '[', ']', '...', 8)
FROM docs
WHERE docs MATCH 'recherche'
ORDER BY rank;
FTS5 convient aux applications qui doivent chercher dans leurs propres données sans exploiter un serveur de recherche distinct :
- Les applications desktop et mobiles fondées sur SQLite—applications de prise de notes, clients de messagerie ou gestionnaires de mots de passe, par exemple—peuvent l’utiliser pour rechercher des notes, des messages ou des entrées.
- Datasette et d’autres outils adossés à SQLite peuvent utiliser FTS5 pour effectuer des recherches dans les jeux de données publiés.
- Certains outils de recherche pour sites statiques et documentations construisent un index FTS5 lors du build et le distribuent avec le site pour effectuer les recherches côté client avec SQLite compilé en WebAssembly.
- Une application qui utilise déjà SQLite comme datastore embarqué peut ainsi ajouter une recherche full-text sans déployer Elasticsearch ou un autre serveur lorsque cette infrastructure serait disproportionnée par rapport au volume de données.
FAQ
FTS5 est-il plus rapide que LIKE ‘%terme%’ ?
Pour les grandes collections et les recherches par token, l’écart est généralement important. Un motif LIKE commençant par un joker ne peut pas utiliser un index classique et impose le plus souvent un full scan, tandis que FTS5 recherche les tokens dans son index inversé. Le gain exact dépend toutefois des données et de la requête.
Faut-il activer FTS5 séparément ?
Cela dépend de la compilation de SQLite. FTS5 est inclus dans l’amalgamation SQLite depuis la version 3.9.0, publiée le 14 octobre 2015, mais doit toujours être activé lors de la compilation. Le script de configuration de l’amalgamation l’active actuellement par défaut, contrairement à celui de l’arborescence source canonique. La requête SELECT sqlite_compileoption_used('ENABLE_FTS5'); permet de vérifier l’environnement d’exécution ; un résultat égal à 0 indique que FTS5 n’est pas inclus dans le build.
FTS5 peut-il classer les résultats par pertinence ?
Oui. Sa fonction auxiliaire intégrée bm25() implémente le scoring BM25. Dans l’implémentation de SQLite, plus le score est bas, meilleure est la correspondance.
FTS5 gère-t-il la recherche floue ou partielle ?
Par défaut, FTS5 effectue des correspondances sur des tokens complets. Les requêtes par préfixe (terme*) sont intégrées et le tokenizer trigram permet la recherche par sous-chaîne. En revanche, FTS5 n’intègre pas de recherche floue tolérante aux fautes de frappe ; celle-ci nécessite une logique supplémentaire ou une extension telle que spellfix1.
Faut-il synchroniser l’index FTS5 manuellement ?
Uniquement lorsque le contenu indexé se trouve en dehors de la table FTS5, par exemple avec content='autretable'. L’application doit alors maintenir l’index synchronisé, souvent à l’aide de triggers. Une table FTS5 autonome—le mode par défaut, sans option content=—met elle-même son index à jour lors des insertions, modifications et suppressions de lignes.
Sources
- SQLite FTS5 Extension — La documentation officielle de SQLite sur l’architecture de FTS5, la syntaxe des requêtes, les tokenizers, le scoring, les modes de stockage, la compilation et la migration depuis FTS3/FTS4.
- Full-text search — Datasette documentation — La documentation de Datasette sur l’utilisation des tables FTS de SQLite pour rechercher des données publiées.
- The spellfix1 virtual table — La documentation officielle de SQLite sur l’extension optionnelle de correction orthographique mentionnée dans la FAQ.