m
xquery version "1.0"; (:~ ================================================ Implements the documentation search. ================================================ :) import module namespace xdb="http://exist-db.org/xquery/xmldb"; import module namespace kwic="http://exist-db.org/xquery/kwic"; import module namespace setup="http://exist-db.org/xquery/docs/setup" at "docsetup.xql"; declare namespace dq="http://exist-db.org/xquery/documentation"; declare option exist:serialize "method=html media-type=text/html expand-xincludes=yes"; declare variable $dq:COLLECTION := "xqdocs"; declare variable $dq:FIELDS := <fields> <field name="title">section[ft:query(.//title, '$q')]</field> <field>section[ft:query(., '$q')]</field> <!--field>chapter[ft:query(title, '$q')]</field--> </fields>; declare variable $dq:CHARS_SUMMARY := 120; declare variable $dq:CHARS_KWIC := 60; (:~ Display the hits: this function iterates through all hits and calls kwic:summarize to print out a summary of each match. :) declare function dq:print($hit as element(), $docXPath as xs:string, $mode as xs:string) as element()* { let $nodeId := util:node-id($hit) let $uri := concat( "../", util:document-name(root($hit)), "?q=", (: "docs.xql?path=", document-uri(root($hit)), "&q=", :) escape-uri($docXPath, true()), "&id=", $nodeId, "#", $nodeId ) let $config := <config xmlns="" width="{if ($mode eq 'summary') then $dq:CHARS_SUMMARY else $dq:CHARS_KWIC}" table="{if ($mode eq 'summary') then 'no' else 'yes'}" link="{$uri}"/> let $matches := kwic:get-matches($hit) for $ancestor in ($matches/ancestor::para | $matches/ancestor::title | $matches/ancestor::td | $matches/ancestor::note) return kwic:get-summary($ancestor, ($ancestor//exist:match)[1], $config) }; (:~ Print the hierarchical context of a hit. :) declare function dq:print-headings($section as element()*, $docXPath as xs:string) { $section/ancestor-or-self::chapter/title//text(), for $s at $p in $section/ancestor-or-self::section let $nodeId := util:node-id($s) let $uri := concat("../", util:document-name(root($s)), "?q=", escape-uri($docXPath, true()), "&id=", $nodeId, "#", $nodeId ) return (" > ", <a href="{$uri}">{$s/title//text()}</a>) }; (:~ Display the query results. :) declare function dq:print-results($hits as element()*, $docXPath as xs:string) { let $mode := request:get-parameter("view", "summary") return <div id="f-results"> <p id="f-results-heading">Found: {count($hits)}.</p> { if ($mode eq 'summary') then for $section in $hits let $score := ft:score($section) order by $score descending return <div class="section"> <span class="score">Score: {round-half-to-even($score, 2)}</span> <div class="headings">{ dq:print-headings($section, $docXPath) }</div> { dq:print($section, $docXPath, $mode) } </div> else <table class="kwic"> { for $section in $hits order by ft:score($section) descending return ( <tr> <td class="headings" colspan="3"> {dq:print-headings($section, $docXPath)} </td> </tr>, dq:print($section, $docXPath, $mode) ) } </table> } </div> }; (:~ Process the query. :) declare function dq:query() { let $query := request:get-parameter("q", ()) let $field := request:get-parameter("field", "all") return if ($query) then let $fields := if ($field ne "all") then $dq:FIELDS/field[@name = $field] else $dq:FIELDS/field let $queryParts := for $f in $fields return replace($f, "\$q", $query) let $xpath := string-join( for $p in $queryParts return concat("collection('/db/", $dq:COLLECTION, "')//", $p), " | " ) let $log := util:log("DEBUG", ("Query: ", $xpath)) let $docXPath := string-join(for $p in $queryParts return concat(".//", $p), " or ") let $hits := util:eval($xpath) return dq:print-results($hits, $docXPath) else () }; (:~ Return the main XML page, which will be transformed into HTML by Cocoon. If Javascript is enabled on the client, this function will only be called once. All subsequent calls to this script will be made via AJAX and we don't need to return the entire page. :) declare function dq:get-page($action as xs:string?, $askPass as xs:boolean) as element() { <book> <bookinfo> <graphic fileref="logo.jpg"/> <productname>Open Source Native XML Database</productname> <title>Documentation Search</title> <link rel="stylesheet" type="text/css" href="styles/docsearch.css"/> <script type="text/javascript" src="scripts/yui/utilities2.7.0.js"/> <source>search.xql/source</source> </bookinfo> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="sidebar.xml"/> <chapter> <title>Search the Documentation</title> { if ($askPass) then <form id="f-pass" name="f-pass" action="search.xql" method="POST"> <para>The documentation needs to be loaded into the first, which requires administrator rights. Please enter the password for the admin user below:</para> <input type="password" name="pass" value=""/> <input type="hidden" name="generate" value="true"/> <button type="submit">Generate</button> </form> else ( <div id="f-search"> <form name="f-query" action="search.xql" method="GET"> <table> <tr> <td colspan="2"> <label for="q">Search:</label> <input name="q" type="text" value="{request:get-parameter('q', '')}"/> <label for="field">in</label> <select name="field"> <option value="all">All</option> <option value="title">Headings Only</option> </select> </td> <td> <label for="view">View:</label> <select name="view"> <option value="summary">Summary</option> <option value="kwic">One Line</option> </select> </td> <td class="f-btn"> <input id="f-btn-search" type="submit" name="action" value="Search"/> </td> </tr> </table> <input type="hidden" name="prev" value="{$action}"/> </form> <p class="f-info">(<b>eXist version: {util:system-property("product-version")}, build: {util:system-property("product-build")}</b>). </p> <div id="f-result"> { dq:query() } </div> </div> ) } </chapter> </book> }; let $askPass := if (not(xdb:collection-available($dq:COLLECTION))) then let $adminPass := request:get-parameter("pass", ()) let $generate := request:get-parameter("generate", ()) return if ($generate) then let $dummy := setup:setup($adminPass) return false() else true() else false() return dq:get-page((), $askPass)