XQuery produces incorrect output - xquery

The example xml looks like this:
<sales>
............
<customer custid="108">
<name>NORTH WOODS HEALTH AND FITNESS SUPPLY CENTER</name>
<address>98 LONE PINE WAY</address>
<city>HIBBING</city>
<state>MN</state>
<zip>55649</zip>
<area>612</area>
<phone>566-9123</phone>
<repid>7844</repid>
<creditlimit>8000</creditlimit>
<ord ordid="613">
<orderdate>1987-02-01</orderdate>
<shipdate>1987-02-01</shipdate>
<total>6400</total>
<item itemid="1">
<product_ref ref="100871"/>
<actualprice>5.6</actualprice>
<qty>100</qty>
<itemtot>560</itemtot>
</item>
</ord>
</customer>
<product prodid="100860">
<descrip>ACE TENNIS RACKET I</descrip>
<price>
<stdprice>35</stdprice>
<minprice>28</minprice>
<startdate>1986-06-01</startdate>
</price>
</product>
.........
</sales>
let $product := doc('sales.xml')/sales/product
for $item in doc('sales.xml')/sales/customer/ord/item
where $item/actualprice < $product[#prodid=$item/product_ref/#ref]/price/minprice
return $item
And I get as result this:
<item itemid="1">
<product_ref ref="100861"/>
<actualprice>35</actualprice>
<qty>1</qty>
<itemtot>35</itemtot>
</item>
<item itemid="3">
<product_ref ref="101863"/>
<actualprice>10</actualprice>
<qty>150</qty>
<itemtot>1500</itemtot>
</item>
<item itemid="7">
<product_ref ref="101863"/>
<actualprice>12.5</actualprice>
<qty>200</qty>
<itemtot>2500</itemtot>
</item>
<item itemid="5">
<product_ref ref="101863"/>
<actualprice>9</actualprice>
<qty>100</qty>
<itemtot>900</itemtot>
</item>
But the items with ref=101863 are not right. Only the item with id=5 is less then minprice.
Why does this error occur? Tried a lot of different queries but it gives me always the same result.
It works fine with ref=100861.

Assuming the query isn't schema-aware, you are comparing the two prices as strings, not as numbers. You need to convert both to numbers before comparison.

Related

Replace text node with element if text match in the document BaseX

I am trying to replace text node with element if document matches that text, below query I have tried but it is giving error "Target is not an element, text, attribute, comment or pi" below is my query.
inputXML:
<book>
<p>Isn't it lovely here? Very smart. We'll be like three queens when you've finished with us,
Edie. You doing well then?</p>
<p>
<name type="person">April De Angelis</name>’ plays include <title type="work">Positive
Hour</title> (Out of Joint) <title type="work">Playhouse Creatures</title> (<name
type="org">Sphinx Theatre Company</name>), <title type="work">Hush</title> (<name
type="org">Royal Court</name>), <title type="work">Soft Vengeance</title>, <title
type="work">The Life and Times of Fanny Hill</title> (adapted from the <name type="org"
>John Cleland novel</name>) and <title type="work">Ironmistress</title>. Her work for
radio includes <title>The Outlander</title> (<name type="org">Radio 5</name>), which won the
<name type="org">Writers’ Guild Award</name> (<date>1992</date>), and, for opera, <title
type="work">Flight</title> with composer <name type="person">Jonathan Dove</name> (<name
type="place">Glyndebourne</name>, <date>1998</date>).</p>
</book>
Expected output:
<book>
<p>Isn't it lovely here? Very smart. We'll be like three <highlight>>queens</highlight> when
you've finished with us, Edie. You doing well then?</p>
<p>
<name type="person">April De Angelis</name>’ plays <highlight>include</highlight>
<title type="work">Positive Hour</title> (Out of Joint) <title type="work">Playhouse
Creatures</title> (<name type="org">Sphinx Theatre Company</name>), <title type="work"
>Hush</title> (<name type="org">Royal Court</name>), <title type="work">Soft
Vengeance</title>, <title type="work">The Life and Times of Fanny Hill</title> (adapted
from the <name type="org">John Cleland novel</name>) and <title type="work"
>Ironmistress</title>. Her work for radio includes <title>The Outlander</title> (<name
type="org">Radio 5</name>), which won the <name type="org">Writers’ Guild Award</name>
(<date>1992</date>), and, for opera, <title type="work">Flight</title> with composer
<name type="person">Jonathan Dove</name> (<name type="place">Glyndebourne</name>,
<date>1998</date>).</p>
</book>
I am using BaseX version 9.5.1 below is the code.
let $body := <indexedterms>
<content>
<terms>
<term>include</term>
<term>Queens</term>
</terms>
<uri>/IEEE/IEEE/test.xml</uri>
</content>
</indexedterms>
for $contents in $body/content
let $uri := $contents/uri
let $doc := fn:doc($uri)
for $selectedterm in $contents/terms/term/string()
let $Modifieddoc := copy $c := $doc
modify
(
for $nodes in $c//*//text()[fn:matches(.,$selectedterm)]/parent::*
return
if($nodes/node()[fn:matches(.,$selectedterm)]/parent::*:highlight)
then ()
else
replace node $nodes/$selectedterm with <highlight>{$selectedterm}</highlight>
)
return $c
return
db:replace('IEEE',substring-after($uri,'/IEEE'),$Modifieddoc)
Previously I was using the "replace node $nodes/node()[fn:contains(.,$selectedterm)] with {$selectedterm} " instead of "replace node $nodes/$selectedterm with {$selectedterm}" it was doing the work but where terms like steam e.g.(include, includes) so it was matching the both words which is not correct so I have changed the code to "replace node "$nodes/$selectedterm with {$selectedterm}"
$nodes/$selectedterm is probably the culprit and most likely not what you want as the $selectedterm variable is a sequence of string values (you bind for $selectedterm in $contents/terms/term/string()). It might help us understand what you want to achieve if you show us a sample document you load with the doc function and the update you want to do on that with BaseX, for instance, for the two sample terms you have shown in your code snippet.
Your task of identifying and wrapping search terms in your text contents can be done nicely in XSLT 3 or 3 which you can run with BaseX if you put Saxon 9.9 or 10 or 11 on the class path:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:param name="terms" as="xs:string*" select="'include', 'Queens'"/>
<xsl:output method="xml" indent="no"/>
<xsl:template match="p//text()">
<xsl:apply-templates select="analyze-string(., string-join($terms, '|'), 'i')/node()"/>
</xsl:template>
<xsl:template match="fn:match">
<highlight>{.}</highlight>
</xsl:template>
<xsl:template match="fn:non-match">
<xsl:apply-templates/>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>
As the used analyze-string function exists also in BaseX/XQuery you should also be able to use XQuery update on the result calling that function, i.e. by replacing fn:match elements with highlight elements.

XQuery: Return value of attribute if it contains another spesific attribute

I want to return a result on all the books which contain the "databases" attribute in inside of "field".
for $e in //testbook[field="databases"]
return $e/title
Sample of the .xml file:
<?xml version="1.0" encoding="UTF-?>
<testbook>
<book>
<author> AuthorGuy </author>
<title> theBook</title>
<field> databases </field>
</book>
</testbook>
There are two issues:
<title/> and <field/> elements are contained in a <book/> element, and are not direct children of <testbook/>, fix the path expressions.
The <field/> tag contains the field wrapped in whitespace, use contains($string, $needle) instead of a simple comparison.
A working example:
let $document := document{<testbook>
<book>
<author> AuthorGuy </author>
<title> theBook</title>
<field> databases </field>
</book>
</testbook>}
for $e in $document//testbook[contains(book/field, "databases")]
return $e/book/title

Create a XML file having a particular schema using Xquery

I am new to Xquery, I want to change the given xml into another xml format.
Given XML:
<?xml version="1.0" encoding="UTF-8"?>
<Store>
<consumer id="H01">
<name>John Doe</name>
<items>
<item type = "Torch">
<price>$3</price>
</item>
<item type = "Gas">
<price>$4</price>
</item>
</items>
</consumer >
<consumer id="H05">
<name>Jane Doe</name>
<items>
<item type = "Cell">
<price>$8</price>
</item>
<item type = "Shirt">
<price>$12</price>
</item>
</items>
</consumer>
Desired XML Format:
<Store>
<user>
<number><id>H01</id><name>John Doe</name></number>
<number><id>H05</id><name>Jane Doe</name></number>
</user>
<inventory>
<number><type>Torch</type><price>$3</price></number>
<number><type>Gas</type><price>$4</price></number>
<number><type>Cell</type><price>$8</price></number>
<number><type>Shirt</type><price>$12</price></number>
</inventory>
</Store>
Xquery I made:
for $customer in distinct-values(doc("../xml/store.xml")/store/consumer/#id)
let $name := doc("../xml/store.xml")/store/consumer[#id=$customer]/name
for $object in distinct- values(doc("../xml/store.xml")/store/consumer[#id=$customer]/items/item/#type)
return
<store>
<user>
<number>
<id>{$customer}</id>
{$name}
</number>
</user>
<inventory>
<number>
<type>{$object}</type>
</number>
</inventory>
</store>
Where exactly am I going wrong? Is there a way we could make attributes as new node elements.
your two for clauses are nested, you don't want that in this case. I would do something like this:
let $doc := doc("../xml/store.xml")
let $customerIds := distinct-values($doc/store/consumer/#id)
return
<store>
<user>{for $customerId in $customerIds
let $consumer := $doc/store/consumer[#id=$customerId]
return <number><id>{data($consumer/#id)}</id><name>{$consumer/name}</name></number>
}
</user>
<inventory>
similar thing for items
</inventory>
</store>
Is there a way we could make attributes as new node elements.
Yes you can for example use the data() function to extract the text. See example above.

Flex e4x filter out children

I'm trying to filter my xml so that the childnodes are not displayed.
This xml will then be used as a dataprovider for my advanceddatagrid.
Given this XML :
<item text="folder1" display="true">
<item text="folder2" display="true">
<item text="node" display="true">
<item display="false">
<property1>val1</property1>
<property2>val2</property2>
</item>
</item>
</item>
</item>
What I want is an XML with only the nodes that have the property display set to true.
So, the resulting XML should be:
<item text="folder1" display="true">
<item text="folder2" display="true">
<item text="node" display="true">
</item>
</item>
</item>
When I try trace(data.item.(#display == 'true')); every node is still displayed, even the ones with display false..
Any help would be appreciated ..
okay, this is how I solved it now :
var childNodes:XMLList = new XMLList(data.descendants("item").(#display == 'false'));
for ( var i:int = childNodes.length() - 1; i >= 0; i-- ) {
delete childNodes[i];
}
I think this is happening because of the format of that XML.
All of the item elements under folder1 are child nodes of folder1. Since it has
display="true", then the trace will display all of its children.
Anyone else, is this XML formatted correctly to do the search?

Voicexml how to store input into a global variable

I'm creating a voicexml appliacation.
I want to store an user input into a global variable.
I wondered, the input should be stored in the fieldvar. shouldn't it? After I tried it with this, i tried to store it in an global variable:
<assign name="myvar" expr="'myinput'"/>
but somehow it didn't work. I used value expr="var" as expr.
<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd"
version="2.0">
<var name="myProdukt" />
<form id="test">
<field name="var">
<prompt bargein="true" bargeintype="hotword" >Sagen Sie ein Produkt</prompt>
<grammar root="main" version="1.0" xml:lang="de-DE">
<rule id="main" scope="public">
<one-of>
<item> p1 </item>
<item> p2 </item>
<item> p3 </item>
<item> p4 </item>
</one-of>
</rule>
</grammar>
<filled>
<assign name="myProdukt" expr="<value expr="var"/>"/>
</filled>
</field>
</form>
<<!--[...] Here i want to use the input.-->
</vxml>
thanks in advance
---------------EDIT:
now i used this:
<filled>
test
<assign name="myProdukt" expr="var" />
</filled>
I only changed that. The Applications says "test" but then there is an error.
It isn'T allowed to use "var" instead I used an other name :-)
Did you try a simple assignment of field var to the variable myProdukt like so ?
<filled>
<assign name="myProdukt" expr="var"/>
</filled>
Which would be fine except that according to Section 5.1, Variables and Expressions of the Voice XML specification:
VoiceXML variables, including form
item variables, must not contain
ECMAScript reserved words.
So, you'll need to rename the field var to something that is not a reserved word in ECMAscript, say productSelection:
<field name="productSelection">
<!-- .. prompt, grammar as before .. -->
<filled>
<assign name="myProdukt" expr="productSelection"/>
</filled>
</field>

Resources