Verbesserung der Suche vorhandener Dokumente
This commit is contained in:
105
lib/client.js
105
lib/client.js
@@ -183,16 +183,113 @@ export class PaperlessClient {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dokumente suchen. Vier Wege, in dieser Reihenfolge:
|
||||||
|
*
|
||||||
|
* 1. Archiv-Seriennummer, wenn die Eingabe nur aus Ziffern besteht.
|
||||||
|
* Sie ist ein eigenes Feld - die Volltextsuche findet sie NICHT.
|
||||||
|
*
|
||||||
|
* 2. Die GLOBALE SUCHE von Paperless (/api/search/). Dieselbe
|
||||||
|
* Schnittstelle, die die Suchleiste oben in der Weboberfläche
|
||||||
|
* bedient. Sie verbindet Titelsuche und Volltext und liefert
|
||||||
|
* damit die Ergebnisse, die man aus der Oberfläche kennt.
|
||||||
|
*
|
||||||
|
* 3. Teilzeichenkette in Titel und Inhalt. Greift, wenn die globale
|
||||||
|
* Suche nichts liefert oder in der eingesetzten Version fehlt.
|
||||||
|
*
|
||||||
|
* 4. Volltextsuche über den Index, mit UND-Verknüpfung. Sie
|
||||||
|
* beherrscht Wortstämme und findet auch Beugungsformen.
|
||||||
|
*
|
||||||
|
* Zwei Fallstricke, die hier berücksichtigt sind:
|
||||||
|
*
|
||||||
|
* * Bei der Volltextsuche darf KEIN ordering mitgegeben werden.
|
||||||
|
* Paperless sortiert dort nach Relevanz; eine eigene
|
||||||
|
* Sortierangabe führte dazu, dass die Suchanfrage verworfen wurde
|
||||||
|
* und schlicht die zuletzt hinzugefügten Dokumente kamen.
|
||||||
|
*
|
||||||
|
* * Ohne UND-Verknüpfung sucht der Index mit ODER. "Anschreiben
|
||||||
|
* Markus Straub" lieferte dann alles, was irgendeines der drei
|
||||||
|
* Wörter enthält - der gesuchte Treffer ging darin unter.
|
||||||
|
*/
|
||||||
async search(query, limit = 50) {
|
async search(query, limit = 50) {
|
||||||
|
const q = String(query || "").trim();
|
||||||
|
|
||||||
|
if (!q) return this.#suchlauf({ ordering: "-added" }, limit);
|
||||||
|
|
||||||
|
// 1. Seriennummer
|
||||||
|
if (/^\d+$/.test(q)) {
|
||||||
|
const asn = await this.#suchlauf({ archive_serial_number: q }, limit);
|
||||||
|
if (asn.length) return asn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Globale Suche wie in der Weboberfläche
|
||||||
|
const global = await this.#globaleSuche(q, limit);
|
||||||
|
if (global.length) return global;
|
||||||
|
|
||||||
|
// 3. Titel und Inhalt
|
||||||
|
const text = await this.#suchlauf(
|
||||||
|
{ title_content: q, ordering: "-created" }, limit);
|
||||||
|
if (text.length) return text;
|
||||||
|
|
||||||
|
// 4. Volltext mit UND, ohne ordering.
|
||||||
|
//
|
||||||
|
// Nach dem Ersetzen der Sonderzeichen ERNEUT zerlegen: Aus
|
||||||
|
// "Müller-Lüdenscheidt" würde sonst ein Wort mit Leerzeichen, und
|
||||||
|
// die UND-Verknüpfung wäre wieder aufgehoben.
|
||||||
|
const worte = q.replace(/[+\-&|!(){}[\]^"~*?:\\/]/g, " ")
|
||||||
|
.split(/\s+/).filter(Boolean);
|
||||||
|
if (!worte.length) return [];
|
||||||
|
return this.#suchlauf({ query: worte.join(" AND ") }, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die Schnittstelle hinter der Suchleiste der Weboberfläche.
|
||||||
|
*
|
||||||
|
* Sie gibt es erst ab neueren Paperless-Ständen, und die Form der
|
||||||
|
* Antwort ist nicht überall gleich - mal ein Feld "documents", mal
|
||||||
|
* "results". Deshalb wird beides angenommen und bei jedem Fehler
|
||||||
|
* stillschweigend auf den nächsten Weg ausgewichen.
|
||||||
|
*/
|
||||||
|
async #globaleSuche(q, limit) {
|
||||||
|
let antwort;
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams({ query: q,
|
||||||
|
page_size: String(limit) });
|
||||||
|
antwort = await this.#json(`search/?${params}`);
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (!antwort) return [];
|
||||||
|
|
||||||
|
for (const schluessel of ["documents", "results"]) {
|
||||||
|
const rows = antwort[schluessel];
|
||||||
|
if (Array.isArray(rows) && rows.length) {
|
||||||
|
// Die globale Suche liefert je nach Version verschiedene
|
||||||
|
// Objektarten. Nur echte Dokumente durchlassen.
|
||||||
|
return rows
|
||||||
|
.filter((r) => r && typeof r === "object"
|
||||||
|
&& r.id !== undefined && r.title !== undefined)
|
||||||
|
.slice(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async #suchlauf(zusatz, limit) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
page_size: String(limit),
|
page_size: String(limit),
|
||||||
ordering: "-added",
|
// Ohne diese Einschränkung liefert Paperless je Treffer auch den
|
||||||
fields: "id,title,created,added,archive_serial_number,mime_type," +
|
// vollständigen Inhalt - bei fünfzig Treffern mehrere Megabyte.
|
||||||
"original_file_name"
|
fields: "id,title,created,added,archive_serial_number,mime_type,"
|
||||||
|
+ "original_file_name"
|
||||||
});
|
});
|
||||||
if (query) params.set("query", query);
|
for (const [k, v] of Object.entries(zusatz)) params.set(k, String(v));
|
||||||
|
try {
|
||||||
const data = await this.#json(`documents/?${params}`);
|
const data = await this.#json(`documents/?${params}`);
|
||||||
return data?.results || [];
|
return data?.results || [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document(id) {
|
document(id) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "__MSG_extName__",
|
"name": "__MSG_extName__",
|
||||||
"description": "__MSG_extDescription__",
|
"description": "__MSG_extDescription__",
|
||||||
"version": "1.14.3",
|
"version": "1.16.0",
|
||||||
"author": "Marco Morath",
|
"author": "Marco Morath",
|
||||||
"homepage_url": "https://gitea.cloud.mmorath.de/marco.morath/thunderbird-paperless-connector",
|
"homepage_url": "https://gitea.cloud.mmorath.de/marco.morath/thunderbird-paperless-connector",
|
||||||
"default_locale": "de",
|
"default_locale": "de",
|
||||||
|
|||||||
Reference in New Issue
Block a user