BizTalk mapping records into pairs - biztalk

Using a BizTalk Map and wondering if there's a good way to do the following.
I need to map a received Invoice to a list of Orders. However each Order must contain only two line items (let's assume the Invoice has an even number).
This means that Invoice(1, 2, 3, 4) should map to Orders(Order (1, 2), Order (3, 4)).
Essentially, I need to create new parent elements when (index % 2 == 0).
Is there a functoid-only way of doing this?
XML example below:
<Invoice>
<Client>Client1</Client>
<Line>
<Code>1</Code>
<Price>$1.00</Price>
<Qty>1</Qty>
</Line>
<Line>
<Code>2</Code>
<Price>$2.00</Price>
<Qty>2</Qty>
</Line>
<Line>
<Code>3</Code>
<Price>$3.00</Price>
<Qty>3</Qty>
</Line>
<Line>
<Code>4</Code>
<Price>$4.00</Price>
<Qty>4</Qty>
</Line>
</Invoice>
to
<Orders>
<Order>
<Client>Client1</Client>
<OrderItem>
<Code>1</Code>
<Qty>1</Qty>
</OrderItem>
<OrderItem>
<Code>2</Code>
<Qty>2</Qty>
</OrderItem>
</Order>
<Order>
<Client>Client1</Client>
<OrderItem>
<Code>3</Code>
<Qty>3</Qty>
</OrderItem>
<OrderItem>
<Code>4</Code>
<Qty>4</Qty>
</OrderItem>
</Order>
</Orders>
What I've tried:
Standard mapping: A single Order with all lines, as expected
Value Mapping to Client based on index % 2: Order per line (half have Client)
Table Looping (Gated to index % 2) + Extractor to Client: Order per line, only half lines
What I'm likely to end up doing:
XSLT
Or, C# manipulation after mapping

In the advanced functoids section of the toolbox look for the 'index' functoid.
Add a scripting functoid on the output.
Add C# code to check for the indexes you want to match.
You can switch output on and off by returning a boolean from the scripting functoid.

Related

How to set weight for geo-elem-pair-query in MarkLogic structure query?

I'm trying to rewrite the following cts query using XML structure query:
cts:element-pair-geospatial-query(
fn:QName("http://www.example.com/2009/foo","wgs84"),
fn:QName("http://www.example.com/2009/foo","latitude"),
fn:QName("http://www.example.com/2009/foo","longitude"),
cts:circle("#12 53.411541,-2.9900994"),
("coordinate-system=wgs84","score-function=reciprocal","slope-factor=4"),
32
)
I converted it into:
<geo-elem-pair-query>
<parent ns="http://www.example.com/2009/foo" name="wgs84" />
<lat ns="http://www.example.com/2009/foo" name="latitude" />
<lon ns="http://www.example.com/2009/foo" name="longitude" />
<fragment-scope>documents</fragment-scope>
<geo-option>coordinate-system=wgs84</geo-option>
<geo-option>score-function=reciprocal</geo-option>
<geo-option>slope-factor=4</geo-option>
<circle>
<radius>12.0</radius>
<point>
<latitude>53.411541</latitude>
<longitude>-2.9900994</longitude>
</point>
</circle>
</geo-elem-pair-query>
Unfortunately, I don't know how to add weight into <geo-elem-pair-query>. Accordingly to MarkLogic documentation it seems to be unsupported (but cts equivalent supports it). I've tried to add <weight>32.0</weight> but it doesn't work.
Do you know if there is a way to add weight to geo-elem-pair-query structure query?

How to Concatenate multiple repetitive nodes into a single node - BizTalk

I have something like this in an input XML
<OrderText>
<text_type>0012</text_type>
<text_content>Text1</text_content>
</OrderText>
<OrderText>
<text_type>ZT03</text_type>
<text_content>Text2</text_content>
</OrderText>
The above data I need to map after concatenating as the below schema
<Order>
<Note>0012:Text1#ZT03:Text2</Note>
</Order>
Can anyone please help?
I'm going to assume that your input actually has a Root node, as otherwise it is not valid XML.
<Root>
<OrderText>
<text_type>0012</text_type>
<text_content>Text1</text_content>
</OrderText>
<OrderText>
<text_type>ZT03</text_type>
<text_content>Text2</text_content>
</OrderText>
</Root>
Then all you need is a map like this
With a String Concatenate functoid with
Input[0] = text_type
Input[1] = :
Input[2] = text_content
Input[3] = #
That goes into a Cumulative Concatenate
This will give you an output of
<Order>
<Note>0012:Text1#ZT03:Text2#</Note>
</Order>
Note: There is a extra # at the end, but you could use some more functoids to trim that off if needed.
You can use the Value-Mapping Flattening functoid in a map, then feed the result of each into a Concatenate functoid to generate the result string. The map can be executed on a port or in an orchestration.

Drillthrough to underlying text data in icCube?

How to set-up a model in icCube to allow to drill down to the details, when details contain text fields?
The idea is to get a list, with column names containing the text fields (in combination with amount fields). Just like a simple SQL statement would give.
I have tried the following:
a) added a technical dimension that is linked to the rows (via rownumber) and added MIN Aggregation for the text fields. With the idea to use these when a DRILLTHROUGH MDX statement is invoked. The DRILLTHROUGH function works, but does not give the values next to each other for the measures. Result is like:
b) added each unique line a line number and loaded the line number as lowest detail in one of the dimensions. Added attributes for these text and date items for the "drillthrough" columns. Next, added calculated measures to get the property for these attributes. The drillthrough is now effectively a drillby to the lowest details. It works, but this is not nice as it blows up my dimension.
c) tried to use the widget data source SQL, but it is not available for text files, and it does not work for MSAccess files (too slow).
The preferable solution should works in the dashboards and in any XMLA/REST API interface.
Enclosed this example
the schema file
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<schemaFactory revisionNumber="7">
<schemaDefinition name="drilltrhough-text" description="" group="Issues" loadOnStartup="false">
<activateIncrementalLoad>false</activateIncrementalLoad>
<useUnknownMembersInFacts>true</useUnknownMembersInFacts>
<autoCleanUpTableColumns>false</autoCleanUpTableColumns>
<useFactPartitioning>false</useFactPartitioning>
<callGarbageCollector>NONE</callGarbageCollector>
<backup>NONE</backup>
<nonEmptyCachePolicy>NONE</nonEmptyCachePolicy>
<nonEmptyCacheType>REGULAR</nonEmptyCacheType>
<nonEmptyCachePersistency>MEMORY</nonEmptyCachePersistency>
<storagePolicy>DEFAULT</storagePolicy>
<hierarchyUniqueNameStyle>IncludeDimensionName</hierarchyUniqueNameStyle>
<inMemoryDS name="data">
<memoryDataTable tableName="data" rowLimit="-1" id="d9429713-9be8-4c63-9b40-4a20388e7563">
<column name="dimension" tableType="STRING" type="STRING" selected="true" primaryKey="false"/>
<column name="amount" tableType="STRING" type="STRING" selected="true" primaryKey="false"/>
<column name="text" tableType="STRING" type="STRING" selected="true" primaryKey="false"/>
<addRowNumber>false</addRowNumber>
<stringDateConverter></stringDateConverter>
<trimStrings>true</trimStrings>
<columnSeparator>,</columnSeparator>
<commentMarker>#</commentMarker>
<dataAsString>dimension, amount, text
a, 10,some text
b, 20, some more text
c, ,text without an amount</dataAsString>
</memoryDataTable>
</inMemoryDS>
<multiLevelDimension dataTableId="d9429713-9be8-4c63-9b40-4a20388e7563" isTimeDimension="false" isDefaultTimeDimension="false" isIndexingByRange="false" id="86d118f0-71ba-4826-a6ac-343eac96fb05" name="Dimension">
<multiLevelHierarchy hasAllLevel="true" allLevelName="All-Level" allMemberName="All" name="Dimension" isDefault="true">
<level name="Dimension - L" nameUnique="false" nameUniqueInParent="false" keyUnique="false" ignoreNameCollision="false">
<nameCol name="dimension"/>
<orderType>BY_NAME</orderType>
<orderKind>ASC</orderKind>
</level>
</multiLevelHierarchy>
</multiLevelDimension>
<cube id="caa9c520-f953-4c77-9e72-76c8668170f7" name="Cube">
<defaultFacts measureGroupName="Facts" partitioningLevelName="" partitioningType="NONE" newGeneration="true" dataTableId="d9429713-9be8-4c63-9b40-4a20388e7563" aggregateDataSourceFacts="false" unresolvedRowsBehavior="ERROR">
<rowFactAggregationType>ADD_ROW</rowFactAggregationType>
<measure name="Amount" aggregationType="SUM">
<dataColumn name="amount"/>
</measure>
<measure name="Text" aggregationType="MIN">
<dataColumn name="text"/>
</measure>
<links dimensionId="86d118f0-71ba-4826-a6ac-343eac96fb05">
<viewLinks type="LAST_LEVEL">
<toColumns name="dimension"/>
</viewLinks>
</links>
</defaultFacts>
</cube>
</schemaDefinition>
</schemaFactory>
- the mdx
drillthrough
select [Measures].members on 0
, [Dimension].[Dimension].[Dimension - L] on 1
from [cube]
return Name([Dimension])
the result
This is not related to having a measure of type STRING.
You're performing a multi-cell result drillthrough (which is an extension of standard MDX in icCube). In that case, the result is "organized" per result cell meaning each [Measures] being in its own category (you can add another Amount measure and you'll see the same behavior).
Instead you should perform a single cell drillthrough:
drillthrough
select [Dimension].[Dimension].[Dimension -L].[a] on 0
from [cube]
And the result should look like:
You can see the [Measures].[Info] being on the same row (as all the other measures).
Hope that helps.

