Showing posts with label xsl / xquery. Show all posts
Showing posts with label xsl / xquery. Show all posts

Thursday, March 19, 2015

Xquery performance tips

  • Avoid the use of double slashes (“//”)
  • Index XPath expressions where applicable.  For example, if you know that there is only one “Order” and only one “Address” then using an XPath expression like “$body/Order[1]/Address[1]” instead of “$body/Order/Address”
  • Using predicate is less expensive than where clause e.g. students//[gender=”male”] instead of where $students/gender=”male”.
  • Because sorting is expensive, you should not use the order by clause in FLOWR expressions more than is necessary. In addition, you should minimize the size of the sequence values that are sorted.Beware that some set operations (e.g. Union, intersect, except) will run sort implicitly, so avoid sortingwusing “unordered” expression. e.g. unordered { let $smartstudents := $students//(science|engineering)} instead of let $smartstudents := $students//science| $student//engineering
  • Flatten your query - This means push your where clause into the xpath at the beginning to limit the amount of work you have to do once you’re inside the “for” statement.
  • Extract frequently used parts of a large XML document as intermediate variables.  This will consume more memory, but will reduce redundant XPath processing.  For example: let $customer := $body/Order[1]/CustomerInfo[1]
  • Avoid reduplication of processing (calculate, or walk down the same path) e.g., instead of: {$x/categories/foodstuffs/[@type = 'fish']}
    {$x/categories/foodstuffs/[@type = 'bread']}
    use:  
    let $foodstuffs := $x/categories/foodstuffs return
        {$foodstuffs/[@type = 'fish']}
        {$foodstuffs/[@type = 'bread']}
    This can save a significant amount of time especially if $x or $x/categories or $x/categories/foodstuffs has many children.
  • It might be good to do precalculations e.g. you could put all id’s for docs in a certain state inside of a single doc, which is much cheaper to query than many.
  • Since the xquery processor has exponential complexity, it will appear very fast with small data but much worse with large data, so your XQuery performance should be tested with large payloads whenever possible. Limit your data set - Do whatever it takes. You just want to get to the smallest set of data possible in the most direct way. E.g.putting the most specific predicate first.
Its performance improvement depends on the xquery processors but worth to try:
  • Move ordinals to the end of path expressions e.g./book/@isbn)[1] instead of /book[1]/@isbn
  • Avoid predicates in the middle of path expressions e.g.  /book[@ISBN = "1-8610-0157-6"] "n" /book/author[first-name = "Davis"] intead of  book[@ISBN = "1-8610-0157-6"]/author[first-name = "Davis"]
References:
General tips about performances: http://soa-java.blogspot.nl/2013/01/software-guidelines-performance.html

Friday, January 6, 2012

Gotchas with XQuery: 4 cases


1. In Xquery (and also XSLT) once you defined a variable you can't change its value. This is due to the properties of declarative programming languages. A common workaround is by redefining the variable.

2. A boolean variable can't be compared with a string 'true' (or 'false')
e.g. if ($aboolvariable='true') will not work
solution: if ($aboolvariable= xs:boolean('true'))

3. If you use if you need to use else even if it's empty
e.g. if ($theanswer='true') then 'the answer is true' else ()

4.. Computed constructor element and the attribute string
Given <media type="book" title="Improve your intelligence for dummies"/>, suppose you want to construct an xml element using: element {$media/@type}{$media/@title}. Recall that the definition of the computed constructor element {name}{content}, so we expect to get: <book> Improve your intelligence for dummies </book > but instead we get: <book title="Improve your intelligence for dummies" />. To solve this problem use: element {$media/@type}{ string($media/@title)}.
Source: Steve's blogs http://soa-java.blogspot.com/

Any comments are welcome :)




References:
Common Xquery mistakes by James Fuller

Saturday, December 31, 2011

XQuery vs XSLT comparison: which to use?


The advantages of XSLT:
* XSLT is in xml format, thus XSLT files can be parsed, validated, dynamically created (e.g. using templates) using xml / soa tools.
* Pull/program-driven approach: XSLT works well to query high structured / predictable documents (e.g. a WSDL-defined SOAP message)
* The template is the strong point of XSLT, although it's possible to simulate this with a user-defined xquery function using tree transversal.
* With xsl:import you can override templates, thus improving reusability (analogous to inheritance & polymorphism in OO languages.)

