Originallink: https://i18n.site/blog/tech/search
Nach mehreren Wochen der Entwicklung unterstützt i18n.site (ein rein statisches mehrsprachiges Markdown-Übersetzungs- und Website-Erstellungstool) jetzt die reine Front-End-Volltextsuche.
In diesem Artikel wird die technische Implementierung der reinen Front-End-Volltextsuche von i18n.site vorgestellt. Besuchen Sie i18n.site, um die Suchfunktion kennenzulernen.
Code ist Open-Source: Suchkernel / Interaktive Schnittstelle
Für kleine und mittlere rein statische Websites wie Dokumente/persönliche Blogs ist der Aufbau eines selbst erstellten Volltextsuche-Backends zu aufwändig, und eine dienstfreie Volltextsuche ist die häufigere Wahl.
Serverlose Volltextsuchlösungen sind in zwei Hauptkategorien unterteilt:
Beim ersten handelt es sich um Drittanbieter von Suchdiensten wie algolia.com, die Frontend-Komponenten für die Volltextsuche anbieten.
Solche Dienste erfordern eine Bezahlung basierend auf dem Suchvolumen und sind aufgrund von Compliance-Problemen für Benutzer auf dem chinesischen Festland häufig nicht verfügbar.
Sie können nicht offline oder in Intranets verwendet werden und unterliegen erheblichen Einschränkungen. In diesem Artikel wird nicht näher darauf eingegangen.
Die zweite Kategorie ist die reine Front-End-Volltextsuche.
Zu den derzeit gängigen reinen Front-End-Volltextsuchtools gehören lunrjs und ElasticLunr.js (eine sekundäre Entwicklung, die auf lunrjs basiert).
lunrjs verfügt über zwei Methoden zum Erstellen von Indizes, beide mit ihren eigenen Problemen.
Da der Index alle Wörter aus den Dokumenten enthält, ist er groß.
Jedes Mal, wenn ein Dokument hinzugefügt oder geändert wird, muss eine neue Indexdatei geladen werden.
Dies erhöht die Wartezeit der Benutzer und verbraucht eine erhebliche Menge an Bandbreite.
Das Erstellen eines Index ist eine rechenintensive Aufgabe, und die Neuerstellung bei jedem Zugriff kann zu spürbaren Verzögerungen führen, was zu einer schlechten Benutzererfahrung führt.
Neben lunrjs gibt es noch andere Volltextsuchlösungen, wie zum Beispiel:
fusejs, das sucht, indem es die Ähnlichkeit zwischen Zeichenfolgen berechnet.
Diese Lösung weist eine schlechte Leistung auf und ist nicht für die Volltextsuche geeignet (siehe Fuse.js. Lange Abfrage dauert mehr als 10 Sekunden, wie optimiert man sie?).
TinySearch, das einen Bloom-Filter für die Suche verwendet, kann keine Präfixsuchen durchführen (z. B. Eingabe von „goo“, um nach „Gut“ oder „Google“ zu suchen) und keinen automatischen Vervollständigungseffekt erzielen.
Aufgrund der Nachteile bestehender Lösungen hat i18n.site eine neue reine Front-End-Volltextsuchlösung mit den folgenden Funktionen entwickelt:
Details zur technischen Implementierung von i18n.site werden unten vorgestellt.
Wortsegmentierung verwendet den nativen Intl.Segmenter des Browsers, der von allen gängigen Browsern unterstützt wird.
Der Coffeescript-Code für die Wortsegmentierung lautet wie folgt:
SEG = new Intl.Segmenter 0, granularity: "word" seg = (txt) => r = [] for {segment} from SEG.segment(txt) for i from segment.split('.') i = i.trim() if i and !'|`'.includes(i) and !/\p{P}/u.test(i) r.push i r export default seg export segqy = (q) => seg q.toLocaleLowerCase()
Wo:
Fünf Objektspeichertabellen werden in IndexedDB erstellt:
Durch die Übergabe eines Arrays aus Dokument-URL und Versionsnummer ver wird die Dokumenttabelle auf die Existenz des Dokuments überprüft. Wenn er nicht vorhanden ist, wird ein invertierter Index erstellt. Gleichzeitig wird der invertierte Index für nicht übergebene Dokumente entfernt.
Diese Methode ermöglicht eine inkrementelle Indizierung und reduziert so die Rechenlast.
In the front-end interface, a progress bar for index loading can be displayed to avoid lag during the initial load. See "Animated Progress Bar, Based on a Single progress + Pure CSS Implementation" English / Chinese.
The project is developed based on the asynchronous encapsulation of IndexedDB, idb.
IndexedDB reads and writes are asynchronous. When creating an index, documents are loaded concurrently to build the index.
To avoid data loss due to concurrent writes, you can refer to the following coffeescript code, which adds a ing cache between reading and writing to intercept competitive writes.
`coffee
pusher = =>
ing = new Map()
(table, id, val)=>
id_set = ing.get(id)
if id_set
id_set.add val
return
id_set = new Set([val]) ing.set id, id_set pre = await table.get(id) li = pre?.li or [] loop to_add = [...id_set] li.push(...to_add) await table.put({id,li}) for i from to_add id_set.delete i if not id_set.size ing.delete id break return
rindexPush = pusher()
prefixPush = pusher()
`
To display search results in real-time as the user types, for example, showing words like words and work that start with wor when wor is entered.
The search kernel uses the prefix table for the last word after segmentation to find all words with that prefix and search sequentially.
An anti-shake function, debounce (implemented as follows), is used in the front-end interaction to reduce the frequency of searches triggered by user input, thus minimizing computational load.
js
export default (wait, func) => {
var timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(func.bind(this, ...args), wait);
};
}
The search first segments the keywords entered by the user.
Assuming there are N words after segmentation, the results are first returned with all keywords, followed by results with N-1, N-2, ..., 1 keywords.
The search results displayed first ensure query precision, while subsequent loaded results (click the "Load More" button) ensure recall.
To improve response speed, the search uses the yield generator to implement on-demand loading, returning results after each limit query.
Note that after each yield, a new IndexedDB query transaction must be opened for the next search.
To display search results in real-time as the user types, for example, showing words like words and work that start with wor when wor is entered.
The search kernel uses the prefix table for the last word after segmentation to find all words with that prefix and search sequentially.
An anti-shake function, debounce (implemented as follows), is used in the front-end interaction to reduce the frequency of searches triggered by user input, thus minimizing computational load.
js
export default (wait, func) => {
var timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(func.bind(this, ...args), wait);
};
}
The index table does not store the original text, only words, reducing storage space.
Highlighting search results requires reloading the original text, and using service worker can avoid repeated network requests.
Also, because service worker caches all articles, once a search is performed, the entire website, including search functionality, becomes offline available.
The pure front-end search solution provided by i18n.site is optimized for MarkDown documents.
When displaying search results, the chapter name is shown, and clicking navigates to that chapter.
The pure front-end implementation of inverted full-text search, without the need for a server, is very suitable for small to medium-sized websites such as documents and personal blogs.
i18n.site's open-source self-developed pure front-end search is compact, responsive, and addresses the various shortcomings of current pure front-end full-text search solutions, providing a better user experience.
Das obige ist der detaillierte Inhalt vonReine Front-End-invertierte Volltextsuche. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!