BizTalk Training – Mapping – Muenchian Grouping and Sorting in BizTalk Maps without losing Map functionalities

Posted: October 28, 2009 in BizTalk
Tags: , , ,

There is an astonishing post by Chris Romp about Muenchian Grouping and Sorting in BizTalk Maps, but it has one limitation, by creating and configures Custom XSL Path we lose all mapping features.


So how can we use Muenchian Grouping without losing Map features?

My First approach: Was try to put an Inline XSLT functoid and put all the XSL inside


<xsl:key name="groups" match="Order" use="OrderId"/>
<!-- This will loop through our key ("OrderId") -->
<xsl:for-each select="Order[generate-id(.)=generate-id(key('groups',OrderId))]">
   <!-- And let's do some sorting for good measure... -->
   <xsl:sort select="OrderId" order="ascending"/>
   <Order>
      <OrderId><xsl:value-of select="OrderId/text()" /></OrderId>
      <Items>
         <!-- Another loop... -->
         <xsl:for-each select="key('groups',OrderId)">
            <ItemId><xsl:value-of select="ItemId" /></ItemId>
         </xsl:for-each>
      </Items>
   </Order>
</xsl:for-each>

The problem with that approach is that gives an error:

  • XSLT compile error at (9,8). See InnerException for details. ‘xsl:key’ cannot be a child of the ‘ns0:OutputOrder’ element

So, to avoid this error we need to separate “<xsl:key name=”groups” match=”Order” use=”OrderId”/> expression from the rest of the XSL (see second approach)

Second Approach:

Add two scripting functoids to the map

  • In the first, configure to an “Inline XSLT Call Template” and put key expression
    • <xsl:key name="groups" match="Order" use="OrderId"/>
      
  • In the second, configure to an “Inline XSLT” and the rest of the XSL
    • <!-- This will loop through our key ("OrderId") -->
      <xsl:for-each select="Order[generate-id(.)=generate-id(key('groups',OrderId))]">
         <!-- And let's do some sorting for good measure... -->
         <xsl:sort select="OrderId" order="ascending"/>
         <Order>
            <OrderId><xsl:value-of select="OrderId/text()" /></OrderId>
            <Items>
               <!-- Another loop... -->
               <xsl:for-each select="key('groups',OrderId)">
                  <ItemId><xsl:value-of select="ItemId" /></ItemId>
               </xsl:for-each>
            </Items>
         </Order>
      </xsl:for-each>
      


See Sample 1, map “MapOrder.btm”

How can we improved (a little more) this solution

When leading with large files, speed processing is vital. Classical Muenchian grouping use generate-id(). Muenchian grouping using generate-id() is slowest that using count() function , and shows worst scalability. Probably the reason is poor generate-id() function implementation. In other words, count() function performs is much better.

So to improve Meunchian a little more we have to use count() function instead of generate-id():

  • <xsl:for-each select="Order[count(. | key('groups',OrderId)[1]) = 1]">
    
  • See Sample 1, map “MapOrder2.btm”

Here some performance stats that I found (see original post):

The graph view works better:


Download the example source code:

Muenchian Grouping and Sorting in BizTalk Maps without losing Map functionalities (168.0 KB)
Microsoft | MSDN Code Gallery

Comments
  1. Salam says:

    Both articles are lovely and usefull. I faced one issue when I configured what Chris suggested. Inside VS, testing map gives expected outcome. When I deployed the application, created a recieve location and port (configured map on receive port), also configured a send port which subscribes to the rport, droping the sampl file, I get an ouput file exactly the same as the input which is Order222|Item123Order111|Item456Order222|Item789as if map is not appliedAm I missing something, thankls for your help

  2. Salam says:

    Now I tried your maps inside my application configured Receive Port and Send port, outcome is the same, no xml is coming out, the same flat file is send to ourput directory. Did you apply your solution in a run time environment?Thanks for your help

  3. Sandro says:

    Hi,Yes I apply the solution in run time environment.Both solutions, Chris Romp and mine, work well.Just deploy the schemas and map into a BizTalk application, and then: • Create new ReceivePort (one-way) ex: ReceivePortMG o New Receice Location: Type: FILE, Receive pipeline: XMLReceive o Inbound Map: config the map, In my case o Source: InputOrder o Map: MapOrder o Target: OutputOrder • Create SendPort (static one-way): Type: FILE, Send pipeline: XMLTransmit o Apply a filter to the send port o Property: BTS.ReceivePortName o Operator: == o Value: ReceivePortMGStart the ports.

  4. Sandro says:

    Hi, check this solution: http://code.msdn.microsoft.com/Muenchian-Grouping-and-790347d2 have two folders:C:\Development\Testes\MuenchianGrouping\Samples\INC:\Development\Testes\MuenchianGrouping\Samples\OUT

  5. How about two level group? Let’s say, we have first level by Customer, then second level group by Order id. How can I do this?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s