Nous allons parler aujourd'hui d'une petite fonction assez récente et aussi peu connue qu'utile: then. A ma grande surprise, il n'en est fait que très peu mention aussi bien dans la documentation que dans les formations ou les forums. Evidemment, je n'ai jamais vu quelqu'un d'autre que moi l'utiliser avec des conséquences parfois sévères. Que fait elle ? Et bien rien, en fait. Mais elle le fait merveilleusement bien. Je m'explique. Cette fonction peut se coder (et s'utiliser) de la façon suivante:
%dw 2.0
output application/json
fun then(x, f) = f(x)
---
payload then(x)-> x.message
Ne la créez pas ainsi si vous utilisez une version 4.3.0 ou supérieure de Mulesoft, car elle est fournie en standard. Si vous êtes obligé d'utiliser une version plus ancienne, dépêchez vous de l'inclure dans votre bibliothèque personnalisée de fonctions utilitaires !
Mais alors à quoi peut elle servir ? Et bien, essentiellement, à deux choses:
- éviter de recalculer une valeur
- retenir une valeur pour pouvoir "sauter" une ou plusieurs expressions
Le grand intérêt de then est (uniquement) de créer ce qu'on appelle en programmation, une closure, c'est à dire de rendre visible des ressources (valeur, fonctions) définies au delà de la fonction dans lesquelles elles sont introduites. Je ne m'attarde pas: si vous ne voyez pas de quoi il s'agit, vite interrogez l'ami Google: un peu de lecture vous attend :) . Regardons plutôt en quoi la closure introduite par then peut être utile, dans les deux cas sus cités.
dans notre premier cas, nous allons exploiter un tableau de nombres. Le but est de filtrer tous les nombres de ce tableau plus grands que la moyenne. Une approche naïve (mais qui est celle de 99.99% des développeurs Mulesoft :( ) est:
payload filter $ > avg(payload)
Cette écriture est CATASTROPHIQUE. Pourquoi? Parce que pour CHAQUE nombre dans la liste, la moyenne est recalculée ! Il s'agit donc d'une expression d'ordre quadratique (en NxN). Si votre tableau est petit, vous n'y verrez pas d'inconvénient. Mais si votre tableau est très grand, la performance va s'effondrer ! Avec 10 éléments, vous effectuerez 100 opérations élémentaires, avec 1000, ce sera un million, avec un million, ce sera mille milliards ! La règle à respecter est simple: les expressions quadratiques, c'est JAMAIS. Comment l'éviter ? Très simple:
avg(payload) then (moy)-> payload filter $>moy
On calcule la moyenne une fois, on la "stocke" dans le paramètre de la fonction passée à then, et on l'utilise ensuite sans devoir la recalculer. L'expression devient linéaire (d'ordre N) et devient un million de fois plus efficace si la liste contient un million d'éléments !
"C'est très bien" diriez-vous, dans un cas aussi simple. Mais comment faire si j'ai besoin de plusieurs valeurs dans mon expression principale (ici filter). Et bien c'est très simple, il suffit d'enchaîner les then. Admettons que nous voulons maintenant récupérer tous les nombres compris entre min+20 et max-20. Nous allons écrire:
min(payload) then (min)->
max(payload) then (max)->
payload filter $>min+20 and $<max-20
Elégant, non ?
Autre usage de then: pouvoir enchainer et "sauter" une ou plusieurs expressions. Pour cela laissez moi introduire une autre fonction de DataWeave injustement méconnue: log (oui, oui, elle existe, vous ne rêvez pas!). Notre propos, ici est de logger le commencement de notre traitement, puis de l'exécuter, puis enfin de logger sa conclusion avec pour chaque ligne de log un timestamp afin de pouvoir vérifier l'efficacité de notre petit traitement. Voici la nouvelle version:
log("Start:" ++ now()) then (l1)-> (
min(payload) then (min)->
max(payload) then (max)->
payload filter $>min+20 and $<max-20) then (result)->
log("End:" ++ now())
Voila. Nous avons utilisé then dans son usage le plus basique: enchaîner des expressions. Et notre log contient bien les deux lignes souhaitées:
"Start:2024-03-03T12:56:04.573778Z"
"End:2024-03-03T12:56:04.574056Z"
Mais que ce passe-t-il ? Notre expression vaut maintenant "End:2024-03-03T12:56:04.574056Z" et non notre liste filtrée ! En effet, le dernier élément de notre suite d'expressions, c'est la dernière, le log final qui l'emporte ! Et ce log "vaut" la chaîne de caractères qu'il logue. Pas de panique. Il suffit de compléter notre petit traitement, pour qu'il retourne la valeur souhaitée, valeur prise dans la closure de notre filtrage (result):
log("Start:" ++ now()) then (l1)-> (
min(payload) then (min)->
max(payload) then (max)->
payload filter $>min+20 and $<max-20) then (result)->
log("End:" ++ now()) then (l2)->
result
Noter que les closures des deux logs sont ignorées. Elle ne sont nommées que parce que la syntaxe de DataWeave l'impose. Dans ce second cas, on voit bien l'utilisation de la closure qui permet de bypasser le log et de "revenir" à la valeur qui nous intéresse.
_________________________________________________________________________
Today, we're going to talk about a fairly recent feature that's as little known as it is useful: then. To my great surprise, very little mention is made of it in documentation, training courses or forums. Obviously, I've never seen anyone but myself use it, sometimes with severe consequences. What does it do? Well, nothing, really. But it does it wonderfully well. Let me explain. This function can be coded (and used) as follows:
%dw 2.0
output application/json
fun then(x, f) = f(x)
---
payload then(x)-> x.message
Don't create it this way if you're using Mulesoft version 4.3.0 or higher, as it comes as standard. If you have to use an older version, hurry up and include it in your custom library of utility functions!
So what can it be used for? Well, essentially, two things:
- to avoid recalculating a value
- to retain a value in order to skip one or more expressions.
The main purpose of then is (solely) to create what is known in programming as a closure, i.e. to make visible resources (values, functions) defined beyond the function into which they are introduced. I won't dwell on it: if you don't see what it's all about, just ask your Google friend: a bit of reading awaits you :) . Instead, let's look at how the closure introduced by then can be useful, in the two cases mentioned above.
In our first case, we're going to use an array of numbers. The aim is to filter out all the numbers in this array that are greater than the mean. A naive approach (but one that 99.99% of Mulesoft developers take :( ) is:
payload filter $ > avg(payload)
This writing is CATASTROPHIC. Why is it so? Because for EACH number in the list, the average is recalculated! It's a quadratic expression (in NxN). If your table is small, you won't mind. But if your array is very large, performance will plummet! With 10 elements, you'll perform 100 elementary operations, with 1000, it'll be a million, with a million, it'll be a thousand billion! The rule is simple: quadratic expressions are NEVER used. How to avoid them? Very simply:
avg(payload) then (moy)-> payload filter $>moy
We calculate the average once, "store" it in the parameter of the function passed to then, and then use it without having to recalculate it. The expression becomes linear (of order N) and becomes a million times more efficient if the list contains a million elements!
"That's all very well" you might say, in such a simple case. But what if I need several values in my main expression (here filter)? Well, it's very simple: just chain the then. Let's say we now want to retrieve all the numbers between min+20 and max-20. We'll write:
min(payload) then (min)->
max(payload) then (max)->
payload filter $>min+20 and $<max-20
Elegant, isn't it?
Another use for then is to be able to "skip" one or more expressions. To do this, let me introduce another DataWeave function that's unfairly overlooked: log (yes, it exists, you don't dream!). Our purpose here is to log the start of our processing, then to execute it, and finally to log its conclusion with a timestamp for each log line, so that we can check the efficiency of our little processing. Here's the new version:
log("Start:" ++ now()) then (l1)-> (
min(payload) then (min)->
max(payload) then (max)->
payload filter $>min+20 and $<max-20) then (result)->
log("End:" ++ now())
That's it. We've used then in its most basic form: chaining expressions. And our log contains the two lines we wanted:
"Start:2024-03-03T12:56:04.573778Z"
"End:2024-03-03T12:56:04.574056Z"
But what's going on? Our expression is now worth "End:2024-03-03T12:56:04.574056Z" and not our filtered list! In fact, the last element of our expression sequence is the final log! And this log is "worth" the string it logs. No need to panic. All we need to do is complete our little process, so that it returns the desired value, taken from the closure of our filtering (result):
log("Start:" ++ now()) then (l1)-> (
min(payload) then (min)->
max(payload) then (max)->
payload filter $>min+20 and $<max-20) then (result)->
log("End:" ++ now()) then (l2)->
result
Note that the closures of the two logs are ignored. They are only named because DataWeave syntax requires it. In this second case, you can clearly see the use of the closure, which allows you to bypass the log and "return" to the value of interest.
Aucun commentaire:
Enregistrer un commentaire