0

I have a small lookup file, internal_docnbr_titles.xml:

<custom-lookup>
        <document number="121212">
            <title>Engine Manual</title>
            <aka>EM</aka>
        </document>
        <document number="333333">
            <title>Cleaning, Inspection, and Repair Manual</title>
            <aka>CIR</aka>
        </document>
        <document number="222222">
            <title>Engine Illustrated Parts Catalog</title>
            <aka>EIPC</aka>
        </document>
        <document number="444444">
            <title>Illustrated Tool and Equipment Manual</title>
            <aka>ITEM</aka>
        </document>
</custom-lookup>

I am accessing the title with this key and sequence:

<xsl:variable name="dictionary" select="doc('internal_docnbr_titles.xml')/custom-lookup" as="element()" />
<xsl:key name="referenced-by-docnbr" match="*" use="@number"/>
<xsl:sequence select="key('referenced-by-docnbr',$docnbr, $dictionary)/title" />

But $docnbr might have a suffix attached that I can ignore for lookup purposes, so I would like to change the key to use the contains() function. (@number should always be found in a $docnbr)

So how would I rewrite the key so it will work like this:

<xsl:sequence select="$dictionary/document[contains($docnbr,@number)]/title

I've tried variations of:

<xsl:key name="referenced-by-docnbr" match="*[contains(.,@number)]" use="@number"/>

But that doesn't return anything.

EDIT:

I already had this sequence in a function so I wrapped around Martin's function:

<xsl:function name="fn:get-manual-title" as="xs:string?">
            <xsl:param name="docnbr" as="xs:string?"/>              
           
            <xsl:sequence select="fn:lookup($docnbr)/title" /></xsl:function>

<xsl:function name="fn:lookup" as="element(document)*" cache="yes" new-each-time="no">
            <xsl:param name="docnbr" as="xs:string?"/>
            <xsl:sequence select="$dictionary/document[contains($docnbr,@number)]"/>
        </xsl:function>

EDIT:

I kept Martin's solution but the problem was the key didn't have any context. Change it to <xsl:key name="referenced-by-docnbr" match="*[matches(@number,'^'|| $gek,'i')]" use="@number"/> where $gek is a global variable, then it works.

Caroline
  • 167
  • 1
  • 11
  • 1
    Does the expression (without a key) `` select the right element(s)? While a key helps for an efficient lookup it of course builds a hash map based on a value or sequence of values associated with the node you match on. I don't see why you expect a key to work if you want to check whether your number attribute value is contained in another value, your existing expression should check that, but not based on a key. – Martin Honnen May 12 '23 at 17:46
  • Yes, `` works correctly without using the key. – Caroline May 12 '23 at 20:40
  • 1
    Are you experiencing performance problems or do you want to improve performance at least that you are asking for a key solution? I can't imagine one, will try to post a solution with a memory function that you can try to check whether it improves performance. – Martin Honnen May 12 '23 at 20:42
  • I realize this look-up is small, I wanted to understand for future reference. I saw another poster successfully used a contains function in an xsl:key but I couldn't make it work myself. I don't need to use the key, just trying to improve my understanding. – Caroline May 12 '23 at 20:57
  • 1
    Then let's wait whether someone else has an idea with an `xsl:key`. And see my answer which doesn't use an `xsl:key` but should build up a memoization of the function values and that way cache/remember function calls and speed up repeated lookup of the same value(s). – Martin Honnen May 12 '23 at 21:00

1 Answers1

1

You could try to use a "memory" function (declare some namespace e.g. xmlns:mf="http://example.com/mf")

<xsl:function name="mf:lookup" as="element(document)*" cache="yes" new-each-time="no">
  <xsl:param name="docnbr" as="xs:string"/>
  <xsl:sequence select="$dictionary/document[contains($docnbr,@number)]"/>
</xsl:function>

then instead of <xsl:sequence select="key('referenced-by-docnbr',$docnbr, $dictionary)/title" /> try e.g. <xsl:sequence select="mf:lookup($docnbr)/title" />

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • I switched to your solution – Caroline May 12 '23 at 21:27
  • If I leave `cache-yes` and `new-each-time=no` the function would miss new entries to the lookup table? (The lookup is not yet complete). Also, I am using Saxon HE and it looks like those two attributes require Saxon PE or EE. – Caroline May 15 '23 at 18:20
  • 1
    Recent versions of Saxon HE do have suppport for that, I think since 10, would need to check, but 11 and 12 do have it and are the currently recommended and supported versions, unless you live on .NET framework where Saxon HE 10 .NET is the last version Saxonica produced. So you should be covered if you use an actual version of Saxon on your platform, 10 or later. I don't quite understand what you are asking about in the first sentence. – Martin Honnen May 15 '23 at 18:43
  • Just that the lookup file is a work-in-progress and new entries will be added (misspoke when I said "lookup table") so I thought if I cache the function it wouldn't check for new entries in the lookup file. When would one use `new-each-time=maybe`? – Caroline May 15 '23 at 18:57
  • 1
    But `doc('internal_docnbr_titles.xml')` in any case does give you one certain document tree during each run of Saxon or any other XSLT 2/3 or XQuery processor. So while Saxon processes your XSLT in a certain run, you have a certain tree from the `doc` call, and that is what is worked with, be it a key, a "memo" function or normal XPath. I don't see how you expect "new entries" to show up in XSLT during a certain execution of an XSLT stylesheet. If your documents change in between runs then each run will work with the current document, and the "memo" function as well. – Martin Honnen May 15 '23 at 19:08
  • OK, I misunderstood, it would only change between runs. – Caroline May 16 '23 at 12:45