The advantages of Xquery:
* Push/content-driven approach: Xquery is easier than XSLT to deal with loose structure / less predictable documents (e.g. html) where the stylesheets have to react dynamically to the content of the child elements.
* Xquery is less verbose and less cumbersome compared with XSLT, thus it's easier to learn.
* Xquery applies type strictness using the datatype definitions in the schemas.

Other factors to decide is the supports in the tools you used, e.g. Oracle Soa suite has better xslt editor, no xquery editor. On the other hand, the Oracle OSB has better xquery support than xslt. In general XSLT is better adopted in the SOA tools than Xquery, especially the old tools.

My experience: in my job I need to learn them both, when I started to use xml transformation in my job (about 2006) xquery was not exist, so xslt was the only option. Nowadays people in my office use xquery instead of xslt since they use oracle osb more, which has better xquery support, so I need to learn to adopt xquery more.

Source: Steve's blogs http://soa-java.blogspot.com/

Any comments are welcome :)




References:

XSLT: Axis and Predicate power!

This blog shows you about how to take advantage of axis and predicate in xpath expression: axis::test[predicate].
For example you have this XML:

<Projects>
<Project>
<ProjectName> Teach my toddler computer </ProjectName>

<ProjectActivities>
<ProjectActivity>
<ActivityName> Install Qimo Linux </ActivityName>
</ProjectActivity>
<ProjectActivity>
<ActivityName> Teach mouse game for mouse training </ActivityName >
</ProjectActivity>
</ProjectActivities>

<Elements>
<ProjectElement>
<ElementName>mouse skills</ElementName>
</ProjectElement>
<ProjectElement>
<ElementName>menu navigation</ElementName>
</ProjectElement>
</Elements>
</Project>

<Project>

<ProjectName> Make my wife happy</ProjectName>

<ProjectActivities>
<ProjectActivity>
<ActivityName> Buying flowers </ActivityName>
</ProjectActivity>
<ProjectActivity>
<ActivityName> Morning kiss </ActivityName >
</ProjectActivity>
</ProjectActivities>

<Elements>
<ProjectElement>
<ElementName>love</ElementName>
</ProjectElement>
</Elements>
</Project>

<Projects>

you want to transform this XML to this text:

Project: Teach my toddler computer
*Activities: Install Qimo Linux
**Elements: mouse skills, menu navigation
*Activities: Teach mouse game for mouse
**Elements: mouse skills, menu navigation
Project: Make my wife happy
*Activities: Buying flowers
**Elements: love
*Activities: Morning kiss
**Elements: love

using this xslt:


<xsl:for-each select="//Projects/Project">
<xsl:variable name="nuproj" select="ProjectName"/>
Project:<xsl:value-of select="ActivityName"/>,
<xsl:for-each select="ProjectActivities/ProjectActivity">
*Activities:<xsl:value-of select="ActivityName"/>,
<xsl:text></xsl:text>
<xsl:for-each select="following::Elements[parent::Project/ProjectName=$nuproj]/ProjectElement">
**Elements:<xsl:value-of select="ElementName"/> ,
</xsl:for-each>
<xsl:text></xsl:text>
</xsl:for-each>
<xsl:text></xsl:text>
</xsl:for-each>



In this xsl we iterate over ProjectActivity within each Project. So during this iteration the current context is in a ProjectActivity, while you want also to iterate over each Elements/ProjectElement. We solve this using xpath expression in the form of axis::test[predicate] :

following::Elements[parent::Project/ProjectName=$nuproj]/ProjectElement

so the "following" is the axis which tells that we select the Elements/ProjectElement located following the current context ProjectActivity. There many as other axis expression such as preceding, parents, descendants, etc which specifies the location relative to the current context.

[parent::Project/ProjectName=$nuproj] is the predicate, which specifies the condition of which Elements node to be selected (since there are more than one Elements nodes located following the current context ProjectActivity. In this case we specify that the Elements node to be selected should have a parent Project which has Project/ProjectName node with value equal to the variable $nuproj, i.e. the Elements node that belongs to the same Project with the current context ProjectActivity.

Source: Steve's blogs http://soa-java.blogspot.com/

Any comments are welcome :)




References:


Jesper Tverskov's axis tutorial
XSLT 2.0 and XPath 2.0 Programmer's Reference

Testing for Empty Elements in XSLT & Xquery


While processing an XML with XSLT sometimes you need to access a node but you want to make sure that that node exists otherwise the XSLT processor will complain (analogous to the infamous null point exception in Java).

