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 {
|
body>footer>nav {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
--pico-primary: #8D0B02 !important;
|
--pico-primary: #8D0B02 !important;
|
||||||
|
--pico-primary-background: #8D0B02 !important;
|
||||||
|
--pico-primary-border: #8D0B02 !important;
|
||||||
--pico-primary-hover: #cd5c5c !important;
|
--pico-primary-hover: #cd5c5c !important;
|
||||||
|
--pico-primary-hover-background: #cd5c5c !important;
|
||||||
|
--pico-primary-hover-border: #cd5c5c !important;
|
||||||
}
|
}
|
||||||
body>header>nav {
|
body>header>nav {
|
||||||
border-bottom-width: thin;
|
border-bottom-width: thin;
|
||||||
|
@ -305,6 +309,21 @@ legend {}
|
||||||
|
|
||||||
form {}
|
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 {}
|
label {}
|
||||||
|
|
||||||
::placeholder {}
|
::placeholder {}
|
||||||
|
|
|
@ -45,7 +45,7 @@ $( document ).ready(function() {
|
||||||
if(glosses.length > 0) {
|
if(glosses.length > 0) {
|
||||||
entrystring = `${glosses.join('; ')}: `
|
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>`);
|
${out.glosses.map(g => `‘${g}’`).join(', ')}</option>`);
|
||||||
$select_entry.append(opt)
|
$select_entry.append(opt)
|
||||||
})
|
})
|
||||||
|
@ -57,7 +57,8 @@ $( document ).ready(function() {
|
||||||
|
|
||||||
$('#abv-select-entry').on("dblclick", function() {
|
$('#abv-select-entry').on("dblclick", function() {
|
||||||
let $selected = $(this).children('option:selected');
|
let $selected = $(this).children('option:selected');
|
||||||
let $entry = $selected.attr('value');
|
let entry = $selected.attr('value');
|
||||||
window.location.replace('./dict/' + $entry);
|
let path = $selected.attr('data-abv-xpath');
|
||||||
|
window.location.replace('./dict/' + entry + `?entry=${entry}&xpath=${path}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -267,4 +267,40 @@ 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) {
|
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()
|
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,
|
'refs': array:build(./ref,
|
||||||
fn {
|
fn {
|
||||||
map:merge((
|
map:merge((
|
||||||
map:entry('node-id', string(./@node-id)),
|
map:entry('path', string(./@path)),
|
||||||
if(./gloss) then
|
if(./gloss) then
|
||||||
{'glosses': array:build(distinct-values(./gloss/@text), string#1)}
|
{'glosses': array:build(distinct-values(./gloss/@text), string#1)}
|
||||||
else ()
|
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")
|
declare %rest:path("{$db-lang}/api/entries")
|
||||||
%rest:GET
|
%rest:GET
|
||||||
function api:entries($db-lang as xs:string) {
|
function api:entries($db-lang as xs:string) {
|
||||||
|
@ -187,4 +187,15 @@ declare %rest:path("{$db-lang}/api/entries/{$entry-id}")
|
||||||
$db-lang)
|
$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
|
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';
|
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at './abv-mod.xqm';
|
||||||
|
|
||||||
|
|
||||||
(: =========================================================== :)
|
(: =========================================================== :)
|
||||||
(: ================== GLOBAL VARIABLES ======================= :)
|
(: ================== GLOBAL VARIABLES ======================= :)
|
||||||
(: =========================================================== :)
|
(: =========================================================== :)
|
||||||
|
@ -49,7 +50,7 @@ declare function page:header($lang as xs:string, $href-other as xs:string) {
|
||||||
<strong>{
|
<strong>{
|
||||||
switch($lang)
|
switch($lang)
|
||||||
case 'ru' return 'ИЭСОЯ В. И. Абаева'
|
case 'ru' return 'ИЭСОЯ В. И. Абаева'
|
||||||
default return 'Abaevdict'
|
default return `Abaevdict`
|
||||||
}</strong><i> β</i></li>
|
}</strong><i> β</i></li>
|
||||||
<li>{
|
<li>{
|
||||||
(element {if ($lang = 'ru') then 'mark' else 'a'}{
|
(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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<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">{
|
<li><a href="./home">{
|
||||||
switch($lang)
|
switch($lang)
|
||||||
case 'ru' return 'Главная'
|
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'
|
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 ============================== :)
|
(: ======================= HOMEPAGE ============================== :)
|
||||||
(: =============================================================== :)
|
(: =============================================================== :)
|
||||||
|
@ -328,9 +438,11 @@ declare %rest:path("dict")
|
||||||
(: The main dictionary view :)
|
(: The main dictionary view :)
|
||||||
declare %rest:path("{$lang}/dict")
|
declare %rest:path("{$lang}/dict")
|
||||||
%rest:query-param("page","{$p}", 1)
|
%rest:query-param("page","{$p}", 1)
|
||||||
|
%rest:query-param("xpath","{$xpath}", '')
|
||||||
|
%rest:query-param("entry","{$entry}", '')
|
||||||
%output:method("html")
|
%output:method("html")
|
||||||
%output:html-version('5')
|
%output:html-version('5')
|
||||||
function page:dict($lang, $p) {
|
function page:dict($lang, $p, $xpath, $entry) {
|
||||||
<html>
|
<html>
|
||||||
{page:head('HEDO – Dictionary',
|
{page:head('HEDO – Dictionary',
|
||||||
(<script src="/static/infinite-scroll.pkgd.min.js">
|
(<script src="/static/infinite-scroll.pkgd.min.js">
|
||||||
|
@ -381,7 +493,9 @@ declare %rest:path("{$lang}/dict")
|
||||||
<main>
|
<main>
|
||||||
{for $doc at $i in $page:sorted
|
{for $doc at $i in $page:sorted
|
||||||
where $i > ($p - 1) * $page:items and $i <= $p * $page:items
|
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')/
|
return if (doc('abaevdict_index/mentioned_en.xml')/
|
||||||
lang-index/lang[@id != 'os' and
|
lang-index/lang[@id != 'os' and
|
||||||
not(starts-with(@id,'os-'))]/word/entry[@id=string($doc/@xml:id)]) then
|
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 == :)
|
(: == ENTRY BY ID == :)
|
||||||
(: English as default language :)
|
(: English as default language :)
|
||||||
declare %rest:path("dict/{$id}")
|
declare %rest:path("dict/{$id}")
|
||||||
|
%rest:query-param("xpath", "{$xpath}")
|
||||||
%output:method("html")
|
%output:method("html")
|
||||||
%output:html-version('5')
|
%output:html-version('5')
|
||||||
function page:by-id($id) {
|
function page:by-id($id, $xpath) {
|
||||||
page:by-id('en',$id)
|
page:by-id('en', $id, $xpath)
|
||||||
};
|
};
|
||||||
|
|
||||||
declare %rest:path("{$lang}/dict/{$id}")
|
declare %rest:path("{$lang}/dict/{$id}")
|
||||||
|
%rest:query-param("xpath", "{$xpath}")
|
||||||
%output:method("html")
|
%output:method("html")
|
||||||
%output:html-version('5')
|
%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-of($page:sorted, $page:sorted[@xml:id=`{$id}`]) :)
|
||||||
let $doc-index := index-where($page:sorted, fn { ./@xml:id=$id })
|
let $doc-index := index-where($page:sorted, fn { ./@xml:id=$id })
|
||||||
let $pagenum := ceiling($doc-index div $page:items)
|
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">
|
<xsl:template match="abv:mark">
|
||||||
<mark>
|
<mark>
|
||||||
<xsl:next-match/>
|
<xsl:apply-templates/>
|
||||||
</mark>
|
</mark>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
|
@ -129,7 +129,8 @@
|
||||||
</xsl:element>
|
</xsl:element>
|
||||||
</xsl:template>
|
</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:element name="i">
|
||||||
<xsl:attribute name="class">abv-example</xsl:attribute>
|
<xsl:attribute name="class">abv-example</xsl:attribute>
|
||||||
<xsl:attribute name="lang" select="../@xml:lang"/>
|
<xsl:attribute name="lang" select="../@xml:lang"/>
|
||||||
|
|
Loading…
Add table
Reference in a new issue