implemented full-text search
This commit is contained in:
parent
947fe623da
commit
9b42996cef
6 changed files with 203 additions and 15 deletions
|
@ -89,7 +89,11 @@ body>header>nav,
|
|||
body>footer>nav {
|
||||
margin: auto;
|
||||
--pico-primary: #8D0B02 !important;
|
||||
--pico-primary-background: #8D0B02 !important;
|
||||
--pico-primary-border: #8D0B02 !important;
|
||||
--pico-primary-hover: #cd5c5c !important;
|
||||
--pico-primary-hover-background: #cd5c5c !important;
|
||||
--pico-primary-hover-border: #cd5c5c !important;
|
||||
}
|
||||
body>header>nav {
|
||||
border-bottom-width: thin;
|
||||
|
@ -305,6 +309,21 @@ legend {}
|
|||
|
||||
form {}
|
||||
|
||||
header form {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
header form button {
|
||||
padding-left: 1em !important;
|
||||
padding-right: 1em !important;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
header form select {
|
||||
max-width:35%;
|
||||
}
|
||||
|
||||
label {}
|
||||
|
||||
::placeholder {}
|
||||
|
|
|
@ -45,7 +45,7 @@ $( document ).ready(function() {
|
|||
if(glosses.length > 0) {
|
||||
entrystring = `${glosses.join('; ')}: `
|
||||
}
|
||||
opt = $(`<option value="${out.xmlid}">${entrystring}<i>${out.form}</i>
|
||||
opt = $(`<option value="${out.xmlid}" data-abv-xpath="${e.refs[0].path}">${entrystring}<i>${out.form}</i>
|
||||
${out.glosses.map(g => `‘${g}’`).join(', ')}</option>`);
|
||||
$select_entry.append(opt)
|
||||
})
|
||||
|
@ -57,7 +57,8 @@ $( document ).ready(function() {
|
|||
|
||||
$('#abv-select-entry').on("dblclick", function() {
|
||||
let $selected = $(this).children('option:selected');
|
||||
let $entry = $selected.attr('value');
|
||||
window.location.replace('./dict/' + $entry);
|
||||
let entry = $selected.attr('value');
|
||||
let path = $selected.attr('data-abv-xpath');
|
||||
window.location.replace('./dict/' + entry + `?entry=${entry}&xpath=${path}`);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -268,3 +268,39 @@ declare function abv-m:langname-by-id($id as xs:string, $lang as xs:string) {
|
|||
declare function abv-m:entry-form-by-id($id as xs:string) {
|
||||
doc(`abaevdict_index/lookup.xml`)/tei:table[1]/tei:entry[@xml:id=$id]/text()
|
||||
};
|
||||
|
||||
declare function abv-m:mark-element($doc as document-node(), $path as xs:string) {
|
||||
let $doc-tr := $doc transform with {
|
||||
for $n in xquery:eval($path, {'': .})
|
||||
return replace node $n
|
||||
with <abv:mark>{$n}</abv:mark>
|
||||
}
|
||||
return $doc-tr
|
||||
};
|
||||
|
||||
(: Function to search, used in API and elsewhere :)
|
||||
declare function abv-m:search($db-lang as xs:string,
|
||||
$type as xs:string,
|
||||
$query as xs:string) {
|
||||
let $pexpr := string-join(
|
||||
('declare namespace tei = "http://www.tei-c.org/ns/1.0";',
|
||||
switch($type)
|
||||
case "full" return "//text()"
|
||||
case "form" return "/tei:entry[1]/tei:form/tei:orth"
|
||||
case "sense" return "/tei:entry[1]/tei:sense"
|
||||
case "example" return "/tei:entry[1]//tei:cit[@type='example']/tei:quote"
|
||||
case "translation" return "/tei:entry[1]//tei:cit[@type='translation']"
|
||||
case "mentioned" return "/tei:entry[1]//tei:mentioned/(tei:m|tei:w|tei:phr|tei:s)"
|
||||
case "gloss" return "tei:entry[1]//tei:gloss"
|
||||
case "etym" return "tei:entry[1]/tei:etym[1]//text()"
|
||||
default return "//text()")
|
||||
)
|
||||
return array{for $doc in collection(`abaevdict_{$db-lang}/xml`)
|
||||
let $hits := for $node in xquery:eval($pexpr, {'': $doc})
|
||||
where $node contains text {$query}
|
||||
return path($node)
|
||||
where count($hits) > 0
|
||||
order by abv-m:sortKey($doc/tei:entry[1]/tei:form[1]/tei:orth[1])
|
||||
return {'entry_id': string($doc/tei:entry[1]/@xml:id),
|
||||
'path': array:build($hits)}}
|
||||
};
|
|
@ -85,7 +85,7 @@ declare function api:word-info-long($n as node()) {
|
|||
'refs': array:build(./ref,
|
||||
fn {
|
||||
map:merge((
|
||||
map:entry('node-id', string(./@node-id)),
|
||||
map:entry('path', string(./@path)),
|
||||
if(./gloss) then
|
||||
{'glosses': array:build(distinct-values(./gloss/@text), string#1)}
|
||||
else ()
|
||||
|
@ -168,7 +168,7 @@ declare %rest:path("{$db-lang}/api/languages/{$lang}/words/{$word-id}")
|
|||
)
|
||||
};
|
||||
|
||||
(: Entry info (only form for now) :)
|
||||
(: Entry info :)
|
||||
declare %rest:path("{$db-lang}/api/entries")
|
||||
%rest:GET
|
||||
function api:entries($db-lang as xs:string) {
|
||||
|
@ -187,4 +187,15 @@ declare %rest:path("{$db-lang}/api/entries/{$entry-id}")
|
|||
$db-lang)
|
||||
};
|
||||
|
||||
(: Search API :)
|
||||
declare %rest:path("{$db-lang}/api/search")
|
||||
%rest:query-param("type","{$type}",'full')
|
||||
%rest:query-param("query","{$query}")
|
||||
%rest:GET
|
||||
function api:search($db-lang as xs:string,
|
||||
$type as xs:string,
|
||||
$query as xs:string) {
|
||||
abv-m:search($db-lang,$type,$query)
|
||||
};
|
||||
|
||||
0
|
|
@ -4,6 +4,7 @@ declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
|||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at './abv-mod.xqm';
|
||||
|
||||
|
||||
(: =========================================================== :)
|
||||
(: ================== GLOBAL VARIABLES ======================= :)
|
||||
(: =========================================================== :)
|
||||
|
@ -49,7 +50,7 @@ declare function page:header($lang as xs:string, $href-other as xs:string) {
|
|||
<strong>{
|
||||
switch($lang)
|
||||
case 'ru' return 'ИЭСОЯ В. И. Абаева'
|
||||
default return 'Abaevdict'
|
||||
default return `Abaevdict`
|
||||
}</strong><i> β</i></li>
|
||||
<li>{
|
||||
(element {if ($lang = 'ru') then 'mark' else 'a'}{
|
||||
|
@ -68,6 +69,59 @@ declare function page:header($lang as xs:string, $href-other as xs:string) {
|
|||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<form role="search" action="./search/new" id="abv-search">
|
||||
{if (session:get('searchQuery')) then
|
||||
<button onclick="location.href='./search/clear'" type="button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||
</svg>
|
||||
</button>
|
||||
else
|
||||
<select name="searchType" required="1">
|
||||
<option selected="1" value="full">All</option>
|
||||
<option value="form">Forms</option>
|
||||
<option value="sense">Senses</option>
|
||||
<option value="example">Examples</option>
|
||||
<option value="translation">Translations</option>
|
||||
<option value="mentioned">Mentioned</option>
|
||||
<option value="gloss">Glosses</option>
|
||||
<option value="etym">Etymology</option>
|
||||
</select>
|
||||
}
|
||||
<input name="searchQuery"
|
||||
placeholder="{if ($lang = 'ru') then 'Искать' else 'Search'}"
|
||||
value="{session:get('searchQuery')}"
|
||||
aria-label="Search" />
|
||||
{if (not(session:get('searchQuery'))) then
|
||||
<button type="submit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="16" height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor" class="bi bi-search">
|
||||
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
|
||||
</svg>
|
||||
</button>
|
||||
else
|
||||
(
|
||||
if (session:get('searchN') > 1) then
|
||||
<button type="button" onclick="location.href='./search/prev'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8"/>
|
||||
</svg>
|
||||
</button>,
|
||||
<button disabled="true">{session:get('searchN')}/{array:size(session:get('searchData'))}</button>
|
||||
,
|
||||
if (session:get('searchN') and session:get('searchN') < array:size(session:get('searchData'))) then
|
||||
<button type="button" onclick="location.href='./search/next'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8"/>
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</form>
|
||||
</li>
|
||||
<li><a href="./home">{
|
||||
switch($lang)
|
||||
case 'ru' return 'Главная'
|
||||
|
@ -96,6 +150,62 @@ declare function page:invert-lang($lang as xs:string) as xs:string {
|
|||
if($lang = 'en') then 'ru' else 'en'
|
||||
};
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ======================= SEARCH ENTRYPOINT ===================== :)
|
||||
(: =============================================================== :)
|
||||
|
||||
declare %rest:path("/search/{$path=.+}")
|
||||
%rest:GET
|
||||
function page:search-default($path as xs:string) {
|
||||
web:forward(`/en/search/{$path}`)
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/new")
|
||||
%rest:query-param("searchType", "{$searchType}", 'full')
|
||||
%rest:query-param("searchQuery", "{$searchQuery}")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:search($lang, $searchType, $searchQuery) {
|
||||
let $r1 := session:set('searchType', $searchType)
|
||||
let $r2 := session:set('searchQuery', $searchQuery)
|
||||
let $r3 := session:set('searchN', 1)
|
||||
let $sd := abv-m:search($lang,$searchType,$searchQuery)
|
||||
let $r4 := session:set('searchData',
|
||||
$sd)
|
||||
return page:by-id($lang, $sd(1)('entry_id'), string-join($sd(1)('path')?*,"|"))
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/next")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:search-next($lang) {
|
||||
let $n := session:get('searchN')+1
|
||||
let $r1 := session:set('searchN', $n)
|
||||
let $sd := session:get('searchData')
|
||||
return page:by-id($lang, $sd($n)('entry_id'), string-join($sd($n)('path')?*,"|"))
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/prev")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:search-prev($lang) {
|
||||
let $n := session:get('searchN')-1
|
||||
let $r1 := session:set('searchN', $n)
|
||||
let $sd := session:get('searchData')
|
||||
return page:by-id($lang, $sd($n)('entry_id'), string-join($sd($n)('path')?*,"|"))
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/clear")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:clearSearch($lang) {
|
||||
let $r1 := session:delete('searchType')
|
||||
let $r2 := session:delete('searchQuery')
|
||||
let $r3 := session:delete('searchN')
|
||||
let $r4 := session:delete('searchData')
|
||||
return web:redirect('../dict')
|
||||
};
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ======================= HOMEPAGE ============================== :)
|
||||
(: =============================================================== :)
|
||||
|
@ -328,9 +438,11 @@ declare %rest:path("dict")
|
|||
(: The main dictionary view :)
|
||||
declare %rest:path("{$lang}/dict")
|
||||
%rest:query-param("page","{$p}", 1)
|
||||
%rest:query-param("xpath","{$xpath}", '')
|
||||
%rest:query-param("entry","{$entry}", '')
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:dict($lang, $p) {
|
||||
function page:dict($lang, $p, $xpath, $entry) {
|
||||
<html>
|
||||
{page:head('HEDO – Dictionary',
|
||||
(<script src="/static/infinite-scroll.pkgd.min.js">
|
||||
|
@ -381,7 +493,9 @@ declare %rest:path("{$lang}/dict")
|
|||
<main>
|
||||
{for $doc at $i in $page:sorted
|
||||
where $i > ($p - 1) * $page:items and $i <= $p * $page:items
|
||||
let $html := doc(`abaevdict_{$lang}/html/{$doc/@xml:id}.html`)
|
||||
let $html := if ($xpath != '' and $entry = $doc/@xml:id)
|
||||
then abv-m:mark-element(doc(`abaevdict_{$lang}/xml/{$doc/@xml:id}.xml`),$xpath) => abv-m:make-html($lang)
|
||||
else doc(`abaevdict_{$lang}/html/{$doc/@xml:id}.html`)
|
||||
return if (doc('abaevdict_index/mentioned_en.xml')/
|
||||
lang-index/lang[@id != 'os' and
|
||||
not(starts-with(@id,'os-'))]/word/entry[@id=string($doc/@xml:id)]) then
|
||||
|
@ -430,20 +544,26 @@ declare %rest:path("{$lang}/dict")
|
|||
(: == ENTRY BY ID == :)
|
||||
(: English as default language :)
|
||||
declare %rest:path("dict/{$id}")
|
||||
%rest:query-param("xpath", "{$xpath}")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:by-id($id) {
|
||||
page:by-id('en',$id)
|
||||
function page:by-id($id, $xpath) {
|
||||
page:by-id('en', $id, $xpath)
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/dict/{$id}")
|
||||
%rest:query-param("xpath", "{$xpath}")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:by-id($lang, $id) {
|
||||
function page:by-id($lang, $id, $xpath) {
|
||||
(: let $doc-index := index-of($page:sorted, $page:sorted[@xml:id=`{$id}`]) :)
|
||||
let $doc-index := index-where($page:sorted, fn { ./@xml:id=$id })
|
||||
let $pagenum := ceiling($doc-index div $page:items)
|
||||
return web:redirect('../dict', {'page': $pagenum}, web:decode-url($id))
|
||||
return web:redirect('../dict',
|
||||
{'page': $pagenum,
|
||||
'entry': web:decode-url($id),
|
||||
'xpath': $xpath},
|
||||
web:decode-url($id))
|
||||
};
|
||||
|
||||
(: =============================================================== :)
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<xsl:template match="abv:mark">
|
||||
<mark>
|
||||
<xsl:next-match/>
|
||||
<xsl:apply-templates/>
|
||||
</mark>
|
||||
</xsl:template>
|
||||
|
||||
|
@ -129,7 +129,8 @@
|
|||
</xsl:element>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="tei:cit[@type='example']/tei:quote">
|
||||
<xsl:template match="tei:cit[@type='example']/tei:quote |
|
||||
tei:cit[@type='example']/abv:mark/tei:quote">
|
||||
<xsl:element name="i">
|
||||
<xsl:attribute name="class">abv-example</xsl:attribute>
|
||||
<xsl:attribute name="lang" select="../@xml:lang"/>
|
||||
|
|
Loading…
Add table
Reference in a new issue