diff --git a/profiles/base10/modules/config.xqm b/profiles/base10/modules/config.xqm
index 5800fcd..b59bd3d 100644
--- a/profiles/base10/modules/config.xqm
+++ b/profiles/base10/modules/config.xqm
@@ -117,34 +117,6 @@ declare variable $config:pagination-depth := $gen:pagination-depth;
:)
declare variable $config:pagination-fill := $gen:pagination-fill;
-(:
- : Display configuration for facets to be shown in the sidebar. The facets themselves
- : are configured in the index configuration, collection.xconf.
- :)
-declare variable $config:facets := [
- map {
- "dimension": "genre",
- "heading": "facets.genre",
- "max": 5,
- "hierarchical": true()
- },
- map {
- "dimension": "language",
- "heading": "facets.language",
- "max": 5,
- "hierarchical": false(),
- "output": function($label) {
- switch($label)
- case "de" return "German"
- case "es" return "Spanish"
- case "la" return "Latin"
- case "fr" return "French"
- case "en" return "English"
- default return $label
- }
- }
-];
-
(:
: The function to be called to determine the next content chunk to display.
: It takes two parameters:
diff --git a/profiles/base10/modules/facets-config.xql b/profiles/base10/modules/facets-config.xql
new file mode 100644
index 0000000..8fee875
--- /dev/null
+++ b/profiles/base10/modules/facets-config.xql
@@ -0,0 +1,33 @@
+module namespace facets-config="http://teipublisher.com/api/facets-config";
+
+import module namespace config="http://www.tei-c.org/tei-simple/config" at "config.xqm";
+
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+declare function facets-config:get-name($id as xs:string, $type as xs:string) as xs:string {
+ let $entity := collection($config:register-root)/id($id)
+ return head((
+ switch ($type)
+ case 'place' return head(($entity//tei:placeName[@type = "main"], $entity//tei:placeName))
+ case 'actor' return head(($entity//(tei:persName | tei:orgName)[@type = "main"], $entity//tei:placeName))
+ default return "ERR",
+ "Unresolvable entity " || $id || " of type " || $type)
+ )
+};
+
+declare %public variable $facets-config:facets as array(*) := [
+ map {
+ "dimension": "place",
+ "heading": "facets.place",
+ "max": 5,
+ "hierarchical": false(),
+ "output": facets-config:get-name(?, 'place')
+ },
+ map {
+ "dimension": "actor",
+ "heading": "facets.actor",
+ "max": 5,
+ "hierarchical": false(),
+ "output": facets-config:get-name(?, 'actor')
+ }
+];
diff --git a/profiles/base10/modules/facets.xql b/profiles/base10/modules/facets.xql
new file mode 100644
index 0000000..f734a12
--- /dev/null
+++ b/profiles/base10/modules/facets.xql
@@ -0,0 +1,166 @@
+(:
+ :
+ : Copyright (C) 2019 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 facets="http://teipublisher.com/facets";
+
+import module namespace config="http://www.tei-c.org/tei-simple/config" at "config.xqm";
+
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+declare function facets:sort($config as map(*), $lang as xs:string?, $facets as map(*)?) {
+ array {
+ if (exists($facets)) then
+ for $key in map:keys($facets)
+ let $value := map:get($facets, $key)
+ let $sortKey := facets:translate($config, $lang, $key)
+ order by $sortKey ascending
+ return
+ map { $key: $value }
+ else
+ ()
+ }
+};
+
+declare function facets:print-table($config as map(*), $nodes as element()+, $values as xs:string*, $params as xs:string*) {
+ let $all := exists($config?max) and facets:get-parameter("all-" || $config?dimension)
+ let $lang := tokenize(facets:get-parameter("language"), '-')[1]
+ let $count := if ($all) then 50 else $config?max
+ let $facets :=
+ if (exists($values)) then
+ ft:facets($nodes, $config?dimension, $count, $values)
+ else
+ ft:facets($nodes, $config?dimension, $count)
+ return
+ if (map:size($facets) > 0) then
+
+
+ { if ($label = $params) then attribute checked { "checked" } else () }
+ {$content}
+
+
+
{$freq}
+
,
+ if (empty($params)) then
+ ()
+ else
+ let $nested := facets:print-table($config, $nodes, ($values, head($params)), tail($params))
+ return
+ if ($nested and head($params) eq $label) then
+
+
+ {$nested}
+
+
+ else
+ ()
+ })
+ })
+ }
+
+ else
+ ()
+};
+
+declare function facets:display($config as map(*), $nodes as element()+) {
+ let $params := facets:get-parameter("facet-" || $config?dimension)
+ let $lang := tokenize(facets:get-parameter("language"), '-')[1]
+ let $table := facets:print-table($config, $nodes, (), $params)
+
+ let $maxcount := 50
+ (: maximum number shown :)
+ let $max := head(($config?max, 50))
+
+ (: facet count for current values selected :)
+ let $fcount :=
+ map:size(
+ if (count($params)) then
+ ft:facets($nodes, $config?dimension, $maxcount, $params)
+ else
+ ft:facets($nodes, $config?dimension, $maxcount)
+ )
+
+ where $table
+ return (
+
+
{$config?heading}
+ {
+ if ($fcount > $max) then
+
+ { if (facets:get-parameter("all-" || $config?dimension)) then attribute checked { "checked" } else () }
+ Show top 50
+
+ else
+ ()
+ }
+
+ {
+ $table,
+ (: if config specifies a property "source", output combo-box :)
+ if (map:contains($config, "source")) then
+ (: use source as URL to API endpoint from which to retrieve possible values :)
+
+
+
+ else
+ ()
+ }
+
+ )
+};
+
+declare function facets:get-parameter($name as xs:string) {
+ let $param := request:get-parameter($name, ())
+ return
+ if (exists($param)) then
+ $param
+ else
+ let $fromSession := session:get-attribute($config:session-prefix || '.params')
+ return
+ if (exists($fromSession)) then
+ $fromSession?($name)
+ else
+ ()
+};
+
+declare function facets:translate($config as map(*)?, $language as xs:string?, $label as xs:string) {
+ if (exists($config) and map:contains($config, "output")) then
+ let $fn := $config?output
+ return
+ if (function-arity($fn) = 2) then
+ $fn($label, $language)
+ else
+ $fn($label)
+ else
+ $label
+};
\ No newline at end of file
diff --git a/profiles/base10/modules/lib/api/search.xql b/profiles/base10/modules/lib/api/search.xql
new file mode 100644
index 0000000..3db0c49
--- /dev/null
+++ b/profiles/base10/modules/lib/api/search.xql
@@ -0,0 +1,150 @@
+xquery version "3.1";
+
+module namespace sapi="http://teipublisher.com/api/search";
+
+import module namespace query="http://www.tei-c.org/tei-simple/query" at "../../query.xql";
+import module namespace nav="http://www.tei-c.org/tei-simple/navigation" at "../../navigation.xql";
+import module namespace config="http://www.tei-c.org/tei-simple/config" at "../../config.xqm";
+import module namespace tpu="http://www.tei-c.org/tei-publisher/util" at "../util.xql";
+import module namespace kwic="http://exist-db.org/xquery/kwic" at "resource:org/exist/xquery/lib/kwic.xql";
+import module namespace facets="http://teipublisher.com/facets" at "../../facets.xql";
+import module namespace facets-config="http://teipublisher.com/api/facets-config" at "../../facets-config.xqm";
+
+declare namespace tei="http://www.tei-c.org/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 containing one or two . The 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, ())
+ order by ft:score($hit) descending
+ 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
+
+
+
+ {
+ 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 :=
+ return
+ kwic:get-summary($expanded, $match, $config)
+ }
+
+
+};
+
+declare function sapi:facets($request as map(*)) {
+
+ let $hits := session:get-attribute($config:session-prefix || ".hits")
+ where count($hits) > 0
+ return
+
+ {
+ for $config in $facets-config:facets?*
+ return
+ facets:display($config, $hits)
+ }
+
+};
+
+declare function sapi:list-facets($request as map(*)) {
+ let $type := $request?parameters?type
+ let $lang := tokenize($request?parameters?language, '-')[1]
+ let $facetConfig := filter($facets-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 }
+};