1
votes

At the moment in Xquery 3.1 (in eXist 4.7) I receive XML fragments that look like the following (from eXist's Lucene full text search):

let $text :=

 <tei:text>
   <front>
     <tei:div>
        <tei:listBibl>
            <tei:bibl>There is some</tei:bibl>
            <tei:bibl>text in certain elements</tei:bibl>
        </tei:listBibl>
     </tei:div>
     <tei:div>
        <tei:listBibl>
            <tei:bibl>which are subject <exist:match>to</exist:match> a Lucene search</tei:bibl>
            <tei:bibl></tei:bibl>
        <tei:listBibl>
     </tei:div>
   <tei:front>
   <tei:body>
     <tei:p>and often produces</tei:p>
     <tei:p>a hit.</tei:p>
   <tei:body>
 <tei:text> 

Currently I have Xquery send this fragment to an XSLT stylesheet in order to transform it into HTML like this:

<td>...elements which are subject <span class="search-hit">to</span> a Lucene search and often p...

Where the stylesheet's job is to return 30 characters of text before and after <exist:match/> and put the content of <exist:match/> into a span. There is only one <exist:match/> per transformation.

This all works fine. However, it's occurred to me that it is a very small job with effectively a single transformation of only one element, the rest being a sort of string-join. I therefore wonder if this can't be done efficiently in Xquery.

In trying to do this, I'm can't seem to find a way to handle the string content up to the <exist:match/> and then the string content after <exist:match/>. My idea is, in pseudo code, to output a result like:

let $textbefore := some function to get the text before <exist:match/>
let $textafter := some function to get text before <exist:match/>
return <td>...{$textbefore} 
        <span class="search-hit">
          {$text//exist:match/text()}
        </span> {$textafter}...</td>

Is this even worth doing in Xquery vs the current Xquery -> XSLT pipeline I have?

Many thanks.

1
Can you show us the XSLT you have? - Martin Honnen

1 Answers

2
votes

I think it can be done as

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";

declare namespace tei = "http://example.com/tei";
declare namespace exist = "http://example.com/exist";

declare option output:method 'html';

let $text :=

 <tei:text>
   <tei:front>
     <tei:div>
        <tei:listBibl>
            <tei:bibl>There is some</tei:bibl>
            <tei:bibl>text in certain elements</tei:bibl>
        </tei:listBibl>
     </tei:div>
     <tei:div>
        <tei:listBibl>
            <tei:bibl>which are subject <exist:match>to</exist:match> a Lucene search</tei:bibl>
            <tei:bibl></tei:bibl>
        </tei:listBibl>
     </tei:div>
   </tei:front>
   <tei:body>
     <tei:p>and often produces</tei:p>
     <tei:p>a hit.</tei:p>
   </tei:body>
 </tei:text> 
 , 
 $match := $text//exist:match,
 $text-before-all := normalize-space(string-join($match/preceding::text(), ' ')),
 $text-before := substring($text-before-all, string-length($text-before-all) - 30),
 $text-after := substring(normalize-space(string-join($match/following::text(), ' ')), 1, 30)
return 
 <td>...{$text-before} 
        <span class="search-hit">
          {$match/text()}
        </span> {$text-after}...</td>

which is not really much of a query in XQuery either but just some XPath selection plus some possibly expensive string joining and extraction on the preceding and following axis.