For example using this XML:

<Projects>
<Project>
<ProjectName> Teach my toddler computer </ProjectName>
<ProjectActivities>
<ProjectActivity>
<ActivityName> Install Qimo Linux </ActivityName>
</ProjectActivity>
<ProjectActivity>
<ActivityName> Teach mouse game for mouse training </ActivityName >
</ProjectActivity>
</ProjectActivities>
</Project>

<Project>
<ProjectName> Teach my kids piano </ProjectName>
<ProjectActivities/>
</Project>

<Projects>

Suppose you want to iterate over ProjectActivity within project, but some project has no ProjectActivity (such as the "Teach my kids piano" project above).

XSLT solution
* To test if the element exists in XSLT you can use: xsl:if test="ProjectActivities/ProjectActivity".
*
To test if the element exists and non-empty: xsl:if test="string(ProjectActivities/ProjectActivity)".
*To test that the element exists and has text or any element content (e.g. subnodes or attributes): xsl:if test=" ProjectActivities/ProjectActivity/text() or ProjectActivities/ProjectActivity/*"

So for example using this test in this XSL:

<xsl:for-each select="//Projects/Project">
<xsl:variable name="nuproj" select="ProjectName"/>
Project:<xsl:value-of select="ActivityName"/>
<xsl:if test="ProjectActivities/ProjectActivity">
<xsl:for-each select="ProjectActivities/ProjectActivity">
To do:<xsl:value-of select="ActivityName"/>
<xsl:text></xsl:text>
</xsl:for-each>
</xsl:if>
<xsl:text></xsl:text>
</xsl:for-each>

you will expect this result:

Project: Teach my toddler computer
To do: Install Qimo Linux
Project: Teach my kids piano

Xquery solution:
* To test if the element exists :exists($ProjectVariable/ProjectActivities/ProjectActivity)
or using similar strategy used by xslt above:
if ($ProjectVariable/ProjectActivities/ProjectActivity) then ... else ...

*To test if the string non-empty:string-length($ProjectVariable/ProjectActivities/ProjectActivity) != 0)
or using similar strategy used by xslt above:
if (string($ProjectVariable/ProjectActivities/ProjectActivity)!="") then ... else ...

Source: Steve's blogs http://soa-java.blogspot.com/

Any comments are welcome :)




References:
XSLT Empty Element tutorial

Beginning XSLT and XPath: by Ian Williams

Friday, April 8, 2011

XSLT to transform a flat XML to a hierarchy / tree using grouping

A common problem in the SOA development is that you need to provide a webservice which produce a hierarchy/tree-structure XML such as:


<toys>
<category> Train </category>
<members>
<name>Thomas </name>
<name> Percy</name>
</members>
</toys>
<toys>
<category> Sesame street Monsters </category>
<members>
<name> Elmo </name>
<name> Zoe </name>
</members>
</toys>

Where the data come from a relational database table such as:























Category Name
Train Thomas
Train Percy
Monster Elmo
Monster Zoe



When you build this webservice using Oracle SOA suite, first you make a database adapter (the right side in the picture bellow) then expose this to a web service (the left side in the picture below). You can add a mediator between them which is handy for routing (if you use more than one database adapters / external webservice calls) and/or transformations.

However the output from this database-webservice is apparently just a flat XML such as:

<toy>
<category>Train </category>
<name>Thomas </name>
</toy>
<toy>
<category>Train </category>
<name>Percy </name>
</toy>
<toy>
<category>Monster </category>
<name>Elmo </name>
</toy>
<toy>
<category>Monster </category>
<name>Zoe </name>
</toy>

Thus we need to transform this flat XML to the hierarchical XML as mentioned above. With Oracle SOA Suite, you can define de Schema in the exposed webservice and then apply a transformation in the reply route from the database adapter to the exposed webservice.

A common XSLT solution for this problem is the Muenchian method. However, thanks to XSLT 2.0 now we have a much easier solution using xsl:for-each-group. Here is an example of the XSLT code that I used:



You're welcome to drop comments or discuss more XSL/XML/SOA tricks.

To learn more about grouping or XSLT in general, please read the "Beginning XSLT and Xpath."


Oracle SOA Suite is a great way to build a web service to publish your database. This solution can be built within minutes. Please read "Getting Started With Oracle SOA Suite" to learn more.