Ludovic Frank - Développeur indépendant

Typesense, un moteur de recherche léger et rapide, une bonne alternative a Elasticsearch ?

ionicons-v5-k Ludovic Frank 2 mai 2023
2369 lectures Niveau : Débutant confirmé

Bonjour bonjour 😁,
Cette semaine, on attaque un gros sujet, cet article fait suite à « Pourquoi MYSQL n'est-il pas un moteur de recherche ? ».
Dans ce dernier, je vous parlais d'Elasticsearch, un moteur de base de données indexée populaire et qui fait bien le travail.

Cette semaine, on va parler de Typesense, si vous passez de temps en temps sur ce blog, vous savez que j'aime bien vous parler de technologie sur lesquels j'ai passé du temps, c'est le cas de Typesense, puisqu'il a été une grosse partie du projet sur lequel je travaille en ce moment.

Vous êtes prêt ? Alors c'est parti !

Qu'est-ce que Typesense ?

Typesense est un moteur de base de données indexée (et donc un moteur de recherche), Open Source, écrit en C++.
Sur son Github, il se définit comme étant une alternative Open Source à Algolia (Un "SaaS" propriétaire, offrant une API de recherche).

Pour interagir avec typesense, celui-ci expose une API REST, assez bien documentée. l'API permet de tout faire, par exemple créer des clefs d'accès avec des droits spécifiques, créer des collections, indexer des données ... etc.

Qu'est-ce qu'une collection ?

Contrairement à un moteur de base de données relationnel, il n'y a pas de relation dans un moteur de base de données indexée, les données sont enregistrées "en brut", si par exemple vous avez une collection "Circuit" et que dedans vous avez un champ "type vélo" alors il y aura écrit autant de fois que nécessaire "Route" ou encore "VTT".
Voyez un peu ça comme un très grand tableau Excel.
Chaque ligne du tableau Excel est appelée un "document", et chaque colonne est appelée un "champs".
C'est vraiment basé sur le texte, il n'y a pas de logique autre derrière, c'est pour cela que c'est rapide.

Quelques fonctionnalités de Typesense

Bien qu'elle ne soit pas spécifique à Typesense, voici quelques fonctionnalités que vous retrouverez dans la plupart des moteurs de recherche :

Les facettes
Vous connaissez forcément cela, vous l'avez déjà vu sur des sites e-commerces, c'est la possibilité de filtrer les résultats en fonction de critères, par exemple sur un site de vente de vélo, vous pouvez filtrer par "type de vélo" ou encore "taille de roue". Vous pouvez regarder sur la droite de cette page (en dessous sur mobile) pour voir de quoi l'on parle.

Résultat de recherche en fonction du "poids"
C'est très simple, cela veut dire que : certains champs sont plus importants que d'autres, par exemple si vous cherchez un nom de Ville comme Paris et que dans votre collection il y a un document avec comme champs « titre » : « Une balade en Île-de-France » et dans le champ « ville » : « Paris », alors si votre champ "titre" a un plus gros poids que le champ "ville", le moteur de recherche regardera en premier dans « titre » puis dans « ville », et donc triera les résultats de recherche en fonction de ça. Dans ce cas-là les documents qui ont « Paris » dans le champ « titre » sortiront avant ceux qui ont « Paris » dans le champ « ville ».

Qu'apporte Typesense par rapport à Elasticsearch ?

