I'm trying to transform 1 xml file(file1.xml) to change it's tags and at the same time i want to replace value on 1 tag(category) with value from another XML file(replace.xml) by comparing if the #id of "file1.xml" is equal to #id from "replace.xml". If true then replace value of that TAG(category) in file1.xml with value from "replace.xml", but i dont know how to do it in 1 XSLT file so i am trying to make 2 files which will transform:
File1.xml
<?xml version="1.0"?>
<products>
<product>
<id_item>1002333</id_item>
<producer>XYZ</producer>
<price>
<delivery_cost>
<prepayment>000</prepayment>
<cash_on_delivery>000</cash_on_delivery>
</delivery_cost>
</price>
<category id="456" name="Gloves"></category>
</product>
<product>
<id_item>1002000</id_item>
<producer>XYZ</producer>
<price>
<delivery_cost>
<prepayment></prepayment>
<cash_on_delivery></cash_on_delivery>
</delivery_cost>
</price>
<category id="123" name="Shoes"></category>
</product>
</products>
i transformed this Example above with my first XSL(i cant include that file here because it is too big and i dont know how to make an example of it)
Output of the first transformation:
<?xml version="1.0"?>
<channel>
<PRODUCT>
<ID_ITEM>1002333</ID_ITEM>
<PRODUCER>XYZ</PRODUCER>
<PRICE_VAT>000</PRICE_VAT>
<CATEGORY id="456">Gloves</CATEGORY >
</PRODUCT>
<PRODUCT>
<ID_ITEM>1002000</ID_ITEM>
<PRODUCER>XYZ</PRODUCER>
<PRICE_VAT>000</PRICE_VAT>
<CATEGORY id="123">Shoes</CATEGORY >
</PRODUCT>
</channel>
and now i want to use this XML file (replace.xml) to change the text value of tag
CATEGORY of Output above with value from (replace.xml)
Replace.xml
<?xml version="1.0"?>
<ITEM>
<category id="456">ReplacedGloves</category>
<category id="123">ReplacedShoes</category>
<category id="321">REplaced1</category>
<category id="432">REplaced2</category>
</ITEM>
with 2nd XSL file
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:import href="firstXSLhere.xsl"/>
<xsl:variable name="plik_mapping" select="document('maptest.xml')"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="category">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:choose>
<xsl:when test="$plik_mapping//category[#id=current()/#id]">
<xsl:apply-templates select="$plik_mapping//category[#id=current()/#id]/#name"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="#name"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and final output
<?xml version="1.0"?>
<channel>
<PRODUCT>
<ID_ITEM>1002333</ID_ITEM>
<PRODUCER>XYZ</PRODUCER>
<PRICE_VAT>000</PRICE_VAT>
<CATEGORY id="456">ReplacedGloves</CATEGORY >
</PRODUCT>
<PRODUCT>
<ID_ITEM>1002000</ID_ITEM>
<PRODUCER>XYZ</PRODUCER>
<PRICE_VAT>000</PRICE_VAT>
<category id="123">ReplacedShoes</CATEGORY >
</PRODUCT>
</channel>
what i'm trying to do here is importing the first XSL file to the 2nd XSL file
and transform File1.xml to it's final output. But im not sure what im doing wrong here. Since the File1.xml only transform with the first XSL file templates. I'm not sure if i did the order of import precedence in wrong way.
As I told you in the previous version of this question, you can probably do the entire transformation using a single stylesheet.
Since you did not post the 1st XSLT, it id difficult to say exactly how this should be done. Here is a simplified example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:param name="plik_mapping" select="document('maptest.xml')"/>
<xsl:template match="/products">
<channel>
<xsl:for-each select="product">
<PRODUCT>
<ID_ITEM>
<xsl:value-of select="id_item" />
</ID_ITEM>
<PRODUCER>
<xsl:value-of select="producer" />
</PRODUCER>
<PRICE_VAT>
<!-- ??? -->
</PRICE_VAT>
<xsl:variable name="cat" select="category" />
<xsl:variable name="replacement" select="$plik_mapping/ITEM/category[#id=$cat/#id]" />
<CATEGORY id="{$cat/#id}">
<xsl:choose>
<xsl:when test="$replacement">
<xsl:value-of select="$replacement" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$cat/#name" />
</xsl:otherwise>
</xsl:choose>
</CATEGORY>
</PRODUCT>
</xsl:for-each>
</channel>
</xsl:template>
</xsl:stylesheet>
Applied to your input XML example, this will produce:
Result
<?xml version="1.0"?>
<channel>
<PRODUCT>
<ID_ITEM>1002333</ID_ITEM>
<PRODUCER>XYZ</PRODUCER>
<PRICE_VAT/>
<CATEGORY id="456">ReplacedGloves</CATEGORY>
</PRODUCT>
<PRODUCT>
<ID_ITEM>1002000</ID_ITEM>
<PRODUCER>XYZ</PRODUCER>
<PRICE_VAT/>
<CATEGORY id="123">ReplacedShoes</CATEGORY>
</PRODUCT>
</channel>
which is similar to the output you show, except for the value of PRICE_VAT for which you gave us no information regarding how it should be derived (and of course the category vs. CATEGORY mismatch).
I am facing an xslt/xpath problem and hope someone could help, in a few words here is what I try to achieve.
I have to transform an XML document where some nodes may be missing, these missing nodes are mandatory in the final result. I have the set of mandatory node names available in an xsl:param.
The base document is:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="TRANSFORM.xslt"?>
<BEGIN>
<CLIENT>
<NUMBER>0021732561</NUMBER>
<NAME1>John</NAME1>
<NAME2>Connor</NAME2>
</CLIENT>
<PRODUCTS>
<PRODUCT_ID>12</PRODUCT_ID>
<DESCRIPTION>blah blah</DESCRIPTION>
</PRODUCTS>
<PRODUCTS>
<PRODUCT_ID>13</PRODUCT_ID>
<DESCRIPTION>description ...</DESCRIPTION>
</PRODUCTS>
<OPTIONS>
<OPTION_ID>1</OPTION_ID>
<DESCRIPTION>blah blah blah ...</DESCRIPTION>
</OPTIONS>
<PROMOTIONS>
<PROMOTION_ID>1</PROMOTION_ID>
<DESCRIPTION>blah blah blah ...</DESCRIPTION>
</PROMOTIONS>
</BEGIN>
Here is the stylesheet so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:param name="mandatoryNodes" as="xs:string*" select=" 'PRODUCTS', 'OPTIONS', 'PROMOTIONS' "/>
<xsl:template match="/">
<xsl:apply-templates select="child::node()"/>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="BEGIN">
<xsl:element name="BEGIN">
<xsl:for-each select="$mandatoryNodes">
<!-- If there is no node with this name -->
<xsl:if test="count(*[name() = 'current()']) = 0">
<xsl:element name="{current()}" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="child::node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I tried the transformation in XML Spy, the xsl:iftest failed saying that 'current item is PRODUCTS of type xs:string.
I've tried the same xsl:if outside of a for-each and it seems to work ... what am I missing ?
Inside of <xsl:for-each select="$mandatoryNodes"> the context item is a string but you want to access the primary input document and its nodes so you need to store that document or the template's context node in a variable and use that e.g.
<xsl:template match="BEGIN">
<xsl:variable name="this" select="."/>
<xsl:element name="BEGIN">
<xsl:for-each select="$mandatoryNodes">
<!-- If there is no child node of `BEGIN` with this name -->
<xsl:if test="count($this/*[name() = current()]) = 0">
<xsl:element name="{current()}" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="child::node()"/>
</xsl:element>
</xsl:template>
I am trying to write an XSLT code which splits the text data having multiple lines and produces an XML which contains group of multiple fixed number of lines from the text data.
For example, If my input XML is like this
<?xml version="1.0" encoding="UTF-8"?>
<csv>
<data>Id,Name,Address,Location,Extid,contact
1,raagu1,hosakote1,bangalore1,123,contact1
2,raagu2,hosakote2,bangalore2,123,contact2
3,raagu3,hosakote3,bangalore3,123,contact3
4,raag4,hosakote4,bangalore4,123,contact4
5,raagu5,hosakote5,bangalore5,123,contact5
6,raagu6,hosakote6,bangalore6,123,contact6
7,raagu7,hosakote7,bangalore7,123,contact7
</data>
</csv>
where the text data under element data tells, the first line (Id,Name,Address,Location,Extid,contact) is header and rest of the lines are data corresponding to the header fields.
When I say fixed length for lines is 4 i,e. group of 4 data sets,
then my output XML should be like this.
<?xml version="1.0" encoding="UTF-8"?>
<csv>
<data>
Id,Name,Address,Location,Extid,contact
1,raagu1,hosakote1,bangalore1,123,contact1
2,raagu2,hosakote2,bangalore2,123,contact2
3,raagu3,hosakote3,bangalore3,123,contact3
4,raag4,hosakote4,bangalore4,123,contact4
</data>
<data>
Id,Name,Address,Location,Extid,contact
5,raagu5,hosakote5,bangalore5,123,contact5
6,raagu6,hosakote6,bangalore6,123,contact6
7,raagu7,hosakote7,bangalore7,123,contact6
</data>
</csv>
To achieve this I have explored on xslt scripts and tried following XSLT
<xsl:stylesheet version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" encoding="UTF-8"/>
<xsl:template match = "/csv/data">
<xsl:variable name="header" select="substring-before(.,'
')"/>
<xsl:variable name="data" select="substring-after(.,'
')"/>
<csv>
<xsl:for-each select = "tokenize($data, '\n')">
<xsl:variable name="count" select="position()"/>
<data>
<xsl:value-of select="$header"/>
<xsl:text>
</xsl:text>
<xsl:sequence select = "."/>
</data>
</xsl:for-each>
</csv>
</xsl:template>
</xsl:stylesheet>
With this, the output I got was
<?xml version="1.0" encoding="UTF-8"?>
<csv>
<data>
Id,Name,Address,Location,Extid,contact
1,raagu1,hosakote1,bangalore1,123,contact1
</data>
<data>
Id,Name,Address,Location,Extid,contact
2,raagu2,hosakote2,bangalore2,123,contact2
</data>
<data>
Id,Name,Address,Location,Extid,contact
3,raagu3,hosakote3,bangalore3,123,contact3
</data>
<data>
Id,Name,Address,Location,Extid,contact
4,raag4,hosakote4,bangalore4,123,contact4
</data>
<data>
Id,Name,Address,Location,Extid,contact
5,raagu5,hosakote5,bangalore5,123,contact5
</data>
<data>
Id,Name,Address,Location,Extid,contact
6,raagu6,hosakote6,bangalore6,123,contact6
</data>
<data>
Id,Name,Address,Location,Extid,contact
7,raagu7,hosakote7,bangalore7,123,contact7
</data>
</csv>
I could not quite get it right since for every line it is grouping. I think I missing some thing to do with concatenation. I am looking for some help to see whether are they any functions in xslt using which we can split the text into multiple groups lines and create a single element for each of those group with very good performance? I am ok for xslt 2.0 functions. The code should work even for 1,00,000+ data sets.
Thanks
Raagu
Do you really want to create that XML result format that continues to have comma separated data and line separated data? I would consider to clean up the data and mark it up properly with XML.
But as for the grouping, here is an example:
<xsl:stylesheet version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:param name="chunk-size" select="4" as="xs:integer"/>
<xsl:output indent="yes" method="xml" encoding="UTF-8"/>
<xsl:template match = "/csv/data">
<xsl:variable name="header" select="substring-before(.,'
')"/>
<xsl:variable name="data" select="substring-after(.,'
')"/>
<csv>
<xsl:for-each-group select = "tokenize($data, '\n')" group-adjacent="(position() - 1) idiv $chunk-size">
<data>
<xsl:value-of select="$header"/>
<xsl:text>
</xsl:text>
<xsl:value-of select = "current-group()" separator="
"/>
</data>
</xsl:for-each-group>
</csv>
</xsl:template>
</xsl:stylesheet>
This is a basic solution (not group-adjacent) with manual element creation - not very beautiful, but works and is comprehensive.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" encoding="UTF-8"/>
<xsl:template match="/csv/data">
<xsl:variable name="header" select="substring-before(.,'
')"/>
<xsl:variable name="data" select="substring-after(.,'
')"/>
<xsl:variable name="numberOfRows" select="4"/>
<csv>
<xsl:for-each select="tokenize($data, '\n')">
<xsl:variable name="count" select="position()-1"/>
<xsl:variable name="modulo" select="$count mod $numberOfRows"/>
<xsl:if test="$modulo = 0">
<xsl:text disable-output-escaping="yes"><data></xsl:text>
<xsl:value-of select="$header"/>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:sequence select="."/>
<xsl:text>
</xsl:text>
<xsl:if test="$modulo = ($numberOfRows - 1)">
<xsl:text disable-output-escaping="yes"></data></xsl:text>
</xsl:if>
</xsl:for-each>
</csv>
</xsl:template>
</xsl:stylesheet>
I have the following xml
<TopLevel>
<data m="R263">
<s ut="263firstrecord" lt="2013-02-16T09:21:40.393" />
<s ut="263secondrecord" lt="2013-02-16T09:21:40.393" />
</data>
<data m="R262">
<s ut="262firstrecord" lt="2013-02-16T09:21:40.393" />
<s ut="262secondrecord" lt="2013-02-16T09:21:40.393" />
</data>
</TopLevel>
I have some XSLT that does the call template but it's not itterating correctly.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="data">
<xsl:value-of select="#m" />
<xsl:variable name="vYourName" select="#m"/>
<xsl:choose>
<xsl:when test="#m='R262'">
<xsl:call-template name="R262"/>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="#m='R263'">
<xsl:call-template name="R263"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="R262">
<xsl:for-each select="/TopLevel/data/s">
Column1=<xsl:value-of select="#ut" />
Column2=<xsl:value-of select="#lt" />
</xsl:for-each>
</xsl:template>
<xsl:template name="R263">
<xsl:for-each select="/TopLevel/data/s">
Column1=<xsl:value-of select="#ut" />
Column2=<xsl:value-of select="#lt" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This gives me 8 records insead of the 4 (<s> level) records. I know it has to do with my iteration ... but I am not sure how to address this.
I am also aware of the apply stylesheets but I couldn't unravel that mystery either ... If someone can help me with XSLT that will only process everything from <TopLevel> to <\TopLevel> checking the value of m at the <data> level and applying the stylesheet at the <s> level for each <s> record I will be greateful beyond belief.
I don't know what output you want to produce, but I suspect you want to replace
<xsl:for-each select="/TopLevel/data/s">
by
<xsl:for-each select="s">
that is, you only want to process the "s" elements within the "data" you are currently processing, rather than selecting all the "s" elements in the whole document.
Why not do this using apply-templates?
<xsl:template match="data">
...
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="s[../#m='R262']">
...
</xsl:template>
<xsl:template match="s[../#m='R263']">
...
</xsl:template>
If you want to use match template and apply-templates you could do the following which gives you also a text output just like your stylesheet does. So this XSLT applied to your original source XML:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="data">
<xsl:value-of select="#m"/>
<xsl:apply-templates select="s"/>
</xsl:template>
<xsl:template match="s">
Column1=<xsl:value-of select="#ut"/>
Column2=<xsl:value-of select="#lt"/>
</xsl:template>
</xsl:stylesheet>
gives you this output:
<?xml version="1.0" encoding="UTF-8"?>
R263
Column1=263firstrecord
Column2=2013-02-16T09:21:40.393
Column1=263secondrecord
Column2=2013-02-16T09:21:40.393
R262
Column1=262firstrecord
Column2=2013-02-16T09:21:40.393
Column1=262secondrecord
Column2=2013-02-16T09:21:40.393
You basically only match on the s and give out the attributes "ut" and "lt". You can also output XML which would look better.
Using this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="data">
<list>
<xsl:apply-templates select="s"/>
</list>
</xsl:template>
<xsl:template match="s">
<xsl:element name="record">
<xsl:attribute name="m">
<xsl:value-of select="parent::data/#m"/>
</xsl:attribute>
<item>Column1=<xsl:value-of select="#ut"/></item>
<item>Column2=<xsl:value-of select="#lt"/></item>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
will give you this nice XML output:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<list>
<record m="R263">
<item>Column1=263firstrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
<record m="R263">
<item>Column1=263secondrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
</list>
<list>
<record m="R262">
<item>Column1=262firstrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
<record m="R262">
<item>Column1=262secondrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
</list>
You have to adapt the original XSLT a little bit to get a nice XML structure. Also when matching s you "climb" up to element data to get the R-numbers for your attribute values.
The template matching root you need for a proper XML root element. <list> you could also get rid off then you have <record> as child of <root>.
I'm transforming an XML document with PHP/XSL. I'm searching for a keyword value. I want to add paging, so I'm not returning all the search results. I can do this with separate xsl files, but I'd like to join them if I can. How can I return the search results and then apply the paging? E.g.
Paging
...
<xsl:if test="position() > $start and position() < $end">
...
Search.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<items>
<xsl:attribute name="count"><xsl:value-of select="count(//item)"/></xsl:attribute>
<xsl:apply-templates select="//item">
<xsl:sort select="*[name()=$sortBy]" order="{$order}" data-type="{$type}" />
</xsl:apply-templates>
</items>
</xsl:template>
<xsl:template match="//item">
<xsl:choose>
<xsl:when test="contains(
translate(title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz'), $keyword)
or contains(translate(content, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz'), $keyword)">
<item>
<title><xsl:value-of select="title"/></title>
<content><xsl:value-of select="content"/></content>
<date><xsl:value-of select="date"/></date>
<author><xsl:value-of select="author"/></author>
<uri><xsl:value-of select="uri"/></uri>
<division><xsl:value-of select="division"/></division>
</item>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Final Solution using xsl-variable and node-set()
Need to do some more checks, but i'm pretty sure this works ok.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="searchResults">
<xsl:apply-templates select="//item">
<xsl:sort select="*[name()=$sortBy]" order="{$order}" data-type="{$type}" />
</xsl:apply-templates>
</xsl:variable>
<xsl:template match="//item">
<xsl:choose>
<xsl:when test="contains(translate(title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), $keyword) or contains(translate(content, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), $keyword)">
<item>
<title><xsl:value-of select="title"/></title>
<content><xsl:value-of select="content"/></content>
<date><xsl:value-of select="date"/></date>
<author><xsl:value-of select="author"/></author>
<uri><xsl:value-of select="uri"/></uri>
<division><xsl:value-of select="division"/></division>
</item>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="//item" mode="paging">
<xsl:choose>
<xsl:when test="position() > $start and position() < $end">
<item>
<title><xsl:value-of select="title"/></title>
<content><xsl:value-of select="content"/></content>
<date><xsl:value-of select="date"/></date>
<author><xsl:value-of select="author"/></author>
<uri><xsl:value-of select="uri"/></uri>
<division><xsl:value-of select="division"/></division>
</item>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="/*">
<items>
<xsl:attribute name="count"><xsl:value-of select="count(//item)"/></xsl:attribute>
<xsl:apply-templates select="exslt:node-set($searchResults)/*" mode="paging" />
</items>
</xsl:template>
Read about modes in XSLT.
Then use in these two cases:
<xsl:apply-templates mode="search" select="someExpression">
<!-- <xsl:with-param> children if necessary -->
<!-- <xsl:sort> children if necessary -->
</xsl:apply-templates>
and also:
<xsl:apply-templates mode="paging" select="someExpression">
<!-- <xsl:with-param> children if necessary -->
<!-- <xsl:sort> children if necessary -->
</xsl:apply-templates>
Of course, you must have tempaltes in each of the above modes.
I think this is no ways to apply different templates consequentially in single transformation. So, try to add paging xpath to search xpath:
<xsl:when test="contains(
translate(title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz'), $keyword)
or contains(translate(content, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz'), $keyword)
and
position() > $start and position() < $end">
This feels to me like a case where you should split the transformation into two phases, one to do searching and one to do paging.
You can either writing a pipeline that executes two transformations written as separate stylesheets, or (with a bit of help from exslt:node-set()) you can do it in a single stylesheet saving the results in a temporary variable. I'd recommend two stylesheets - it makes the code more readable and reusable.