generated from ztimson/template
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a45d7bad8c | ||
|
|
0a7e87f6b7 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ztimson/zim-utils",
|
"name": "@ztimson/zim-utils",
|
||||||
"version": "0.4.1",
|
"version": "0.4.3",
|
||||||
"description": "Native, dependency-light ZIM archive reader/searcher and Kiwix catalog downloader for Node.js",
|
"description": "Native, dependency-light ZIM archive reader/searcher and Kiwix catalog downloader for Node.js",
|
||||||
"author": "Zak Timson",
|
"author": "Zak Timson",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
+1
-1
@@ -101,7 +101,7 @@ export function rerank(hits, termList) {
|
|||||||
const title = fieldScore(hit.title, termList, {fuzzy: true});
|
const title = fieldScore(hit.title, termList, {fuzzy: true});
|
||||||
const summary = fieldScore(hit.summary, termList);
|
const summary = fieldScore(hit.summary, termList);
|
||||||
const titleLower = (hit.title || '').toLowerCase();
|
const titleLower = (hit.title || '').toLowerCase();
|
||||||
const summaryLower = (hit.summary || '').toLowerCase();
|
const summaryLower = (hit.summary?._text || '').toLowerCase();
|
||||||
const phrase = termList.join(' ').toLowerCase();
|
const phrase = termList.join(' ').toLowerCase();
|
||||||
|
|
||||||
const titleExactPhrase = titleLower.includes(phrase) ? 1 : 0;
|
const titleExactPhrase = titleLower.includes(phrase) ? 1 : 0;
|
||||||
|
|||||||
+17
-24
@@ -212,7 +212,7 @@ export class KiwixServer {
|
|||||||
|
|
||||||
return makeArray(entries?.library?.book || []).map(e => {
|
return makeArray(entries?.library?.book || []).map(e => {
|
||||||
const tags = e.tags.split(';');
|
const tags = e.tags.split(';');
|
||||||
const name = e.path.replace('.zim', '');
|
const name = e.path.replace(/\.zim$/i, '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: e.id,
|
id: e.id,
|
||||||
@@ -262,53 +262,43 @@ export class KiwixServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #rawSearch(termList, scoped, bookMap, limit) {
|
async #rawSearch(termList, scoped, bookMap, limit) {
|
||||||
const groups = new Map();
|
const perBook = await Promise.all(scoped.map(async book => {
|
||||||
|
|
||||||
for(const book of scoped) {
|
|
||||||
const lang = book.language || '';
|
|
||||||
(groups.get(lang) ?? groups.set(lang, []).get(lang)).push(book);
|
|
||||||
}
|
|
||||||
|
|
||||||
const perGroup = await Promise.all([...groups.values()].map(async group => {
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
pattern: termList.join(' '),
|
pattern: termList.join(' '),
|
||||||
format: 'xml',
|
format: 'xml',
|
||||||
pageLength: String(limit),
|
pageLength: String(limit),
|
||||||
|
'books.name': book.href,
|
||||||
});
|
});
|
||||||
|
|
||||||
for(const book of group)
|
|
||||||
params.append('books.name', book.name);
|
|
||||||
|
|
||||||
const res = await fetch(`${this.baseUrl}/search?${params}`);
|
const res = await fetch(`${this.baseUrl}/search?${params}`);
|
||||||
if(!res.ok) return [];
|
if(!res.ok) return [];
|
||||||
|
|
||||||
const found = fromXml(await res.text())?.rss?.channel?.item || [];
|
const found = fromXml(await res.text())?.rss?.channel?.item || [];
|
||||||
|
|
||||||
return found.map(hit => {
|
return makeArray(found).map(hit => {
|
||||||
const book = bookMap.get(hit.book.title);
|
const resultBook = bookMap.get(hit.book?.title) || book;
|
||||||
if(!book) return null;
|
|
||||||
|
|
||||||
const prefix = `/content/${book.href}/`;
|
const prefix = `/content/${resultBook.href}/`;
|
||||||
const page = hit.link.startsWith(prefix)
|
const page = hit.link.startsWith(prefix)
|
||||||
? hit.link.slice(prefix.length)
|
? hit.link.slice(prefix.length)
|
||||||
: hit.link.replace(/^\/+/, '');
|
: hit.link.replace(/^\/+/, '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: book.id,
|
id: resultBook.id,
|
||||||
title: hit.title,
|
title: hit.title,
|
||||||
page,
|
page,
|
||||||
name: book.name,
|
name: resultBook.name,
|
||||||
publisher: book.publisher,
|
publisher: resultBook.publisher,
|
||||||
href: `${book.href}/${page}`,
|
href: `${resultBook.href}/${page}`,
|
||||||
icon: book.icon,
|
icon: resultBook.icon,
|
||||||
viewer: this.baseUrl + hit.link,
|
viewer: this.baseUrl + hit.link,
|
||||||
summary: hit.description,
|
summary: hit.description,
|
||||||
xapianScore: +hit.score || 0,
|
xapianScore: +hit.score || 0,
|
||||||
};
|
};
|
||||||
}).filter(Boolean).sort((a, b) => b.xapianScore - a.xapianScore);
|
}).filter(Boolean);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return rrfMerge(perGroup);
|
return rrfMerge(perBook);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fulltext search across local ZIMs with ranking, diversification and spelling correction. */
|
/** Fulltext search across local ZIMs with ranking, diversification and spelling correction. */
|
||||||
@@ -320,6 +310,7 @@ export class KiwixServer {
|
|||||||
|
|
||||||
const books = await this.list();
|
const books = await this.list();
|
||||||
const bookMap = new Map(books.map(b => [b.title, b]));
|
const bookMap = new Map(books.map(b => [b.title, b]));
|
||||||
|
|
||||||
const scoped = sources?.length
|
const scoped = sources?.length
|
||||||
? books.filter(b => sources.includes(b.name) || sources.includes(b.href))
|
? books.filter(b => sources.includes(b.name) || sources.includes(b.href))
|
||||||
: books;
|
: books;
|
||||||
@@ -360,8 +351,10 @@ export class KiwixServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ranked = rerank(hits, activeTerms);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
results: diversify(rerank(hits, activeTerms), limit),
|
results: diversify(ranked, limit),
|
||||||
spellcheck,
|
spellcheck,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user