Déjà, ils ont des choses en commun, par exemple ils peuvent tous les deux fonctionner en cluster (sur plusieurs serveurs, cela permet d'avoir une haute disponibilité et de meilleures performances quand il y a du trafic).

Pour le reste :

  • Typesense est beaucoup plus léger qu'Elasticsearch et cela peut s'expliquer par le fait qu'il est écrit en C++ et qu'Elasticsearch, lui, c'est java.
  • Il est tolérant aux fautes de frappe, vous pouvez par exemple taper « turtue nonja » pour trouver « Tortue Ninja » (ne me demandez pas pourquoi Tortue Ninja, j'avais envie 😁).
  • Le réglage du "poids" des champs de recherche est très simple, c'était un des gros points forts d'Algolia avant l'arrivée de Typesense. En fait, il suffit juste de définir l'ordre des champs dans lequel Typesense va chercher lors de l'envoi de la requête à l'API, le premier champ dans la liste aura le plus de poids, et bien sûr le dernier en aura moins, c'est parfait quand on veut que des résultats sortent si on ne trouve "rien d'autre".
  • Il semble plus facile à prendre en main qu'Elasticsearch, son API est simple et logique à prendre en main, vous n'aurez pas besoin d'y passer des années pour avoir quelque chose de fonctionnel et surtout qui renvoie des réponses pertinentes. Il y a également une image « Docker » officielle disponible.

Un projet encore jeune

À l'heure de l'écriture de ces lignes, la version "stable" est la version 0.24.1.
Cela peut se ressentir lorsque vous travaillez avec...

Attention aux mises à jour de donnée fréquentes

Voilà donc un « comportement » bizarre qui m'est tombé dessus lorsque je travaillais avec Typesense, il m'arrivait de faire beaucoup de modification de donnée de l'index et de laisser tourner Typesense sur ma machine de dev, à partir d’un certain temps, l'API était beaucoup plus lente, et parfois même, des données disparaissait carrément, par exemple quand je souhaitais faire un « update » sur un document, l'API me renvoyait que ce document n'existait pas... 😮
Le problème vient simplement du fait que lorsqu'on met à jour souvent les documents, Typesense a tendance à « bugger ».
J'imagine, et ce n'est que mon interprétation, que l'équipe travail plus sur la pertinence des reulstats de recherche ainsi que sur la rapidité que sur cette partie-là pour le moment.

Integrer Typesense dans un projet Symfony

Tout comme pour ElasticSearch avec ce bundle, un bundle est en cours de développement pour Typesense.

Tout comme Typesense lui-même, le Bundle est assez jeune, lors de mon travail je suis tombé sur un OS, dans mon cas à moi j'avais énormément d’entrées à indexer, et, à cette époque, le bundle envoyait tout d'un coup à Typesense sans pagination, plutôt que de l'envoyer en plusieurs requêtes...

Du coup j'étais prêt à coder l'import avec une pagination et faire une « pull request » sur le projet, mais on m'a devancé .

Comment fonctionne le Bundle ?

Si vous avez déjà utilisé celui pour ElasticSearch, vous ne serez pas dépaysé...

  • Passez en variable d'environnement, l'URL, le port, ainsi que la clef de votre serveur Typesense.
  • Configurez votre fichier "acseo_typesense", pour comprendre comment faire, jetez un œil à la page Github
  • Utilisez les commandes « typesense:create » et « typesense:import » pour peupler votre base de données indexée (en fonction de ce que vous avez configuré dans votre fichier de configuration)
  • Utilisez les services générés dynamiquement par le bundle pour accéder à vos collections et faire des requêtes dessus.

La synchronisation avec Doctrine

Pour fonctionner, le bundle écoute les événements "doctrine", pour détecter quand votre base de données est à mise à jour, et donc, mettre à jour la base de données indexée.
Comme je vous l'ai dit plus haut, il faut faire attention avec ce comportement, car Typesense n'aime pas trop quand il y a beaucoup de modifications sur les documents d'une collection.

Attention à l'hydratation

Il peut être intéressant de noter également, que par défaut, tout comme le bundle pour ElasticSearch, quand on fait une requête, le bundle "Hydrate" les résultats avec les objets « Doctrin », et pour cela, il fait beaucoup de requête à votre base de données (MYSQL, Postgres ... etc), pensez à regarder si dans votre cas d'usage vous avez vraiment besoin des objets complets.

Le front-end

Tout comme Algolia, Typesense permet au front-end (le JavaScript) de s'y connecter directement, cela évite de passer par le back-end de votre application à chaque fois, c'est plus léger et plus réactif.

Typesense-js

En plus de fournir le moteur de recherche, l'équipe de Typesense fournit également un client JavaScript, qui permet de faire des requêtes facilement vers les serveurs Typesense (car oui, le client prend en charge le fait qu'il y ai plusieurs « nodes ».

C'est très simple à utiliser, pour instancier votre client, regardez ce fichier de test qui montre comment faire.
Une fois que vous avez votre "client".

Une fois que vous avez instancié votre client, vous pouvez simplement faire une recherche de la manière suivante :

Le résultat sera sous forme de JSON, et si vous utilisez Symfony, pensez bien que là vous vous connectez directement à Typesense, donc il n'y a aucune hydratation.

InstantSearch

InstantSearch est une librairie JavaScript proposée par Algolia pour créer des interfaces de recherche avancée, elle est intéressante, car de base elle est pas mal conçue et surtout, elle est extensible pour la faire se comporter comme vous vous le voulez.

Plus haut, je vous ai dit que Typesense se place comme alternative à Algolia, du coup il existe un adaptateur pour InstantSearch.

Son utilisation est très simple également, il suffit de l'instancier un peu à la manière de typesense-js et le passer à InstantSearch. Jetez un œil à la page Github du projet pour voir comment faire.
Une fois que vous avez instancié votre adaptateur, consultez la documentation d’InstantSearch

Concrètement, en production, qu'est-ce que ça donne ?

Une image vaut mieux que 1000 mots, donc je vous propose de tester directement :

Ici, vous avez une page de recherche avancée, avec les facettes sur la droite et des requêtes complexes.

Là, juste un « input », qui fait des requêtes simples

Les serveurs qui traitent vos requêtes se trouvent en France, à Paris, cela vous donne un bon exemple de ce qu'il est possible de faire avec Typesense.

Conclusion

Bien que ce soit un projet jeune, Typesense a, je pense, de l'avenir sur le marché des moteurs de recherche.
J'ai beaucoup aimé travailler avec et le réutiliserais quand j'en aurais l'occasion. Il me permet de créer l'expérience utilisateur que je veux créer.
À la prochaine 😁.