Übersicht
JavaScript ist ab Fess 15.9 die Standard-Skriptsprache. Es läuft auf Sai (einem Nashorn-Fork von CodeLibs, den Fess bereits für DI-XML-Ausdrücke verwendet), und Skripte werden als ECMAScript 6 ausgeführt. Der Bezeichner lautet javascript; alternativ können auch die Aliase js und sai verwendet werden.
Wie Skripte ausgewertet werden
Die Skript-Engine von Fess versucht zunächst, den Skripttext als einzelnen „Ausdruck“ zu kompilieren. Nur wenn dies syntaktisch fehlschlägt, wird der Text erneut als Block von „Anweisungen“ kompiliert.
Deshalb funktioniert sowohl ein einfacher Ausdruck, der lediglich einen Wert zurückgibt:
als auch ein Skript mit einer return-Anweisung auf oberster Ebene:
Letzteres wäre in reinem JavaScript normalerweise ein Syntaxfehler, da ein return auf oberster Ebene nicht zulässig ist. Da es sich jedoch nicht als Ausdruck kompilieren lässt, wird es als Anweisungsblock neu interpretiert und als gültiges Skript ausgeführt.
An Stellen, an denen jede Zeile als einzelner Ausdruck behandelt wird — etwa bei Datenspeicher-Skripten — kann ein Skript aus mehreren Anweisungen nicht verwendet werden. An Stellen, an denen das gesamte Skript ausgewertet wird — etwa bei geplanten Aufgaben — können Sie dagegen frei mehrzeilige Anweisungen, let / const-Variablendeklarationen und Kontrollstrukturen verwenden.
Warnung
Ein Skript, das als Anweisungsblock kompiliert wird, liefert nur dann einen Wert zurück, wenn es ein explizites return enthält. Lässt sich der Text nicht als Ausdruck parsen, wird er in eine Funktion eingebettet und als Block von Anweisungen ausgeführt — und ein Block ohne return wird zu null ausgewertet. Ein einziges abschließendes Semikolon genügt, um diese Grenze zu überschreiten:
| Skript | Ergebnis | Grund |
|---|---|---|
content.length() | 11 | Wird als Ausdruck geparst; der Wert des Ausdrucks ist das Ergebnis |
content.length(); | null | Wird nur als Anweisungsblock geparst, der kein return enthält |
var x = 1; x + 2 | null | Wird nur als Anweisungsblock geparst, der kein return enthält |
Unter Groovy lieferten alle drei einen Wert, da dort der Wert der zuletzt ausgewerteten Anweisung der Rückgabewert des Skripts ist. In JavaScript gibt es diese Regel nicht.
Dies ist der einzige Unterschied bei der Migration, der weder einen Fehler noch eine Logzeile erzeugt und dessen einziges Symptom ein still leer bleibendes Feld ist: Ein Datenspeicher-Mapping, dessen Skript null zurückgibt, setzt dieses Feld schlicht nicht. Schreiben Sie jede Feldname=Ausdruck-Zeile eines Datenspeichers als reinen Ausdruck ohne abschließendes Semikolon, und geben Sie jedem Skript einer geplanten Aufgabe ein explizites return.
Grundlegende Syntax
Eine Zeile ohne abschließendes Semikolon ist im Folgenden ein Ausdruck und kann überall verwendet werden, auch in einer Feldname=Ausdruck-Zeile eines Datenspeichers. Deklarationen ( let / const ), if-Blöcke und Schleifen sind Anweisungen: Sie können nur dort verwendet werden, wo das gesamte Skript ausgewertet wird, etwa in einer geplanten Aufgabe, und das Skript muss ein explizites return enthalten, um einen Wert zu liefern. Siehe „Wie Skripte ausgewertet werden“ oben.
Variablendeklaration
Zeichenkettenoperationen
Collection-Operationen
Bedingte Verzweigung
Schleifenverarbeitung
Datenspeicher-Skripte
Beispiele für Skripte zur Datenspeicher-Konfiguration.
Bemerkung
In Datenspeicher-Skripten wird jede Feldname=Ausdruck-Zeile unabhängig als einzelner Ausdruck ausgewertet. Daher können Variablendeklarationen wie let / const und mehrzeilige Kontrollstrukturen, die mehrere Felder gleichzeitig setzen (z. B. if-Blöcke), nicht verwendet werden. Wenn Sie Java-Klassen verwenden, schreiben Sie diese als einzelnen Ausdruck mit vollständig qualifiziertem Klassennamen (FQCN), und verwenden Sie für bedingte Werte den Ternäroperator pro Feld (zum Beispiel url=data.published ? data.url : null ). Der hier verwendete Variablenname data ist nur ein Beispiel; der tatsächliche Variablenname hängt vom verwendeten Datenspeicher-Konnektor ab. Details finden Sie unter Datenspeicher-Crawl. Schreiben Sie den Ausdruck ohne abschließendes Semikolon: Eine Zeile, die nur als Anweisungsblock geparst werden kann, wird zu null ausgewertet und das Feld bleibt ungesetzt — siehe Wie Skripte ausgewertet werden.
Grundlegendes Mapping
URL-Generierung
Inhaltsverarbeitung
Datumsverarbeitung
Verfügbare Objekte
Die in Skripten verfügbaren Objekte variieren je nach Ausführungskontext.
| Kontext | Objekt | Beschreibung |
|---|---|---|
| Alle Kontexte | container | DI-Container. Wird für den Zugriff auf Komponenten über container.getComponent("...") verwendet |
| Geplante Aufgaben | executor | Job-Ausführungssteuerung ( JobExecutor ). Erforderlich für die Unterstützung des Jobstopps |
| Datenspeicher | (Connector-spezifisch) | Von jedem Datenspeicher bereitgestellte Datensatzvariablen. Der Variablenname hängt vom Konnektor ab |
| Pfad-Mapping | url , matcher | Die zu konvertierende URL-Zeichenkette und das Ergebnis des Regulärausdruck-Abgleichs ( Matcher ). Verfügbar, wenn die Ersetzung mit dem Namen einer registrierten Engine vorangestellt ist, z. B. javascript: (Aliase js:, sai:) |
| Dokument-Boost | (Dokumentfelder) | Jedes Feld des Zieldokuments ist als Variable verfügbar (wird in Bedingungs- und Boost-Wert-Ausdrücken verwendet) |
Geplante Aufgaben-Skripte
Beispiele für JavaScript-Skripte in geplanten Aufgaben. In geplanten Aufgaben sind container und executor verfügbar. Durch Übergabe von executor an die execute()-Methode des Jobs wird die Jobstoppsteuerung aktiviert.
Bemerkung
Ein geplantes Aufgaben-Skript wird als vollständiges Skript in einem einzigen Durchlauf ausgewertet. Die Skript-Engine versucht zunächst, es als Ausdruck zu kompilieren, und interpretiert es nur bei einem Fehlschlag als Block von Anweisungen neu. Daher können mehrzeilige Anweisungen, let / const-Deklarationen, Kontrollstrukturen und eine return-Anweisung auf oberster Ebene verwendet werden (Details siehe „Wie Skripte ausgewertet werden“ oben). Die nachfolgenden Beispiele unter „Java-Klassen verwenden“, „Zugriff auf Fess-Komponenten“, „Fehlerbehandlung“ und „Debugging und Protokollausgabe“ setzen ebenfalls diesen vollständigen Skript-Kontext voraus.
Crawl-Aufgabe ausführen
Bedingtes Crawling
Mehrere Aufgaben nacheinander ausführen
Java-Klassen verwenden
Innerhalb von JavaScript-Skripten können Sie dank der Java-Interoperabilität von Sai (Nashorn) Java-Standardbibliotheken und Fess-Klassen direkt verwenden. JavaScript kennt keine import-Anweisung, daher werden Klassen stets mit ihrem vollständig qualifizierten Namen (FQCN) geschrieben.
Datum und Uhrzeit
Dateioperationen
HTTP-Kommunikation
Warnung
Der Zugriff auf externe Ressourcen beeinträchtigt die Leistung, halten Sie ihn daher auf ein Minimum.
Zugriff auf Fess-Komponenten
Sie können mit container auf Fess-Komponenten zugreifen.
System-Helfer
Konfigurationswerte abrufen
Suchen ausführen
Fehlerbehandlung
JavaScript kennt keine import-Anweisung, sodass die Platzierungsregeln von Groovy hier keine Rolle spielen. Mit try-catch können Ausnahmen abgefangen und Job-Fehler gesteuert werden.
Debugging und Protokollausgabe
Protokollausgabe
Debug-Ausgabe
Um den Inhalt einer Variablen schnell zu überprüfen, wandeln Sie sie mit JSON.stringify in eine Zeichenkette um und protokollieren Sie diese.
Migration von Groovy
Beachten Sie beim Portieren eines vorhandenen Groovy-Skripts nach JavaScript die folgenden Unterschiede.
Genauigkeit arithmetischer Operationen
Zahlenoperationen in JavaScript arbeiten immer mit doppelter Gleitkommagenauigkeit. Der folgende Ausdruck liefert beispielsweise in Groovy die Ganzzahl 34, in JavaScript jedoch die Gleitkommazahl 34.0.
Der Rückgabetyp einer über die Java-Interoperabilität aufgerufenen Methode bleibt dagegen unverändert Java-seitig erhalten, sodass content.length() weiterhin eine Ganzzahl liefert.
Groovy-spezifische Syntax umschreiben
Die folgende Groovy-spezifische Syntax muss für JavaScript umgeschrieben werden.
| Groovy | JavaScript | Beschreibung |
|---|---|---|
1000L | 1000 | Der L-Suffix für long-Literale entfällt; die Zahl wird unverändert geschrieben |
["a", "b"] as String[] | ["a", "b"] | Ein JavaScript-Array wird beim Übergeben an eine Methode mit Parametertyp |
Java-Interoperabilität
Die Notation für die Java-Interoperabilität entspricht der von Nashorn und unterscheidet sich kaum von Groovy. Vollständig qualifizierte Konstruktoraufrufe wie new java.io.File(...), java.lang.System.getProperty(...) und new org.codelibs.fess.job.IndexExportJob() werden unverändert aufgelöst.
ES6-Syntax
Da die JavaScript-Engine von Fess als ECMAScript 6 läuft, können Sie ES6-Syntax wie let / const, Arrow-Funktionen, Template-Literale, Destrukturierung, for...of und class verwenden. Optional Chaining (?.) und der Nullish-Coalescing-Operator (??) sind jedoch Syntax ab ES2020 und stehen nicht zur Verfügung.
Best Practices
Einfach halten: Komplexe Logik vermeiden und lesbaren Code schreiben
Standardwerte: Anstelle des Elvis-Operators den logischen OR-Operator (
||) verwendenAusnahmebehandlung: Unerwartete Fehler mit geeignetem try-catch behandeln
Protokollausgabe: Protokolle für einfacheres Debugging ausgeben
Leistung: Zugriff auf externe Ressourcen minimieren
Zahlenoperationen: Wo eine Ganzzahl erwartet wird, das Ergebnis eines Java-Interop-Methodenaufrufs direkt verwenden oder bei Bedarf explizit konvertieren
Referenzinformationen
Skripting-Übersicht - Skripting-Übersicht
Groovy-Skripting-Leitfaden - Groovy-Skripting-Leitfaden (Plugin)
Datenspeicher-Crawl - Datenspeicher-Konfigurationsleitfaden
Scheduler - Scheduler-Konfigurationsleitfaden