Xquery html formatting

I'm new to Xquery. I have a requirement of rewriting the API response into custom xml format.
Input file format:
<root> <_1>
<dataType>
<name>XVar(Osmo [mOsmol/kg])</name>
<term>M185</term>
<type>XVar</type>
</dataType>
<values>305</values>
<values>335</values> </_1> <_2>
<dataType>
<name>XVar(DO (2) [%])</name>
<term>M199</term>
<type>XVar</type>
</dataType>
<values>12</values>
<values>33</values>
</_2> <_3>
<dataType>
<name>Maturity</name>
<type>Maturity</type>
</dataType>
<values>0</values>
<values>0.73600054</values>
</_3> </root>
Expected output:
<element> <XVar(Osmo [mOsmol/kg]> 305</XVar(Osmo [mOsmol/kg]>
<XVar(Osmo [mOsmol/kg]> 335</XVar(Osmo [mOsmol/kg]> <XVar(DO (2)[%])>
12</XVar(DO (2) [%])> <XVar(DO (2) [%])>33 </XVar(DO (2) [%])>
<Maturity>0</Maturity> <Maturity>0.73600054</Maturity> </element>
no of nodes (dataType -> name) will vary in each input file and also
Values will be dynamics .
currently using the below code.
let $input:= /root for $i in $input//values
return <element>
<name>{$i/../dataType/name/text()}</name> <values>{$i/text()} </values>
</element>
but all data are coming in and . my requirement is to
keep the node name as {$i/../dataType/name/text()} as values should
be {$i/text()} -
for the input file sample ideally there should be three different
nodes and its values.
Can any one help me on this?

DataSet.WriteXml() - how to "drop" some fields

I've 2 questions:
First, I've a dataset with 5 tables. I've made the relationships of tables and generating an XML from this dataset like this:
StreamWriter xmlDoc = new StreamWriter("myxml.xml", false);
ds.WriteXml(xmlDoc);
xmlDoc.Close();
Some of the fields in each table in dataset are primary keys and I dont want to show them in XML. If I exclude them from tables, I cant make relationships. Can anybody give me some idea that how to write dataset to XML "droping" the key fields (columns in datatables)? For example, here is the XML generating at the moment:
<?xml version="1.0"?>
<o>
<sp spname="SP1" spid="8">
<event spid="8" eventname="Event1" eventId="482">
<bm bmname="BM1" bmid="2" bmeid="826" eventid="482">
<att bmeid="826" val="3.00" attname="Att1" atttype="Type1" attid="23172"/>
<att bmeid="826" val="3.50" attname="Att2" bettype="Type1" attid="23173"/>
</bm>
</event>
</sp>
</o>
but I want this XML to be generated like this (all id attributes should be "dropped" as all ids are for relationships and should not be added in XML):
<?xml version="1.0"?>
<o>
<sp spname="SP1">
<event eventname="Event1">
<bm bmname="BM1" bmid="2">
<att val="3.00" attname="Att1" atttype="Type1" />
<att val="3.50" attname="Att2" bettype="Type1" />
</bm>
</event>
</sp>
</o>
And now 2nd question:
I've given name to my dataset as "o" so its generating the xml as you can see above. I want to add some attributes to <o> node like current datetime. I mean I want <o> node to be genrated as <o generatedDate="09/13/2011" generatedTime="03:45 PM">. How can I achieve it?
Thanks,
You can hide the column to exclude it from the XML-File:
dataSet.Tables["TableName"].Columns["ColumnName"].ColumnMapping = MappingType.Hidden;
One option is to use LINQ to XML to alter the document. Another option would be to pass the XML into an XSLT transformer, which can parse the XML and output the desired results.
Transforming a DataSet using XSLT:
DataTable table = new DataTable();
System.IO.StringWriter writer = new System.IO.StringWriter();
//notice that we're ignoring the schema so we get clean XML back
//you can change the write mode as needed to get your result
table.WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
string dataTableXml = writer.ToString();
As for displaying it in a readable format, I would suggest passing the XML into an XSL transformer, which you can then use to parse the XML and manipulate the output as needed.
Applying an XSLT Transform to a DataSet
http://msdn.microsoft.com/en-us/library/8fd7xytc%28v=vs.71%29.aspx#Y289
Here's a simple example I created to explain how you would use the XSL transformer. I haven't tested it, but it should be pretty close:
DataSet ds = new DataSet();
StringBuilder sbXslOutput = new StringBuilder();
using (XmlWriter xslWriter = XmlWriter.Create(sbXslOutput))
{
XslCompiledTransform transformer = new XslCompiledTransform();
transformer.Load("transformer.xsl");
XsltArgumentList args = new XsltArgumentList();
transformer.Transform(new XmlDataDocument(ds), args, xslWriter);
}
string dataSetHtml = sbXslOutput.ToString();
Formatting XML as HTML using XSLT
Here's an example of using XSLT to transform XML into an HTML table. It should be fairly easy to adopt so you can use it with your serialized DataSet.
Let's say this is your DataSet, serialized to XML:
<RecentMatter>
<UserLogin>PSLTP6\RJK</UserLogin>
<MatterNumber>99999-2302</MatterNumber>
<ClientName>Test Matters</ClientName>
<MatterName>DP Test Matter</MatterName>
<ClientCode>99999</ClientCode>
<OfficeCode/>
<OfficeName/>
<Billable>true</Billable>
<ReferenceId/>
<LastUsed>2011-08-23T23:40:24.13+01:00</LastUsed>
</RecentMatter>
<RecentMatter>
<UserLogin>PSLTP6\RJK</UserLogin>
<MatterNumber>999991.0002</MatterNumber>
<ClientName>Lathe 1</ClientName>
<MatterName>LW Test 2</MatterName>
<ClientCode/>
<OfficeCode/>
<OfficeName/>
<Billable>true</Billable>
<ReferenceId/>
<LastUsed>2011-07-12T16:57:27.173+01:00</LastUsed>
</RecentMatter>
<RecentMatter>
<UserLogin>PSLTP6\RJK</UserLogin>
<MatterNumber>999991-0001</MatterNumber>
<ClientName>Lathe 1</ClientName>
<MatterName>LW Test 1</MatterName>
<ClientCode/>
<OfficeCode/>
<OfficeName/>
<Billable>false</Billable>
<ReferenceId/>
<LastUsed>2011-07-12T01:59:06.887+01:00</LastUsed>
</RecentMatter>
</NewDataSet>
Here's an XSLT script that transforms the DataSet to HTML:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<table border="1">
<tr>
<th>User Login</th>
<th>Matter Number</th>
...
</tr>
<xsl:for-each select="NewDataSet/RecentMatter">
<tr>
<td>
<xsl:value-of select="UserLogin"/>
</td>
<td>
<xsl:value-of select="MatterNumber"/>
</td>
...
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
I would suggest after generating the XML, use LINQ to XML to filter out / add attribute to desired nodes. This filtering and adding attribute is a separate step then generating the XML from data set and should be handled after the XML is generated from data set as that would lead to better design.

Resources