Vous avez un flux de commandes avec un code client, et il vous faut le nom du client, son segment et son pays. L’information existe, mais ailleurs : dans une table de référence, dans un fichier exporté par un autre service, ou dans un second flux de votre pipeline. C’est le besoin le plus fréquent en intégration de données, et Apache Hop propose non pas un mais plusieurs transforms pour y répondre. Le choix n’est pas cosmétique : selon le volume et la source, un même rapprochement s’exécute en quelques secondes ou sature la mémoire de la JVM.
Prérequis
- Apache Hop 2.19.0 (dernière version publiée, 17 août 2026), qui requiert Java 21.
- Un projet Hop configuré, avec une connexion relationnelle pour tester les transforms de base de données.
- Les notions de pipeline et de flux de lignes, abordées dans les articles précédents de ce blog.
1. Cinq transforms, cinq terrains de jeu
Apache Hop regroupe une quinzaine de transforms dans les familles « lookup » et « join ». Cinq couvrent la grande majorité des cas réels.
| Transform | Source de référence | Comparaison | Cas d’usage typique |
| Stream Lookup | Un autre transform du pipeline | Égalité stricte | Référentiel petit ou moyen, déjà chargé dans le pipeline |
| Database Lookup | Une table de la base | Égalité et autres conditions | Référentiel volumineux que l’on ne veut pas charger entièrement |
| Database Join | Une requête SQL paramétrée | Libre (SQL) | Rapprochement nécessitant une jointure ou un agrégat |
| Merge Join | Deux flux du pipeline | Égalité, flux triés | Deux gros volumes à joindre, y compris en jointure externe |
| Fuzzy match | Un autre transform du pipeline | Similarité approximative | Libellés saisis à la main, référentiels hétérogènes |
2. Stream Lookup : la jointure en mémoire
Le transform Stream Lookup recherche des valeurs à partir des données fournies par un autre transform du pipeline. Son fonctionnement est explicite dans la documentation : il lit d’abord l’intégralité des données de référence en mémoire, puis s’en sert pour alimenter le flux principal. C’est très rapide, à une condition qui est aussi sa limite : le jeu de référence doit tenir dans la mémoire disponible.
La configuration tient en quatre éléments. Lookup Transform désigne le transform qui fournit les données de référence. The keys to lookup… définit les champs de rapprochement, la comparaison se faisant toujours par égalité. Fields to retrieve liste les champs à ramener, avec une valeur par défaut optionnelle et un renommage possible en sortie. Enfin, trois options encadrent la mémoire : Preserve memory encode les lignes en octets bruts, au prix de davantage de CPU pour le calcul des empreintes, tandis que Key and value are exactly one integer field et Use sorted list réduisent encore l’empreinte. Ces deux dernières exigent Preserve memory et sont mutuellement exclusives.
3. Database Lookup : le référentiel reste en base
Quand le référentiel est trop gros pour être chargé, Database Lookup interroge directement une table et ajoute les valeurs trouvées en nouveaux champs du flux. Les options qui font la différence en production :
- Enable cache? conserve les valeurs déjà recherchées : une clé répétée ne redescend pas en base. La contrepartie est documentée : ne l’activez pas si un autre processus modifie la table pendant l’exécution. Cache size in rows borne le cache,
0signifiant « tout mettre en cache ». - Load all data from table précharge la table entière. Outre la mémoire, la documentation signale un effet de bord négligé : la comparaison en mémoire peut différer de celle de la base, notamment sur la casse ou les espaces en fin de chaîne.
- Do not pass the row if the lookup fails transforme le comportement en jointure interne ; décoché, les lignes sans correspondance passent quand même, comme en jointure externe.
- Fail on multiple results? fait échouer le transform si la recherche ramène plus d’une ligne : un référentiel censé être unique qui en renvoie deux est un incident de données, pas un cas à absorber silencieusement. Order by permet, à l’inverse, de choisir l’enregistrement à retenir.
Un piège mérite d’être connu : dans la table des clés de recherche, la condition LIKE se comporte par défaut comme un =. Les jokers ne sont pas ajoutés automatiquement, vous devez les intégrer à la valeur en amont, par exemple avec un transform Replace in String.
4. Database Join : quand il faut du SQL
Dès que le rapprochement demande une jointure entre plusieurs tables, un agrégat ou une condition non triviale, Database Join est le bon outil : il exécute une requête contre la base en utilisant les données des transforms précédents. Les paramètres sont passés par des points d’interrogation, dans l’ordre déclaré dans la table des paramètres.
SELECT c.nom_client,
c.segment,
COUNT(f.id_facture) AS nb_factures
FROM clients c
LEFT JOIN factures f ON f.id_client = c.id_client
WHERE c.code_client = ?
AND c.date_debut <= ?
GROUP BY c.nom_client, c.segment
Trois options structurent le comportement : Number of rows to return (0 renvoie toutes les lignes), Outer join? qui renvoie un résultat même sans correspondance, et le couple Enable cache? / Cache size in rows, avec la même réserve sur les tables modifiées en cours d’exécution.
5. Merge Join : joindre deux flux volumineux
Merge Join réalise une jointure par fusion classique entre deux jeux de données issus de deux transforms différents. Il accepte les quatre types de jointure, INNER, LEFT OUTER, RIGHT OUTER et FULL OUTER, mais ne gère que les jointures d’égalité.
La contrainte est écrite noir sur blanc dans la documentation : le transform suppose que vos données sont triées sur les clés de jointure. Sinon, insérez un transform Sort Rows sur chaque flux entrant. Un tri oublié ne produit pas une erreur franche mais un résultat partiellement faux, bien plus coûteux à diagnostiquer ; si les deux flux viennent de la même base, un ORDER BY reste la solution la plus économique.
6. Fuzzy match : rapprocher ce qui ne correspond pas exactement
Les données saisies à la main ne se rapprochent pas par égalité. Le transform Fuzzy match recherche les chaînes susceptibles de correspondre à l’aide d’algorithmes de détection de doublons, et renvoie les correspondances sous forme de liste séparée, en fonction d’une valeur minimale ou maximale définie par l’utilisateur. Le champ Algorithm propose Levenshtein, Damerau-Levenshtein, Needleman Wunsch, Jaro, Jaro Winkler, Pair letters similarity, Metaphone, Double Metaphone, SoundEx et Refined SoundEx : les premiers mesurent une distance d’édition, les derniers reposent sur une empreinte phonétique.
Vous désignez le Lookup transform, le Lookup field et le Main stream field, puis encadrez le résultat avec Minimum value et Maximal value. Get closer value ne retourne que le meilleur score ; décochée, les correspondances sont concaténées avec le Values separator. Case sensitive n’existe que pour les algorithmes de type Levenshtein. En pratique, Fuzzy match alimente rarement une table de production : il produit une liste de candidats à valider.
7. Le piège à connaître : les blocages de pipeline
Si vous dérivez un flux en deux branches pour les rejoindre avec un Stream Lookup ou un Merge Join, votre pipeline peut se figer sans message d’erreur. La documentation Hop consacre un guide entier à ce phénomène.
Le mécanisme est simple : les transforms communiquent par des tampons de lignes de taille bornée, et un transform de rapprochement attend des données sur ses deux entrées. Si une branche remplit son tampon pendant que l’autre reste bloquée en amont, par exemple derrière un Group By qui doit tout consommer avant de produire, les deux s’immobilisent mutuellement. L’exemple de la documentation est éloquent : avec un tampon de 10 000 lignes, une exécution sur 10 001 lignes suffit à provoquer l’arrêt.
Les solutions recommandées, par ordre de préférence :
- Séparer les flux d’entrée en deux copies indépendantes, afin que chaque branche s’exécute pour son propre compte.
- Découper le pipeline en pipelines plus petits qui se passent les données par une table ou un fichier intermédiaire.
- Utiliser le transform Blocking avec l’option Pass all rows, pour traiter les flux séquentiellement, en ajustant sa taille de cache.
- Augmenter la taille des tampons via l’option Rowset size du moteur
Local, dans la Pipeline Run Configuration. La documentation l’assortit d’une mise en garde : des tampons plus grands consomment plus de mémoire et peuvent ralentir les gros traitements. C’est un correctif de confort, pas de conception.
Pour Merge Join s’ajoute la recommandation de trier les deux flux avant la fusion. Ces blocages se manifestant surtout sur des volumes supérieurs à ceux des tests, relancez le pipeline en ligne de commande avec un jeu de données réaliste avant de le planifier :
./hop-run.sh -j mon_projet -e prod -r local \
-f ${PROJECT_HOME}/pipelines/enrichir-commandes.hpl \
-p DATE_DEBUT=2026-10-01
Ce que vous avez gagné
Vous disposez d’un critère de choix explicite : la source de référence et son volume déterminent le transform, pas l’habitude. Stream Lookup pour un référentiel qui tient en mémoire, Database Lookup pour un référentiel qui reste en base, Database Join quand le SQL est inévitable, Merge Join pour deux gros volumes triés, Fuzzy match pour produire des candidats à valider. Vous savez aussi reconnaître un pipeline qui se fige et traiter la cause plutôt que le symptôme, en revoyant la topologie des flux avant de toucher aux tampons.
Pour aller plus loin
- Stream Lookup
- Database Lookup
- Database Join
- Merge Join
- Fuzzy match
- Avoiding Deadlocks — le guide de référence sur les blocages de pipeline
- Dimension lookup/update — la suite naturelle, vers les dimensions à évolution lente