let $stylesheet := "abc.xsl"
let $params := map:map()
let $_ := map:put ($params,"col1","abc")
return
xdmp:xslt-invoke(
$stylesheet, (), $params,
<options xmlns="xdmp:eval">
<template>a:schema</template>
</options>)
abc.xsl
<xsl:template name="a:schema">
<xsl:param name="collection-uri" as="xs:string" select="$col1"/>
<xsl:apply-templates select="collection($collection-uri)"/>
</xsl:template>
In this currently ,we are taking all the document ,which is coming in collection "abc".
But I want to add more than one collection in $param map, so that the document which contain ,both collection "abc" and "def" will comes
for example :
| Document| collection
|:---------|:----------:|
| Doc1 | abc, def |
| Doc2 | abc |
| Doc3 | abc, def |
it will pick Doc1 and Doc3
collection() accepts a sequence of xs:string, but would return any of the documents in either of the collections specified.
If you want only the docs that are in all of the collections specified, you could use cts:search() with a sequence of cts:collection-query() inside of a cts:and-query().
<xsl:template match="/">
<xsl:param name="collection-uri" as="xs:string*" select="$col1"/>
<xsl:apply-templates select="cts:search(doc(), cts:and-query(( $collection-uri ! cts:collection-query(.) )))"/>
</xsl:template>
Enable the 1.0-ml dialect, so that you can use the cts built-in functions by adding the following attribute to your xsl:stylesheet element:
xdmp:dialect="1.0-ml"
The $collection-uri param is declared as xs:string, so it will only have one string value. You could change that to be a sequence of strings with either * or + quantifier:
<xsl:param name="collection-uri" as="xs:string*" select="$col1"/>
and then set the collections on the $col1 param:
let $_ := map:put ($params,"col1", ('abc', 'def'))
Related
In XPATH under XSLT 2.0, I am unclear as to why an xsl:choose/xsl:when #test isn't working.
When I run this template testing for the element tei:del[#rend='expunctus'], the test DOES NOT return the result:
<xsl:template match="tei:del[#rend='expunctus'] |
tei:gap |
tei:sic |
tei:supplied[#reason='added'] |
tei:surplus[#reason='repeated' or #reason='surplus'] |
tei:unclear">
<xsl:choose>
<xsl:when test="tei:del[#rend='expunctus']">
[<xsl:text>EXPUNCTUS</xsl:text>]
</xsl:when>
</xsl:template>
When I run this template with just the attribute #rend='expunctus' as the test, the test DOES return the result:
<xsl:template match="tei:del[#rend='expunctus'] |
tei:gap |
tei:sic |
tei:supplied[#reason='added'] |
tei:surplus[#reason='repeated' or #reason='surplus'] |
tei:unclear">
<xsl:choose>
<xsl:when test="#rend='expunctus'">
[<xsl:text>EXPUNCTUS</xsl:text>]
</xsl:when>
</xsl:template>
Is this because of the current node already selected?
I prefer to test against the element, not just the attribute, to eliminate possible ambiguity.
Thanks.
Yes, it is because of the current node selected.
Your template matches tei:del[#rend='expunctus'] (amongst other things), so when you do <xsl:when test="tei:del[#rend='expunctus']"> this is relative to the node you have matched, so it is looking for another tei:del as a child node of the current node.
What you probably need to do is this...
<xsl:when test="self::tei:del[#rend='expunctus']">
Alternatively, consider using separate templates for each possible node and putting any shared code in a named template.
I have data structured like this:
<url title="Stack Overflow">http://stackoverflow.com/</url>
<url>http://example.com/</url>
The title attribute is optional. I have an XSLT stylesheet including this:
<xsl:template match="url">
<xsl:value-of select="(#title | .)[1]"/>
</xsl:template>
I want to display the title if there is one and otherwise display the url. However for some reason the title is never displayed. (In every case the url is displayed.)
What am I doing wrong?
Thanks!
Try either:
<xsl:value-of select="(#title | text())[1]"/>
or:
<xsl:value-of select="(#title | .)[last()]"/>
Explanation:
The . abbreviation in your expression represents the url element itself, which precedes its attributes in document order.
A sample structure of my input xml looks as below:
<Products>
<Product>
<ID>Product1</ID>
<Extra1></Extra1>
<Extra2></Extra2>
<Img1>val1</Img1>
<Img2>val2</Img2>
<Img3>val2</Img3>
<Img4>val1</Img4>
</Product>
<Product>
<ID>Product2</ID>
<Extra1></Extra1>
<Extra2></Extra2>
<Img1>val1</Img1>
<Img2>val2</Img2>
<Img3>val2</Img3>
<Img4>val1</Img4>
</Product>
</Products>
I am parsing each product element at a time and grouping the Img tags of each product by its value. I am using <xsl:key name="keyImg" match="Product/*[contains(local-name(), 'Img')]"
use="."/>. But the same key is used over and over again. Will it be an issue if two product contains same value for some Img tag? I am not sure if there will be conflicts in such cases. Please guide. Thanks in advance.
Use a two-part key, like this:
<xsl:key
name="keyImg"
match="Product/*[starts-with(name(), 'Img')]"
use="concat(generate-id(..), '-', .)"
/>
and
<xsl:template match="Product">
<xsl:variable name="productID" select="generate-id()" />
<xsl:for-each select="*[starts-with(name(), 'Img')][
generate-id() = generate-id(key(concat($productID, '-', .)))
]">
<!-- ... --->
</xsl:for-each>
</xsl:template>
On a general note it's not ideal if nodes with the same semantic value (Img) have different names (Img1, Img2, etc). If you can do anything about it, just name them Img.
Q. But the same key is used over and over again. Will it be an issue if two product contains same value for some Img tag? I am not sure if there will be conflicts in such cases.
Whether there is a "conflict" or not depends of the wanted result. Your key holds all Img* items with the same value in a list. With this key you can generate an unique list of all Img* values.
If the key should be unique for Img* values within one Product you may add the generate-id() or the "Product/ID" to the key.
Something like this:
<xsl:key name="keyImg" match="Product/*[starts-with(local-name(), 'Img')]"
use="concat(../ID, '|', .)"/>
If the name of Img* tages is well known you may also try this:
<xsl:key name="keyImg" match="Img1 | Img2 | Img2 | Img4"
use="concat(../ID, '|', .)"/>
We're using Tibco BusinessWorks to pass an XML document to a Tibco BusinessEvents process. Because BusinessEvents does not have a DATE format, only DATETIME, we must change Dates in the source XML document before sending to BusinessEvents, and map the response's DateTime values back to simple Dates.
This is both annoying and cumbersome.
In an attempt to improve BusinessWorks' performance, I'm writing a stylesheet to handle the mapping. Here's what I've got.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:inf="http:/the.company.namespace">
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="inf:PriorExpirationDate | inf:OrderDate | inf:SegmentEffectiveDate |
inf:SegmentExpirationDate | inf:CancelDate | inf:NewBusinessEffectiveDate |
inf:NewBusinessExpirationDate | inf:RenewalEffectiveDate | inf:RenewalExpirationDate |
inf:QuestionDate | inf:ViolationDate | inf:ConvictionDate |
inf:EffectiveDate | inf:RatingDate | inf:AdvanceDate |
inf:SIDRevisionDate | inf:DriverLicensedDate |
inf:ESignatureDate | inf:UploadDate | inf:CancelDate |
inf:CancelProcessedDate | inf:CancelEffectiveDate | inf:CreatedDate |
inf:QuoteCreationDate | inf:QuoteModifiedDate | inf:QuoteExpirationDate |
inf:RateStartDate | inf:RateEndDate | inf:ChangeEffectiveDate | inf:PostDate |
inf:EffectiveDate | inf:ExpirationDate | inf:BirthDate |
inf:InstallmentDueDate | inf:CommercialDriverLicenseDate ">
<xsl:element name="{name()}">
<xsl:value-of
select="concat(format-date(text(),'[Y0001]-[M01]-[D01]'), 'T00:00:00')" />
</xsl:element>
</xsl:template>
While functional, this not ideal. I'd prefer NOT to have to enumerate each element I need transformed, I'd rather specify a TYPE that needs to be converted.
(1) Does XSL offer this functionality ?
(2) Alternatively, there are some element names that may be DATEs in one location and DATETIMEs in others. Is there an efficient way of excluding some DATETIME elements if I know the parent by name ?
(3) Lastly, does anyone see room for enhancement beyond the scope of the question ?
Some context: The original mapping is done inside BusinessWorks' editor, where the GUI generates its own mapping file, a several-hundred-line series of if/then/else statements. For a 50k document (our average) this amounts to nearly 20ms overhead per transformation for a web service that completes its actual work in fewer than 50ms. This is the bottleneck that must be improved upon.
Just use:
<xsl:template match="*[. castable as xs:date]">
<!-- Your code here -->
</xsl:template>
I have a function which returns phone numbers. It returns in this format
<td>#phone</td>
The function may return any number of values.
I want to display them in a grid like structure.
i.e a table which has say 10 columns in it. (So if the function returns 26 records, then I will have 3 rows: two rows with 10 columns and third row with 6 columns.)
I am unable to understand the counter logic in for statement in XQuery (for statement with keyword at). Any help would be appreciated.
The function call is like this (any modification to the function call is also highly appreciated):
The actual code is this:
declare function local:table-construct(
$areacode as xs:string,
$uniquekey as $xs:string,
$doc as xs:element) as xs:element?
{
for $phno in $doc/users[$areacode eq $code and $uniquekey eq $thiskey]
return <td>{$phno/phone}</td>
}
let $doc := <an xml doc from a database>
let $areacode := "somestring"
let $uniquekey := "somekey"
return
<html>
<body>
<table>
<tr>{local:table-construct($areacode, $uniquekey, $doc)}</tr>
</table>
</body>
</html>
The present format gives me all the phone numbers in the same row. I want the table to show only 10 rows. and the remaining data in the next rows.
Because you didn't provide any code, I can only guess you trapped into the "functional programming" trap. XQuery is a functional language without variables as known in imperative languages, better think of them as constants.
let $x := 1 to 10
let $sum := 0
for $i in $x
let $sum := $sum+$i (: here we cover $sum from line 2, do not change it :)
return $sum
The output of this code is 1 2 3 4 5 6 7 8 9 10 which might be unexpected. In line 4, we always add $i (1 to 10) and $sum (0), but we do not update $sum but cover it. For the next $i, $sum will be 0 again.
If this is the problem, think about using some pattern like this:
let $seq := 1 to 15
let $dividor := 4
for $i in 1 to ceiling(count($seq) div $dividor) cast as xs:integer
return <tr>{
for $td in subsequence($seq, ($i -1)*$dividor + 1, $dividor)
return <td>{$td}</td>
}</tr>
You might have to fit it to your code, but the idea should be fine.
EDIT: If your query processor supported it, you also could use a sliding window. As of version 5.0.2, Marklogic still doesn't.
mabe this can help, u can do the grouping as per your request inside the xslt!
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="Columns" select="10"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="phone[position() mod $Columns= 1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="phone">
<tr>
<xsl:apply-templates mode="copy" select=". | following-sibling::phone[position() < $Columns]"/>
</tr>
</xsl:template>
<xsl:template match="line" mode="copy">
<td><xsl:copy-of select="."/><td>
</xsl:template>
</xsl:stylesheet>
The at keyword of the FLWOR statement won't help you much here. You need to take 10 columns at a time and put that in its own <tr>. The most straight-forward way of doing this in MarkLogic is as follows:
declare function local:wrap-columns($columns, $width) {
let $nrrows := ceiling(count($columns) div $width)
for $row in 1 to $nrrows
let $start := ($row - 1) * $width + 1
let $end := $row * $width
return
<tr>{$columns[$start to $end]}</tr>
};
let $columns :=
for $i in 1 to 26
return <td>{$i}</td>
return
local:wrap-columns($columns, 10)
On second glance, it resembles the solution by Ranon (coincidence, honestly), though wrapped in a function to make integration and reuse easier.
A solution in XSLT would work too, but if you are not already using XSLT, then using XQuery makes more sense here I guess.
HTH!