Mule ESB DataMapper: Aggregation of field 1 multiplied by field 2 - dictionary

I have the following structures:
Strcuture A:
<itemlist>
<item>
<id>123</id>
<price>1</price>
<quantity>1</quantity>
</item>
<item>
<id>124</id>
<price>2</price>
<quantity>1</quantity>
</item>
<item>
<id>125</id>
<price>3</price>
<quantity>1</quantity>
</item>
<itemlist>
Structure B:
<totals>
<total>
<totalPrice>3</totalPrice>
</total>
</totals>
If I want a sumation of all the field multiplied
by the field in structure A to be placed into the totalprice field of structure B, would that be possible in the DataMapper.
If it is possible, how would you do it?
Thanks

Yes it is, in datamapper find the mapping of the field 1 and then go to script view. There you will find a datamapper MEL script. Find output.total = input.field1 and add * input.field2.

Related

Conditional combined indexes:When there are several decimal types of indexes, the desired results can not be queried

Use version:4.5.0
db/system/config/db/test/collection.xconf The code is as follows:
<range>
<create qname="item">
<condition attribute="name" value="number"/>
<field name="number" match="#value" type="xs:decimal"/>
</create>
<create qname="item">
<condition attribute="name" value="acreage"/>
<field name="acreage" match="#value" type="xs:decimal"/>
</create>
<create qname="item">
<condition attribute="name" value="radii"/>
<field name="radii" match="#value" type="xs:decimal"/>
</create>
<create qname="item">
<condition attribute="name" value="diameter"/>
<field name="diameter" match="#value" type="xs:decimal"/>
</create>
</range>
Browse Indexes
db/test The code of an XML file is as follows:
<root>
<item name="number" value="4"/>
<item name="acreage" value="5"/>
<item name="radii" value="6"/>
<item name="diameter" value="7"/> </root>
Query statement:
//item[#name='radii'][#value>5.0]
Query Profiling
No result
In theory, the XML file can be found, but the result can not be found for what? Can you help me? Thank you!
Based on the documentation for eXist's Conditional Combined Indexes feature you are trying to use here, it appears to me that this feature only support string comparisons (with an optional "numeric" mode). See https://exist-db.org/exist/apps/doc/newrangeindex#D3.21.18. In other words, your #type="xs:decimal" is not resulting in your attributes' values being cast to xs:decimal; effectively, instead, they are being indexed as xs:string.
Thus, for your query to work with the given data, change the predicate to [#value gt "5"].
Or, to force numeric comparisons, add numeric="yes" to the <field> element in your index definitions, and then change your predicate to [#value gt "5.0"].

XQuery aggregate result AND give total

I have an XML inventory of items.
The inventory lists how much if Item with ID 1 I have, how much of Item with ID 2, etc (think each item ID represents 1 product).
The list, however, is subdivided, depending on what quantity of items of particular type have a particular mark on them.
So, I have let us say:
Item ID Marked? Qty
1 (no) 500
1 ABC 100
1 (no) 50
1 FFFF 333
2 (no) 10000
....
This ir represented in a structure like this:
<Base>
<Item>
<Id>1</Id>
<Qty>500</Qty>
</Item>
<Item>
<Id>1</Id>
<Qty>100</Qty>
<Mark>ABC</Mark>
</Item>
<Item>
<Id>1</Id>
<Qty>50</Qty>
</Item>
<Item>
<Id>1</Id>
<Qty>333</Qty>
<Mark>FFFF</Mark>
</Item>
<Item>
<Id>2</Id>
<Qty>10000</Qty>
</Item>
...
</Base>
Using XQuery transformation I wish to produce, instead of multiple rows per item ID, 1 aggregate row for each item ID, which would list both total sum of quantities per item (both marked and unmarked), and a separate sum for only those quantities of item that are marked (have subtag. I am not concerned about the contents of the individual tags).
In table form what I want would be this:
Item ID Marked Qty Total Qty
1 433 983
2 0 10000
etc.
In actual XML form the transformation should produce something like this:
<Item><Id>1</Id><MarkedQuantity>433</MarkedQuantity><Total>983</Total></Item>
<Item><Id>2</Id><Total>10000</Total></Item>
....
UPD: Edited for clarity.
If you use
for $item in Base/Item
group by $id := $item/Id
order by $id
return <Item>
<Id>{$id}</Id>
{for $marked in $item[Mark]
group by $mark := $marked/Mark
return <Marked name="{$mark}">
{sum($marked/Qty)}
</Marked>
}
<Total>{sum($item/Qty)}</Total>
</Item>
you will get
<Item>
<Id>1</Id>
<Marked name="ABC">100</Marked>
<Total>650</Total>
</Item>
<Item>
<Id>2</Id>
<Total>10000</Total>
</Item>
I am not quite sure you need or want the inner grouping as I am not sure whether there can be different marks you want to distinguish but hopefully it gives you an idea.
If you simply want a total of Marked Items then I think
for $item in Base/Item
group by $id := $item/Id
order by $id
return <Item>
<Id>{$id}</Id>
{if ($item[Mark])
then <MarkedQuantity>{sum($item[Mark]/Qty)}</MarkedQuantity>
else ()
}
<Total>{sum($item/Qty)}</Total>
</Item>
does that.
Turns out that the answer was way easier (as I suspected):
for $item in //Item
let $d := $item/Id
group by $d
order by $d
return <Marked id="{$d}" Total="{ sum($item/Qty) }">{ sum($item[Mark]/Qty) }</Marked>
Basically, give 2 different sums for the same FOR clause: 1 with total sum of items, another with sum of filtered items (those that have Mark child tag in them).
I still do not understand though, why I can't use <Id>{ $d }</Id><Marked>{ sum($item[Mark]/Qty) }</Marked><Total>{ sum($item/Qty) }</Total> - in which case basex complains of finding } where it wanted >
But basically it works.

Exact constraint search in marklogic8

Two different documents in my database (with default index setup) contains title like below
first document:-
<title>Reconceptualizing Subject</title>
second document:-
<title>Reconceptualizing Subject (LP)</title>
I have created word constraint on title for search in the title
<constraint name="title">
<word>
<element ns="" name="title"/>
<term-option>case-insensitive</term-option>
</word>
</constraint>
so if I query search:search("title:Reconceptualizing Subject") then I am getting both document as a result from database but if I query search:search("title:Reconceptualizing Subject (LP)") it gives me only single document result.
How can I achieve exact search (single document) when I query "title:Reconceptualizing Subject" ?
Change your search constraint to:
<constraint name="title">
<value>
<element ns="" name="title"/>
<term-option>case-insensitive</term-option>
</value>
</constraint>
And then double-quote your search string:
search:search(' title:"Reconceptualizing Subject" ')
HTH!

Detect last post-grouping tuple

In XQuery 3.0, how is it possible to detect the last post-grouping tuple (defined by XQuery 3.0 spec) produced by the return clause? Is it possible to use something like position() = last()? What would be the context for the position function?
For example, say I want to generate CSV output via XQuery. In order to separate lines in the CSV output, I append a new-line after each tuple produced by the return clause:
xquery version "3.0";
declare option saxon:output "omit-xml-declaration=yes";
declare option saxon:output "method=text";
declare variable $data := (
<items>
<item>
<property name="a"/>
<property name="a"/>
<property name="b"/>
</item>
<item>
<property name="a"/>
</item>
<item>
<property name="b"/>
<property name="c"/>
<property name="d"/>
</item>
<item>
<property name="b"/>
<property name="c"/>
</item>
</items>
);
for $item in $data/item,
$name in $item/property/#name
group by $name
return (
text{string-join(($name, string(count($item))), ",")},
text{"
"}
)
However, this leaves an empty line after the last tuple. If I could test for the tuple position, I could avoid appending new-line after the last tuple.
The XQuery specs indeed seem to be missing something like group by $variable at $position here, similar what would be allowed in the for clause. Reading up the XQuery 3.0 specs again, I couldn't find anything that would help, either. position() and last() require a node context, which is not available within loops.
But regarding your underlying problem: why not use another string-join(...) to concatenate the items with newlines in-between, similar to like you did for the counts?
string-join(
for $item in $data/item,
$name in $item/property/#name
group by $name
return (
text{string-join(($name, string(count($item))), ",")}
),
text{"
"}
)

how to save multiple rows in SQL using XML?

I have following XML, and i wish to save its data in my SQL table. I have a table named as tblDummy it has three columns "JobID" "ItemID" "SubitemID". there can be multiple subitemsid for particular combination of Jobid and Itemid. How can i do this?
<jobs>
<job>
<jobid>4711</jobid>
<items>
<itemid>1</itemid>
<subitems>
<subitemid>1</subitemid>
<subitemid>2</subitemid>
</subitems>
<itemid>2</itemid>
<subitems>
<subitemid>7</subitemid>
<subitemid>10</subitemid>
</subitems>
<itemid>9</itemid>
<subitems>
<subitemid>12</subitemid>
<subitemid>16</subitemid>
</subitems>
</items>
</job>
</jobs>
As this XML is, you cannot properly parse it. You would need to change it - you should put each item with its itemid and subitems into a separate <item> node - otherwise you just have a long list of <itemid> and <subitems> nodes under your <items> main node, but you have no means of telling which <itemid> and <subitems> nodes belong together ....
You need to change your XML to be something like this:
<job>
<jobid>4711</jobid>
<items>
<item>
<itemid>1</itemid>
<subitems>
<subitemid>1</subitemid>
<subitemid>2</subitemid>
</subitems>
</item>
<item>
<itemid>2</itemid>
<subitems>
......
</subitems>
</item>
... (possibly more <item> nodes) ....
</items>
</job>
THEN you could use basically the same code I had for your previous question - extended to cover three levels:
CREATE PROCEDURE dbo.SaveJobs (#input XML)
AS BEGIN
;WITH JobsData AS
(
SELECT
JobID = JobNode.value('(jobid)[1]', 'int'),
ItemID = ItemNode.value('(itemid)[1]', 'int'),
SubItemID = SubItemNode.value('.', 'int')
FROM
#input.nodes('/jobs/job') AS TblJobs(JobNode)
CROSS APPLY
JobNode.nodes('items/item') AS TblItems(ItemNode)
CROSS APPLY
ItemNode.nodes('subitems/subitem') AS TblSubItems(SubItemNode)
)
INSERT INTO dbo.tblDummy(JobID, ItemID, SubItemID)
SELECT JobID, ItemID, SubItemID
FROM JobsData
END
Basically, you need three "lists" of XML nodes:
first you need the list of all <jobs>/<job> nodes to get the jobid values
for each of those job nodes, you will also need to get their list of nested <items>/<item> to get the itemid value
from each node, you then also get the list of <subitems>/<subitem>
This will most likely work - but most likely, it will be rather slow (three nested calls to the .nodes() function!).
Update:
OK, so the first call #input.nodes('/jobs/job') AS TblJobs(JobNode) basically creates a "pseudo" table TblJobs with a single column JobNode and each <job> element in your XML is being stored into a row in that pseudo table - so the first row will contain this XML in it's JobNode column:
<job>
<jobid>4711</jobid>
<items>
<item>
<itemid>1</itemid>
<subitems>
<subitemid>1</subitemid>
<subitemid>2</subitemid>
</subitems>
</item>
<item>
<itemid>2</itemid>
<subitems>
......
</subitems>
</item>
... (possibly more <item> nodes) ....
</items>
</job>
and each further row will contain the additional XML fragments for each subsequent <job> element inside <jobs>
From each of those XML fragments, the second call
CROSS APPLY JobNode.nodes('items/item') AS TblItems(ItemNode)
again selects a list of XML fragments into a pseudo table (TblItems) with a single column ItemNode that contains the XML fragment for each <item> node inside that <job> node we're dealing with currently.
So the first row in this pseudo-table contains:
<item>
<itemid>1</itemid>
<subitems>
<subitemid>1</subitemid>
<subitemid>2</subitemid>
</subitems>
</item>
and the second row will contain
<item>
<itemid>2</itemid>
<subitems>
......
</subitems>
</item>
and so on.
And then the third call - you've guessed it - again extracts a list of XML elements as rows into a pseudo-table - one entry for each <subitem> node in your XML fragment.
Update #2:
I'm new to "JobID = JobNode.value('(jobid)[1]', 'int')" line of code
OK - given the <Job> XML fragment that you have:
<job>
<jobid>4711</jobid>
<items>
......
</items>
</job>
the .value() call just executes this XPath expression (jobid) on that XML and basically gets back the <jobid>4711</jobid> snippet. It then extracts the value of that node (the inner text), and the second parameter of the .value() call defines what SQL data type to interpret this as - so it basically grabs the 4711 from the <jobid> node and interprets it as an int
You can take a composite key of Jobid and Itemid as primary key.

Resources