Le schéma source est issu d’une base de données SQL, il est composé d’un seul record répétable « Order », contenant tous les champs.
Le schéma de destination est composé de plusieurs records qui sont imbriqués.
Notre besoin ici est de grouper les données source, selon plusieurs critères :
Nous pouvons recevoir environ 50 000 lignes en entrée de la map, c’est pour cela qu’il faut trouver la méthode la plus optimisée pour faire ce traitement.
Pour commencer il semble difficile d’effectuer un tel traitement avec le mappeur, une map en full XSLT s’impose. Le Group By en XSLT 1.0 n’existant pas, il faut donc employer la méthode de Muenchian.
https://en.wikipedia.org/wiki/XSLT/Muenchian_grouping
Cette méthode consiste à créer une clé composite.
<xsl:key name="" match="" use=""/>
Une key est composée d’un nom, d’une requête XPath et de ce qui va permettre d’identifier la ligne recherchée : cela peut être un élément, un attribut ou encore une valeur calculée.
A noter : une key ne peut pas être créée dans un template, on ne peut donc pas utiliser une variable dans la requête XPath.
Dans le cas présent, le premier groupement est effectué sur le champ ShipmentNumber.
La première key est donc la suivante :
<xsl:key name="groupByShipment" match="/s0:ListOrders/Order" use="./ShipmentNumber"/>
Pour effectuer le groupement, il faut alors faire appel à la key dans un foreach.
La fonction generate_id() permet ensuite d’identifier un noeud spécifique en fonction de la key utilisée.
<xsl:for-each select= "/s0:ListOrders/Order[generate-id(.)=generate-id(key('groupByShipment', ./ShipmentNumber))]">
A la sortie de ce foreach, nous avons donc l’équivalent d’un distinct effectué sur le champ ShipmentNumber.
A l’intérieur du foreach, nous assignons à une variable toutes les lignes avec un même ShipmentNumber.
<xsl:variable name="Shipment" select="key('groupsByShipment', ./ShipmentNumber)"/>
Il faut maintenant grouper sur le ContainerNumber, sans perdre le groupement par ShipmentNumber, nous avons donc besoin d’une deuxième clé :
<xsl:key name="groupByContainer" match="/s0:ListOrders/Order" use="concat(./ShipmentNumber, ./ContainerNumber)"/>
En utilisant un concat sur les deux champs cela permet de dissocier deux ContainerNumber identiques qui n’auraient pas le même ShipmentNumber.
Nous avons donc un nouveau foreach qui cette fois va grouper par ContainerNumber. Ici, nous ne partons plus de la racine mais de la variable contenant toutes les lignes d’un même ShipmentNumber.
A noter : Si nous n’avions pas utilisé le concat mais simplement ContainerNumber, la key n’aurait été valable qu’une seule fois par ContainerNumber et donc à la deuxième itératrion du groupeByShipment nous n’aurions pas pu avoir un ContainerNumber déjà trouvé dans la première itération.
Il suffit ensuite de répéter l’opération pour chaque groupement.