I'm coding on a project to digitize historical texts using the XML/TEI language. The texts are published on the tei-publisher plattform. There are two types of search on the platform: faceted search and general search. When searching by facet, the results are presented by article, in alphabetical order. However, when you general search , the results appear in a random order, as shown in the image. I would like the results to appear in alphabetical order and if there are more answers per text, then they should be anized by div
Initially, I was changing this line in the search.xql
file, but I noticed that any change I make to this line, the search results remain unchanged.
let $hitsAll :=
(:If the $query-scope is narrow, query the elements immediately below the lowest div in tei:text and the four major element below tei:teiHeader.:)
for $hit in query:query-default($request?parameters?field, $request?parameters?query, $request?parameters?doc,())
order by xs:string($hit/title) ascending [I changed this line. I added this command]
return $hit
Perhaps I need to make a change to some other file responsible for searches, such as query.xql
, navigation.xql
or query-tei.xql
, but I don't know how to make this change. Any help would be appreciated.
Below are the files I'm using, as well as the structure of the xml file. Thank you very much.
search.xql
xquery version "3.1";
module namespace sapi=";;
import module namespace query="; at "../../query.xql";
import module namespace nav="; at "../../navigation.xql";
import module namespace config="; at "../../config.xqm";
import module namespace tpu="; at "../util.xql";
import module namespace kwic="; at "resource:/exist/xquery/lib/kwic.xql";
import module namespace facets="; at "../../facets.xql";
declare namespace tei=".0";
declare function sapi:autocomplete($request as map(*)) {
let $q := request:get-parameter("query", ())
let $type := request:get-parameter("field", "text")
let $doc := request:get-parameter("doc", ())
let $items :=
if ($q) then
query:autocomplete($doc, $type, $q)
else
()
return
array {
for $item in $items
group by $item
return
map {
"text": $item,
"value": $item
}
}
};
declare function sapi:search($request as map(*)) {
(:If there is no query string, fill up the map with existing values:)
if (empty($request?parameters?query))
then
sapi:show-hits($request, session:get-attribute($config:session-prefix || ".hits"), session:get-attribute($config:session-prefix || ".docs"))
else
(:Otherwise, perform the query.:)
(: Here the actual query commences. This is split into two parts, the first for a Lucene query and the second for an ngram query. :)
(:The query passed to a Luecene query in ft:query is an XML element <query> containing one or two <bool>. The <bool> contain the original query and the transliterated query, as indicated by the user in $query-scripts.:)
let $hitsAll :=
(:If the $query-scope is narrow, query the elements immediately below the lowest div in tei:text and the four major element below tei:teiHeader.:)
for $hit in query:query-default($request?parameters?field, $request?parameters?query, $request?parameters?doc,()) (: aqui que é o anizador :)
order by xs:string($hit/title) ascending
return $hit
let $hitCount := count($hitsAll)
let $hits := if ($hitCount > 1000) then subsequence($hitsAll, 1, 1000) else $hitsAll
(:Store the result in the session.:)
let $store := (
session:set-attribute($config:session-prefix || ".hits", $hitsAll),
session:set-attribute($config:session-prefix || ".hitCount", $hitCount),
session:set-attribute($config:session-prefix || ".search", $request?parameters?query),
session:set-attribute($config:session-prefix || ".field", $request?parameters?field),
session:set-attribute($config:session-prefix || ".docs", $request?parameters?doc)
)
return
sapi:show-hits($request, $hits, $request?parameters?doc )
};
declare %private function sapi:show-hits($request as map(*), $hits as item()*, $docs as xs:string*) {
response:set-header("pb-total", xs:string(count($hits))),
response:set-header("pb-start", xs:string($request?parameters?start)),
for $hit at $p in subsequence($hits, $request?parameters?start, $request?parameters?per-page)
let $config := tpu:parse-pi(root($hit), $request?parameters?view)
let $parent := query:get-parent-section($config, $hit)
let $parent-id := config:get-identifier($parent)
let $parent-id := if (exists($docs)) then replace($parent-id, "^.*?([^/]*)$", "$1") else $parent-id
let $div := query:get-current($config, $parent)
let $expanded := util:expand($hit, "add-exist-id=all")
let $docId := config:get-identifier($div)
return
<paper-card>
<header>
<div class="count">{$request?parameters?start + $p - 1}</div>
{ query:get-breadcrumbs($config, $hit, $parent-id) }
</header>
<div class="matches">
{
for $match in subsequence($expanded//exist:match, 1, 5)
let $matchId := $match/../@exist:id
let $docLink :=
if ($config?view = "page") then
(: first check if there's a pb in the expanded section before the match :)
let $pbBefore := $match/preceding::tei:pb[1]
return
if ($pbBefore) then
$pbBefore/@exist:id
else
(: no: locate the element containing the match in the source document :)
let $contextNode := util:node-by-id($hit, $matchId)
(: and get the pb preceding it :)
let $page := $contextNode/preceding::tei:pb[1]
return
if ($page) then
util:node-id($page)
else
util:node-id($div)
else
(: Check if the document has sections, otherwise don't pass root :)
if (nav:get-section-for-node($config, $div)) then util:node-id($div) else ()
let $config := <config width="60" table="no" link="{$docId}?{if ($docLink) then 'root=' || $docLink || '&' else ()}action=search&view={$config?view}&odd={$config?odd}#{$matchId}"/>
return
kwic:get-summary($expanded, $match, $config)
}
</div>
</paper-card>
};
declare function sapi:facets($request as map(*)) {
let $hits := session:get-attribute($config:session-prefix || ".hits")
where count($hits) > 0
return
<div>
{
for $config in $config:facets?*
return
facets:display($config, $hits)
}
</div>
};
declare function sapi:list-facets($request as map(*)) {
let $type := $request?parameters?type
let $lang := tokenize($request?parameters?language, '-')[1]
let $facetConfig := filter($config:facets?*, function($facetCfg) {
$facetCfg?dimension = $type
})
let $hits := session:get-attribute($config:session-prefix || ".hits")
let $facets := ft:facets($hits, $type, ())
let $matches :=
for $key in if (exists($request?parameters?value)) then $request?parameters?value else map:keys($facets)
let $label := facets:translate($facetConfig, $lang, $key)
return
map {
"text": $label,
"freq": $facets($key),
"value": $key
}
let $filtered := filter($matches, function($item) {
matches($item?text, '(?:^|\W)' || $request?parameters?query, 'i')
})
return
array { $filtered }
};
query.xql
(:
: Copyright (C) 2017 Wolfgang Meier
:
: This program is free software: you can redistribute it and/or modify
: it under the terms of the GNU General Public License as published by
: the Free Software Foundation, either version 3 of the License, or
: (at your option) any later version.
:
: This program is distributed in the hope that it will be useful,
: but WITHOUT ANY WARRANTY; without even the implied warranty of
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
: GNU General Public License for more details.
:
: You should have received a copy of the GNU General Public License
: along with this program. If not, see </>.
:)
xquery version "3.1";
module namespace query=";;
import module namespace tei-query="; at "query-tei.xql";
import module namespace docbook-query="; at "query-db.xql";
import module namespace jats-query="; at "query-jats.xql";
import module namespace config="; at "config.xqm";
import module namespace nav="; at "navigation.xql";
declare namespace tei=".0";
declare variable $query:QUERY_OPTIONS := map {
"leading-wildcard": "yes",
"filter-rewrite": "yes"
};
declare function query:field-prefix($elem as element()) {
switch (namespace-uri($elem))
case ".0" return
""
case "; return
"dbk."
default return
"jats."
};
declare %private function query:sort($items as element()*, $sortBy as xs:string?) {
let $items :=
if (exists($config:data-exclude)) then
$items except $config:data-exclude
else
$items
return
if ($sortBy) then
nav:sort($sortBy, $items)
else
$items
};
declare %private function query:dispatch($config as map(*), $function as xs:string, $args as array(*)) {
let $fn := function-lookup(xs:QName($config?type || "-query:" || $function), array:size($args))
return
if (exists($fn)) then
apply($fn, $args)
else
()
};
(:~
: Query the data set.
:
: @param $fields a sequence of field names describing the type of content to query,
: e.g. "heading" or "text"
: @param $query the query string
: @param $target-texts a sequence of identifiers for texts to query. May be empty.
:)
declare function query:query-default($fields as xs:string+, $query as xs:string,
$target-texts as xs:string*, $sortBy as xs:string*) {
tei-query:query-default($fields, $query, $target-texts, $sortBy),
docbook-query:query-default($fields, $query, $target-texts, $sortBy),
jats-query:query-default($fields, $query, $target-texts, $sortBy)
};
(: declare function query:query-default($fields as xs:string+, $query as xs:string,
$target-texts as xs:string*, $sortBy as xs:string*) {
let $sortBy := xs:integer($sortBy)
return (
tei-query:query-default($fields, $query, $target-texts, $sortBy),
docbook-query:query-default($fields, $query, $target-texts, $sortBy),
jats-query:query-default($fields, $query, $target-texts, $sortBy)
)
}; :)
declare function query:options($sortBy as xs:string*) {
query:options($sortBy, ())
};
declare function query:options($sortBy as xs:string*, $field as xs:string?) {
map:merge((
$query:QUERY_OPTIONS,
map {
"facets":
map:merge((
for $param in request:get-parameter-names()[starts-with(., 'facet-')]
let $dimension := substring-after($param, 'facet-')
return
map {
$dimension: request:get-parameter($param, ())
}
))
},
if ($sortBy) then
map { "fields": ($sortBy, $config:default-fields, $field) }
else
map { "fields": ($config:default-fields, $field) }
))
};
declare function query:query-metadata($root as xs:string?, $field as xs:string?, $query as xs:string?, $sort as xs:string) {
let $results := (
tei-query:query-metadata($root, $field, $query, $sort) |
docbook-query:query-metadata($root, $field, $query, $sort) |
jats-query:query-metadata($root, $field, $query, $sort)
)
let $mode :=
if ((empty($query) or $query = '') and empty(request:get-parameter-names()[starts-with(., 'facet-')])) then
"browse"
else
"search"
return map {
"all": $results,
"mode": $mode
}
};
declare function query:get-parent-section($config as map(*), $node as node()) {
query:dispatch($config, "get-parent-section", [$node])
};
declare function query:get-breadcrumbs($config as map(*), $hit as node(), $parent-id as xs:string) {
query:dispatch($config, "get-breadcrumbs", [$config, $hit, $parent-id])
};
declare function query:expand($config as map(*), $data as node()) {
query:dispatch($config, "expand", [$data])
};
declare function query:get-current($config as map(*), $div as node()?) {
query:dispatch($config, "get-current", [$config, $div])
};
declare function query:autocomplete($doc as xs:string?, $fields as xs:string+, $q as xs:string) {
tei-query:autocomplete($doc, $fields, $q),
docbook-query:autocomplete($doc, $fields, $q),
jats-query:autocomplete($doc, $fields, $q)
};
navigation.xql
(:
:
: Copyright (C) 2017 TEI Publisher Project
:
: This program is free software: you can redistribute it and/or modify
: it under the terms of the GNU General Public License as published by
: the Free Software Foundation, either version 3 of the License, or
: (at your option) any later version.
:
: This program is distributed in the hope that it will be useful,
: but WITHOUT ANY WARRANTY; without even the implied warranty of
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
: GNU General Public License for more details.
:
: You should have received a copy of the GNU General Public License
: along with this program. If not, see </>.
:)
xquery version "3.1";
module namespace nav=";;
import module namespace config="; at "config.xqm";
import module namespace tei-nav="; at "navigation-tei.xql";
import module namespace jats-nav="; at "navigation-jats.xql";
import module namespace docbook-nav="; at "navigation-dbk.xql";
declare %private function nav:dispatch($config as map(*), $function as xs:string, $args as array(*)) {
let $fn := function-lookup(xs:QName($config?type || "-nav:" || $function), array:size($args))
return
if (exists($fn)) then
apply($fn, $args)
else
()
};
declare function nav:get-root($root as xs:string?, $options as map(*)?) {
tei-nav:get-root($root, $options),
docbook-nav:get-root($root, $options),
jats-nav:get-root($root, $options)
};
declare function nav:get-header($config as map(*), $node as element()) {
nav:dispatch($config, "get-header", [$config, $node])
};
declare function nav:get-section-for-node($config as map(*), $node as element()) {
nav:dispatch($config, "get-section-for-node", [$config, $node])
};
declare function nav:get-section($config as map(*), $doc as node()) {
nav:dispatch($config, "get-section", [$config, $doc])
};
declare function nav:get-metadata($root as element(), $field as xs:string) {
nav:get-metadata(map { "type": config:document-type($root) }, $root, $field)
};
declare function nav:get-metadata($config as map(*), $root as element(), $field as xs:string) {
nav:dispatch($config, "get-metadata", [$config, $root, $field])
};
declare function nav:get-document-title($config as map(*), $root as element()) {
nav:dispatch($config, "get-document-title", [$config, $root])
};
declare function nav:get-subsections($config as map(*), $root as node()) {
nav:dispatch($config, "get-subsections", [$config, $root])
};
declare function nav:get-section-heading($config as map(*), $section as node()) {
nav:dispatch($config, "get-section-heading", [$config, $section])
};
declare function nav:get-first-page-start($config as map(*), $data as node()) {
nav:dispatch($config, "get-first-page-start", [$config, $data])
};
declare function nav:sort($sortBy as xs:string, $items as element()*) {
if (empty($items)) then
()
else
nav:dispatch(map { "type": config:document-type(head($items)) }, "sort", [$sortBy, $items])
};
declare function nav:get-content($config as map(*), $div as element()) {
nav:dispatch($config, "get-content", [$config, $div])
};
declare function nav:get-next($config as map(*), $div as element(), $view as xs:string) {
nav:dispatch($config, "get-next", [$config, $div, $view])
};
declare function nav:get-previous($config as map(*), $div as element(), $view as xs:string) {
nav:dispatch($config, "get-previous", [$config, $div, $view])
};
declare function nav:filler($config as map(*), $div as element()?) {
nav:dispatch($config, "filler", [$config, $div])
};
declare function nav:is-filler($config as map(*), $div) {
nav:dispatch($config, "is-filler", [$config, $div])
};
declare function nav:output-footnotes($footnotes as element()*) {
<div class="popovers">
{
$footnotes/self::pb-popover
}
</div>,
<div class="footnotes">
{
$footnotes[not(self::pb-popover)]
}
</div>
};
query-tei.xql
(:
:
: Copyright (C) 2017 Wolfgang Meier
:
: This program is free software: you can redistribute it and/or modify
: it under the terms of the GNU General Public License as published by
: the Free Software Foundation, either version 3 of the License, or
: (at your option) any later version.
:
: This program is distributed in the hope that it will be useful,
: but WITHOUT ANY WARRANTY; without even the implied warranty of
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
: GNU General Public License for more details.
:
: You should have received a copy of the GNU General Public License
: along with this program. If not, see </>.
:)
xquery version "3.1";
module namespace teis=";;
declare namespace tei=".0";
import module namespace config="; at "config.xqm";
import module namespace nav="; at "navigation-tei.xql";
import module namespace query="; at "query.xql";
declare function teis:query-default($fields as xs:string+, $query as xs:string, $target-texts as xs:string*,
$sortBy as xs:string*) {
if(string($query)) then
for $field in $fields
return
switch ($field)
case "head" return
if (exists($target-texts)) then
for $text in $target-texts
return
$config:data-root ! doc(. || "/" || $text)//tei:head[ft:query(., $query, query:options($sortBy))]
else
collection($config:data-root)//tei:head[ft:query(., $query, query:options($sortBy))]
default return
if (exists($target-texts)) then
for $text in $target-texts
let $divisions := $config:data-root ! doc(. || "/" || $text)//tei:div[ft:query(., $query, query:options($sortBy))]
return
if (empty($divisions)) then
$config:data-root ! doc(. || "/" || $text)//tei:text[ft:query(., $query, query:options($sortBy))]
else
$divisions
else
let $divisions := collection($config:data-root)//tei:div[ft:query(., $query, query:options($sortBy))]
return
if (empty($divisions)) then
collection($config:data-root)//tei:text[ft:query(., $query, query:options($sortBy))]
else
$divisions
else ()
};
declare function teis:query-metadata($path as xs:string?, $field as xs:string?, $query as xs:string?, $sort as xs:string) {
let $queryExpr :=
if ($field = "file" or empty($query) or $query = '') then
"file:*"
else
($field, "text")[1] || ":" || $query
let $options := query:options($sort, ($field, "text")[1])
let $result :=
$config:data-default ! (
collection(. || "/" || $path)//tei:text[ft:query(., $queryExpr, $options)]
)
return
query:sort($result, $sort)
};
declare function teis:autocomplete($doc as xs:string?, $fields as xs:string+, $q as xs:string) {
let $lower-case-q := lower-case($q)
for $field in $fields
return
switch ($field)
case "author" return
collection($config:data-root)/ft:index-keys-for-field("author", $lower-case-q,
function($key, $count) {
$key
}, 30)
case "file" return
collection($config:data-root)/ft:index-keys-for-field("file", $lower-case-q,
function($key, $count) {
$key
}, 30)
case "text" return
if ($doc) then (
doc($config:data-root || "/" || $doc)/util:index-keys-by-qname(xs:QName("tei:div"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index"),
doc($config:data-root || "/" || $doc)/util:index-keys-by-qname(xs:QName("tei:text"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index")
) else (
collection($config:data-root)/util:index-keys-by-qname(xs:QName("tei:div"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index"),
collection($config:data-root)/util:index-keys-by-qname(xs:QName("tei:text"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index")
)
case "head" return
if ($doc) then
doc($config:data-root || "/" || $doc)/util:index-keys-by-qname(xs:QName("tei:head"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index")
else
collection($config:data-root)/util:index-keys-by-qname(xs:QName("tei:head"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index")
default return
collection($config:data-root)/ft:index-keys-for-field("title", $lower-case-q,
function($key, $count) {
$key
}, 30)
};
declare function teis:get-parent-section($node as node()) {
($node/self::tei:text, $node/ancestor-or-self::tei:div[1], $node)[1]
};
declare function teis:get-breadcrumbs($config as map(*), $hit as node(), $parent-id as xs:string) {
let $work := root($hit)/*
let $work-title := nav:get-document-title($config, $work)/string()
return
<div class="breadcrumbs">
<a class="breadcrumb" href="{$parent-id}">{$work-title}</a>
{
for $parentDiv in $hit/ancestor-or-self::tei:div[tei:head]
let $id := util:node-id(
if ($config?view = "page") then ($parentDiv/preceding::tei:pb[1], $parentDiv)[1] else $parentDiv
)
return
<a class="breadcrumb" href="{$parent-id || "?action=search&root=" || $id || "&view=" || $config?view || "&odd=" || $config?odd}">
{$parentDiv/tei:head/string()}
</a>
}
</div>
};
(:~
: Expand the given element and highlight query matches by re-running the query
: on it.
:)
declare function teis:expand($data as node()) {
let $query := session:get-attribute($config:session-prefix || ".search")
let $field := session:get-attribute($config:session-prefix || ".field")
let $div :=
if ($data instance of element(tei:pb)) then
let $nextPage := $data/following::tei:pb[1]
return
if ($nextPage) then
if ($field = "text") then
(
($data/ancestor::tei:div intersect $nextPage/ancestor::tei:div)[last()],
$data/ancestor::tei:text
)[1]
else
$data/ancestor::tei:text
else
(: if there's only one pb in the document, it's whole
text part should be returned :)
if (count($data/ancestor::tei:text//tei:pb) = 1) then
($data/ancestor::tei:text)
else
($data/ancestor::tei:div, $data/ancestor::tei:text)[1]
else
$data
let $result := teis:query-default-view($div, $query, $field)
let $expanded :=
if (exists($result)) then
util:expand($result, "add-exist-id=all")
else
$div
return
if ($data instance of element(tei:pb)) then
$expanded//tei:pb[@exist:id = util:node-id($data)]
else
$expanded
};
declare function teis:query-default-view($context as element()*, $query as xs:string, $fields as xs:string+) {
for $field in $fields
return
switch ($field)
case "head" return
$context[./descendant-or-self::tei:head[ft:query(., $query, $query:QUERY_OPTIONS)]]
default return
$context[./descendant-or-self::tei:div[ft:query(., $query, $query:QUERY_OPTIONS)]] |
$context[./descendant-or-self::tei:text[ft:query(., $query, $query:QUERY_OPTIONS)]]
};
declare function teis:get-current($config as map(*), $div as node()?) {
if (empty($div)) then
()
else
if ($div instance of element(tei:teiHeader)) then
$div
else
(nav:filler($config, $div), $div)[1]
};
xml-file:
<TEI xmlns=".0" xml:id="DCH_Abogados" rendition="tei:teisimple"
xml:lang="es">
<teiHeader xml:lang="en">
<fileDesc>
<titleStmt>
<title>Abogados (DCH)</title>
...
<body>
<div n="1">
<head>1. Introducción</head>
<p>Para las <name type="place">Indias</name>, la definición de <term xml:lang="es">
abogado</term>, sus cualidades y requisitos, venía resuelta por la larga
tradición del <rs xml:lang="la">ius commune</rs>
the results appear in random order
The entire platform project is in this repository []. My specific project has a few changes, but in general it is very similar to the matri code.
I'm coding on a project to digitize historical texts using the XML/TEI language. The texts are published on the tei-publisher plattform. There are two types of search on the platform: faceted search and general search. When searching by facet, the results are presented by article, in alphabetical order. However, when you general search , the results appear in a random order, as shown in the image. I would like the results to appear in alphabetical order and if there are more answers per text, then they should be anized by div
Initially, I was changing this line in the search.xql
file, but I noticed that any change I make to this line, the search results remain unchanged.
let $hitsAll :=
(:If the $query-scope is narrow, query the elements immediately below the lowest div in tei:text and the four major element below tei:teiHeader.:)
for $hit in query:query-default($request?parameters?field, $request?parameters?query, $request?parameters?doc,())
order by xs:string($hit/title) ascending [I changed this line. I added this command]
return $hit
Perhaps I need to make a change to some other file responsible for searches, such as query.xql
, navigation.xql
or query-tei.xql
, but I don't know how to make this change. Any help would be appreciated.
Below are the files I'm using, as well as the structure of the xml file. Thank you very much.
search.xql
xquery version "3.1";
module namespace sapi="http://teipublisher/api/search";
import module namespace query="http://www.tei-c./tei-simple/query" at "../../query.xql";
import module namespace nav="http://www.tei-c./tei-simple/navigation" at "../../navigation.xql";
import module namespace config="http://www.tei-c./tei-simple/config" at "../../config.xqm";
import module namespace tpu="http://www.tei-c./tei-publisher/util" at "../util.xql";
import module namespace kwic="http://exist-db./xquery/kwic" at "resource:/exist/xquery/lib/kwic.xql";
import module namespace facets="http://teipublisher/facets" at "../../facets.xql";
declare namespace tei="http://www.tei-c./ns/1.0";
declare function sapi:autocomplete($request as map(*)) {
let $q := request:get-parameter("query", ())
let $type := request:get-parameter("field", "text")
let $doc := request:get-parameter("doc", ())
let $items :=
if ($q) then
query:autocomplete($doc, $type, $q)
else
()
return
array {
for $item in $items
group by $item
return
map {
"text": $item,
"value": $item
}
}
};
declare function sapi:search($request as map(*)) {
(:If there is no query string, fill up the map with existing values:)
if (empty($request?parameters?query))
then
sapi:show-hits($request, session:get-attribute($config:session-prefix || ".hits"), session:get-attribute($config:session-prefix || ".docs"))
else
(:Otherwise, perform the query.:)
(: Here the actual query commences. This is split into two parts, the first for a Lucene query and the second for an ngram query. :)
(:The query passed to a Luecene query in ft:query is an XML element <query> containing one or two <bool>. The <bool> contain the original query and the transliterated query, as indicated by the user in $query-scripts.:)
let $hitsAll :=
(:If the $query-scope is narrow, query the elements immediately below the lowest div in tei:text and the four major element below tei:teiHeader.:)
for $hit in query:query-default($request?parameters?field, $request?parameters?query, $request?parameters?doc,()) (: aqui que é o anizador :)
order by xs:string($hit/title) ascending
return $hit
let $hitCount := count($hitsAll)
let $hits := if ($hitCount > 1000) then subsequence($hitsAll, 1, 1000) else $hitsAll
(:Store the result in the session.:)
let $store := (
session:set-attribute($config:session-prefix || ".hits", $hitsAll),
session:set-attribute($config:session-prefix || ".hitCount", $hitCount),
session:set-attribute($config:session-prefix || ".search", $request?parameters?query),
session:set-attribute($config:session-prefix || ".field", $request?parameters?field),
session:set-attribute($config:session-prefix || ".docs", $request?parameters?doc)
)
return
sapi:show-hits($request, $hits, $request?parameters?doc )
};
declare %private function sapi:show-hits($request as map(*), $hits as item()*, $docs as xs:string*) {
response:set-header("pb-total", xs:string(count($hits))),
response:set-header("pb-start", xs:string($request?parameters?start)),
for $hit at $p in subsequence($hits, $request?parameters?start, $request?parameters?per-page)
let $config := tpu:parse-pi(root($hit), $request?parameters?view)
let $parent := query:get-parent-section($config, $hit)
let $parent-id := config:get-identifier($parent)
let $parent-id := if (exists($docs)) then replace($parent-id, "^.*?([^/]*)$", "$1") else $parent-id
let $div := query:get-current($config, $parent)
let $expanded := util:expand($hit, "add-exist-id=all")
let $docId := config:get-identifier($div)
return
<paper-card>
<header>
<div class="count">{$request?parameters?start + $p - 1}</div>
{ query:get-breadcrumbs($config, $hit, $parent-id) }
</header>
<div class="matches">
{
for $match in subsequence($expanded//exist:match, 1, 5)
let $matchId := $match/../@exist:id
let $docLink :=
if ($config?view = "page") then
(: first check if there's a pb in the expanded section before the match :)
let $pbBefore := $match/preceding::tei:pb[1]
return
if ($pbBefore) then
$pbBefore/@exist:id
else
(: no: locate the element containing the match in the source document :)
let $contextNode := util:node-by-id($hit, $matchId)
(: and get the pb preceding it :)
let $page := $contextNode/preceding::tei:pb[1]
return
if ($page) then
util:node-id($page)
else
util:node-id($div)
else
(: Check if the document has sections, otherwise don't pass root :)
if (nav:get-section-for-node($config, $div)) then util:node-id($div) else ()
let $config := <config width="60" table="no" link="{$docId}?{if ($docLink) then 'root=' || $docLink || '&' else ()}action=search&view={$config?view}&odd={$config?odd}#{$matchId}"/>
return
kwic:get-summary($expanded, $match, $config)
}
</div>
</paper-card>
};
declare function sapi:facets($request as map(*)) {
let $hits := session:get-attribute($config:session-prefix || ".hits")
where count($hits) > 0
return
<div>
{
for $config in $config:facets?*
return
facets:display($config, $hits)
}
</div>
};
declare function sapi:list-facets($request as map(*)) {
let $type := $request?parameters?type
let $lang := tokenize($request?parameters?language, '-')[1]
let $facetConfig := filter($config:facets?*, function($facetCfg) {
$facetCfg?dimension = $type
})
let $hits := session:get-attribute($config:session-prefix || ".hits")
let $facets := ft:facets($hits, $type, ())
let $matches :=
for $key in if (exists($request?parameters?value)) then $request?parameters?value else map:keys($facets)
let $label := facets:translate($facetConfig, $lang, $key)
return
map {
"text": $label,
"freq": $facets($key),
"value": $key
}
let $filtered := filter($matches, function($item) {
matches($item?text, '(?:^|\W)' || $request?parameters?query, 'i')
})
return
array { $filtered }
};
query.xql
(:
: Copyright (C) 2017 Wolfgang Meier
:
: This program is free software: you can redistribute it and/or modify
: it under the terms of the GNU General Public License as published by
: the Free Software Foundation, either version 3 of the License, or
: (at your option) any later version.
:
: This program is distributed in the hope that it will be useful,
: but WITHOUT ANY WARRANTY; without even the implied warranty of
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
: GNU General Public License for more details.
:
: You should have received a copy of the GNU General Public License
: along with this program. If not, see <http://www.gnu./licenses/>.
:)
xquery version "3.1";
module namespace query="http://www.tei-c./tei-simple/query";
import module namespace tei-query="http://www.tei-c./tei-simple/query/tei" at "query-tei.xql";
import module namespace docbook-query="http://www.tei-c./tei-simple/query/docbook" at "query-db.xql";
import module namespace jats-query="http://www.tei-c./tei-simple/query/jats" at "query-jats.xql";
import module namespace config="http://www.tei-c./tei-simple/config" at "config.xqm";
import module namespace nav="http://www.tei-c./tei-simple/navigation" at "navigation.xql";
declare namespace tei="http://www.tei-c./ns/1.0";
declare variable $query:QUERY_OPTIONS := map {
"leading-wildcard": "yes",
"filter-rewrite": "yes"
};
declare function query:field-prefix($elem as element()) {
switch (namespace-uri($elem))
case "http://www.tei-c./ns/1.0" return
""
case "http://docbook./ns/docbook" return
"dbk."
default return
"jats."
};
declare %private function query:sort($items as element()*, $sortBy as xs:string?) {
let $items :=
if (exists($config:data-exclude)) then
$items except $config:data-exclude
else
$items
return
if ($sortBy) then
nav:sort($sortBy, $items)
else
$items
};
declare %private function query:dispatch($config as map(*), $function as xs:string, $args as array(*)) {
let $fn := function-lookup(xs:QName($config?type || "-query:" || $function), array:size($args))
return
if (exists($fn)) then
apply($fn, $args)
else
()
};
(:~
: Query the data set.
:
: @param $fields a sequence of field names describing the type of content to query,
: e.g. "heading" or "text"
: @param $query the query string
: @param $target-texts a sequence of identifiers for texts to query. May be empty.
:)
declare function query:query-default($fields as xs:string+, $query as xs:string,
$target-texts as xs:string*, $sortBy as xs:string*) {
tei-query:query-default($fields, $query, $target-texts, $sortBy),
docbook-query:query-default($fields, $query, $target-texts, $sortBy),
jats-query:query-default($fields, $query, $target-texts, $sortBy)
};
(: declare function query:query-default($fields as xs:string+, $query as xs:string,
$target-texts as xs:string*, $sortBy as xs:string*) {
let $sortBy := xs:integer($sortBy)
return (
tei-query:query-default($fields, $query, $target-texts, $sortBy),
docbook-query:query-default($fields, $query, $target-texts, $sortBy),
jats-query:query-default($fields, $query, $target-texts, $sortBy)
)
}; :)
declare function query:options($sortBy as xs:string*) {
query:options($sortBy, ())
};
declare function query:options($sortBy as xs:string*, $field as xs:string?) {
map:merge((
$query:QUERY_OPTIONS,
map {
"facets":
map:merge((
for $param in request:get-parameter-names()[starts-with(., 'facet-')]
let $dimension := substring-after($param, 'facet-')
return
map {
$dimension: request:get-parameter($param, ())
}
))
},
if ($sortBy) then
map { "fields": ($sortBy, $config:default-fields, $field) }
else
map { "fields": ($config:default-fields, $field) }
))
};
declare function query:query-metadata($root as xs:string?, $field as xs:string?, $query as xs:string?, $sort as xs:string) {
let $results := (
tei-query:query-metadata($root, $field, $query, $sort) |
docbook-query:query-metadata($root, $field, $query, $sort) |
jats-query:query-metadata($root, $field, $query, $sort)
)
let $mode :=
if ((empty($query) or $query = '') and empty(request:get-parameter-names()[starts-with(., 'facet-')])) then
"browse"
else
"search"
return map {
"all": $results,
"mode": $mode
}
};
declare function query:get-parent-section($config as map(*), $node as node()) {
query:dispatch($config, "get-parent-section", [$node])
};
declare function query:get-breadcrumbs($config as map(*), $hit as node(), $parent-id as xs:string) {
query:dispatch($config, "get-breadcrumbs", [$config, $hit, $parent-id])
};
declare function query:expand($config as map(*), $data as node()) {
query:dispatch($config, "expand", [$data])
};
declare function query:get-current($config as map(*), $div as node()?) {
query:dispatch($config, "get-current", [$config, $div])
};
declare function query:autocomplete($doc as xs:string?, $fields as xs:string+, $q as xs:string) {
tei-query:autocomplete($doc, $fields, $q),
docbook-query:autocomplete($doc, $fields, $q),
jats-query:autocomplete($doc, $fields, $q)
};
navigation.xql
(:
:
: Copyright (C) 2017 TEI Publisher Project
:
: This program is free software: you can redistribute it and/or modify
: it under the terms of the GNU General Public License as published by
: the Free Software Foundation, either version 3 of the License, or
: (at your option) any later version.
:
: This program is distributed in the hope that it will be useful,
: but WITHOUT ANY WARRANTY; without even the implied warranty of
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
: GNU General Public License for more details.
:
: You should have received a copy of the GNU General Public License
: along with this program. If not, see <http://www.gnu./licenses/>.
:)
xquery version "3.1";
module namespace nav="http://www.tei-c./tei-simple/navigation";
import module namespace config="http://www.tei-c./tei-simple/config" at "config.xqm";
import module namespace tei-nav="http://www.tei-c./tei-simple/navigation/tei" at "navigation-tei.xql";
import module namespace jats-nav="http://www.tei-c./tei-simple/navigation/jats" at "navigation-jats.xql";
import module namespace docbook-nav="http://www.tei-c./tei-simple/navigation/docbook" at "navigation-dbk.xql";
declare %private function nav:dispatch($config as map(*), $function as xs:string, $args as array(*)) {
let $fn := function-lookup(xs:QName($config?type || "-nav:" || $function), array:size($args))
return
if (exists($fn)) then
apply($fn, $args)
else
()
};
declare function nav:get-root($root as xs:string?, $options as map(*)?) {
tei-nav:get-root($root, $options),
docbook-nav:get-root($root, $options),
jats-nav:get-root($root, $options)
};
declare function nav:get-header($config as map(*), $node as element()) {
nav:dispatch($config, "get-header", [$config, $node])
};
declare function nav:get-section-for-node($config as map(*), $node as element()) {
nav:dispatch($config, "get-section-for-node", [$config, $node])
};
declare function nav:get-section($config as map(*), $doc as node()) {
nav:dispatch($config, "get-section", [$config, $doc])
};
declare function nav:get-metadata($root as element(), $field as xs:string) {
nav:get-metadata(map { "type": config:document-type($root) }, $root, $field)
};
declare function nav:get-metadata($config as map(*), $root as element(), $field as xs:string) {
nav:dispatch($config, "get-metadata", [$config, $root, $field])
};
declare function nav:get-document-title($config as map(*), $root as element()) {
nav:dispatch($config, "get-document-title", [$config, $root])
};
declare function nav:get-subsections($config as map(*), $root as node()) {
nav:dispatch($config, "get-subsections", [$config, $root])
};
declare function nav:get-section-heading($config as map(*), $section as node()) {
nav:dispatch($config, "get-section-heading", [$config, $section])
};
declare function nav:get-first-page-start($config as map(*), $data as node()) {
nav:dispatch($config, "get-first-page-start", [$config, $data])
};
declare function nav:sort($sortBy as xs:string, $items as element()*) {
if (empty($items)) then
()
else
nav:dispatch(map { "type": config:document-type(head($items)) }, "sort", [$sortBy, $items])
};
declare function nav:get-content($config as map(*), $div as element()) {
nav:dispatch($config, "get-content", [$config, $div])
};
declare function nav:get-next($config as map(*), $div as element(), $view as xs:string) {
nav:dispatch($config, "get-next", [$config, $div, $view])
};
declare function nav:get-previous($config as map(*), $div as element(), $view as xs:string) {
nav:dispatch($config, "get-previous", [$config, $div, $view])
};
declare function nav:filler($config as map(*), $div as element()?) {
nav:dispatch($config, "filler", [$config, $div])
};
declare function nav:is-filler($config as map(*), $div) {
nav:dispatch($config, "is-filler", [$config, $div])
};
declare function nav:output-footnotes($footnotes as element()*) {
<div class="popovers">
{
$footnotes/self::pb-popover
}
</div>,
<div class="footnotes">
{
$footnotes[not(self::pb-popover)]
}
</div>
};
query-tei.xql
(:
:
: Copyright (C) 2017 Wolfgang Meier
:
: This program is free software: you can redistribute it and/or modify
: it under the terms of the GNU General Public License as published by
: the Free Software Foundation, either version 3 of the License, or
: (at your option) any later version.
:
: This program is distributed in the hope that it will be useful,
: but WITHOUT ANY WARRANTY; without even the implied warranty of
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
: GNU General Public License for more details.
:
: You should have received a copy of the GNU General Public License
: along with this program. If not, see <http://www.gnu./licenses/>.
:)
xquery version "3.1";
module namespace teis="http://www.tei-c./tei-simple/query/tei";
declare namespace tei="http://www.tei-c./ns/1.0";
import module namespace config="http://www.tei-c./tei-simple/config" at "config.xqm";
import module namespace nav="http://www.tei-c./tei-simple/navigation/tei" at "navigation-tei.xql";
import module namespace query="http://www.tei-c./tei-simple/query" at "query.xql";
declare function teis:query-default($fields as xs:string+, $query as xs:string, $target-texts as xs:string*,
$sortBy as xs:string*) {
if(string($query)) then
for $field in $fields
return
switch ($field)
case "head" return
if (exists($target-texts)) then
for $text in $target-texts
return
$config:data-root ! doc(. || "/" || $text)//tei:head[ft:query(., $query, query:options($sortBy))]
else
collection($config:data-root)//tei:head[ft:query(., $query, query:options($sortBy))]
default return
if (exists($target-texts)) then
for $text in $target-texts
let $divisions := $config:data-root ! doc(. || "/" || $text)//tei:div[ft:query(., $query, query:options($sortBy))]
return
if (empty($divisions)) then
$config:data-root ! doc(. || "/" || $text)//tei:text[ft:query(., $query, query:options($sortBy))]
else
$divisions
else
let $divisions := collection($config:data-root)//tei:div[ft:query(., $query, query:options($sortBy))]
return
if (empty($divisions)) then
collection($config:data-root)//tei:text[ft:query(., $query, query:options($sortBy))]
else
$divisions
else ()
};
declare function teis:query-metadata($path as xs:string?, $field as xs:string?, $query as xs:string?, $sort as xs:string) {
let $queryExpr :=
if ($field = "file" or empty($query) or $query = '') then
"file:*"
else
($field, "text")[1] || ":" || $query
let $options := query:options($sort, ($field, "text")[1])
let $result :=
$config:data-default ! (
collection(. || "/" || $path)//tei:text[ft:query(., $queryExpr, $options)]
)
return
query:sort($result, $sort)
};
declare function teis:autocomplete($doc as xs:string?, $fields as xs:string+, $q as xs:string) {
let $lower-case-q := lower-case($q)
for $field in $fields
return
switch ($field)
case "author" return
collection($config:data-root)/ft:index-keys-for-field("author", $lower-case-q,
function($key, $count) {
$key
}, 30)
case "file" return
collection($config:data-root)/ft:index-keys-for-field("file", $lower-case-q,
function($key, $count) {
$key
}, 30)
case "text" return
if ($doc) then (
doc($config:data-root || "/" || $doc)/util:index-keys-by-qname(xs:QName("tei:div"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index"),
doc($config:data-root || "/" || $doc)/util:index-keys-by-qname(xs:QName("tei:text"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index")
) else (
collection($config:data-root)/util:index-keys-by-qname(xs:QName("tei:div"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index"),
collection($config:data-root)/util:index-keys-by-qname(xs:QName("tei:text"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index")
)
case "head" return
if ($doc) then
doc($config:data-root || "/" || $doc)/util:index-keys-by-qname(xs:QName("tei:head"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index")
else
collection($config:data-root)/util:index-keys-by-qname(xs:QName("tei:head"), $lower-case-q,
function($key, $count) {
$key
}, 30, "lucene-index")
default return
collection($config:data-root)/ft:index-keys-for-field("title", $lower-case-q,
function($key, $count) {
$key
}, 30)
};
declare function teis:get-parent-section($node as node()) {
($node/self::tei:text, $node/ancestor-or-self::tei:div[1], $node)[1]
};
declare function teis:get-breadcrumbs($config as map(*), $hit as node(), $parent-id as xs:string) {
let $work := root($hit)/*
let $work-title := nav:get-document-title($config, $work)/string()
return
<div class="breadcrumbs">
<a class="breadcrumb" href="{$parent-id}">{$work-title}</a>
{
for $parentDiv in $hit/ancestor-or-self::tei:div[tei:head]
let $id := util:node-id(
if ($config?view = "page") then ($parentDiv/preceding::tei:pb[1], $parentDiv)[1] else $parentDiv
)
return
<a class="breadcrumb" href="{$parent-id || "?action=search&root=" || $id || "&view=" || $config?view || "&odd=" || $config?odd}">
{$parentDiv/tei:head/string()}
</a>
}
</div>
};
(:~
: Expand the given element and highlight query matches by re-running the query
: on it.
:)
declare function teis:expand($data as node()) {
let $query := session:get-attribute($config:session-prefix || ".search")
let $field := session:get-attribute($config:session-prefix || ".field")
let $div :=
if ($data instance of element(tei:pb)) then
let $nextPage := $data/following::tei:pb[1]
return
if ($nextPage) then
if ($field = "text") then
(
($data/ancestor::tei:div intersect $nextPage/ancestor::tei:div)[last()],
$data/ancestor::tei:text
)[1]
else
$data/ancestor::tei:text
else
(: if there's only one pb in the document, it's whole
text part should be returned :)
if (count($data/ancestor::tei:text//tei:pb) = 1) then
($data/ancestor::tei:text)
else
($data/ancestor::tei:div, $data/ancestor::tei:text)[1]
else
$data
let $result := teis:query-default-view($div, $query, $field)
let $expanded :=
if (exists($result)) then
util:expand($result, "add-exist-id=all")
else
$div
return
if ($data instance of element(tei:pb)) then
$expanded//tei:pb[@exist:id = util:node-id($data)]
else
$expanded
};
declare function teis:query-default-view($context as element()*, $query as xs:string, $fields as xs:string+) {
for $field in $fields
return
switch ($field)
case "head" return
$context[./descendant-or-self::tei:head[ft:query(., $query, $query:QUERY_OPTIONS)]]
default return
$context[./descendant-or-self::tei:div[ft:query(., $query, $query:QUERY_OPTIONS)]] |
$context[./descendant-or-self::tei:text[ft:query(., $query, $query:QUERY_OPTIONS)]]
};
declare function teis:get-current($config as map(*), $div as node()?) {
if (empty($div)) then
()
else
if ($div instance of element(tei:teiHeader)) then
$div
else
(nav:filler($config, $div), $div)[1]
};
xml-file:
<TEI xmlns="http://www.tei-c./ns/1.0" xml:id="DCH_Abogados" rendition="tei:teisimple"
xml:lang="es">
<teiHeader xml:lang="en">
<fileDesc>
<titleStmt>
<title>Abogados (DCH)</title>
...
<body>
<div n="1">
<head>1. Introducción</head>
<p>Para las <name type="place">Indias</name>, la definición de <term xml:lang="es">
abogado</term>, sus cualidades y requisitos, venía resuelta por la larga
tradición del <rs xml:lang="la">ius commune</rs>
the results appear in random order
The entire platform project is in this repository [https://github/eeditiones/tei-publisher-app]. My specific project has a few changes, but in general it is very similar to the matri code.
Share Improve this question edited yesterday ndso86 asked 2 days ago ndso86ndso86 11 bronze badge 01 Answer
Reset to default 0SOLVED::
Someone helped me and the solution is this:
for $hit in query:query-default($request?parameters?field, $request?parameters?query, $request?parameters?doc, ())
let $config := tpu:parse-pi(root($hit), $request?parameters?view)
let $parent := query:get-parent-section($config, $hit)
let $parent-id := config:get-identifier($parent)
order by query:get-breadcrumbs($config, $hit, $parent-id) ascending
return $hit```
-------------------------------------