/search.xql
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";
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
<para>The function documentation needs to be generated one
time at least. Please change to the
<a href="{request:get-context-path()}/admin/admin.xql?panel=fundocs">documentation
setup page</a>
in the admin web application.</para>
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 :=
not(xdb:collection-available($dq:COLLECTION))
return
dq:get-page((), $askPass)