Groovy-Skripting-Leitfaden

Übersicht

Groovy ist eine der in Fess verfügbaren Skriptsprachen. Sie läuft auf der Java Virtual Machine (JVM) und ermöglicht es Ihnen, bei hoher Kompatibilität mit Java Skripte mit einer prägnanteren Syntax zu schreiben.

Wichtig

Ab Fess 15.9 wird Groovy als fess-script-groovy-Plugin bereitgestellt, und das Plugin ist nicht Teil der Distribution. Installieren Sie es über die Seite System > Plugin in der Administrationsoberfläche oder mit bin/fess-setup install plugin fess-script-groovy. Für eine manuelle Installation legen Sie die entsprechende JAR-Datei (Beispiel: fess-script-groovy-15.9.0.jar) im Verzeichnis app/WEB-INF/plugin/ ab und starten Fess nach der Installation neu.

Die Standard-Skriptsprache ist ab Fess 15.9 JavaScript. Eine Skript-Konfiguration ohne hinterlegten Skripttyp wird als Groovy behandelt, sodass eine vor 15.9 erstellte Konfiguration mit ihrem Groovy-Skript nach dem Upgrade unverändert weiterläuft. Wenn Sie ab 15.9 eine neue Konfiguration mit Groovy anlegen, setzen Sie den Skripttyp explizit auf groovy. Für JavaScript siehe JavaScript-Skripting-Leitfaden.

Grundlegende Syntax

Variablendeklaration

// Typinferenz (def)
def name = "Fess"
def count = 100

// Explizite Typangabe
String title = "Dokumenttitel"
int pageNum = 1

Zeichenkettenoperationen

// Zeichenketteninterpolation (GString)
def id = 123
def url = "https://example.com/doc/${id}"

// Mehrzeilige Zeichenketten
def content = """
Dies ist eine
mehrzeilige Zeichenkette
"""

// Ersetzung
title.replace("alt", "neu")
title.replaceAll(/\s+/, " ")  // Regulaerer Ausdruck

// Teilen und Verbinden
def tags = "tag1,tag2,tag3".split(",")
def joined = tags.join(", ")

// Gross-/Kleinschreibung aendern
title.toUpperCase()
title.toLowerCase()

Collection-Operationen

// Listen
def list = [1, 2, 3, 4, 5]
list.each { println it }
def doubled = list.collect { it * 2 }
def filtered = list.findAll { it > 3 }

// Maps
def map = [name: "Fess", version: "15.9"]
println map.name
println map["version"]

Bedingte Verzweigung

// if-else
if (data.status == "active") {
    return "Aktiv"
} else {
    return "Inaktiv"
}

// Ternaerer Operator
def result = data.count > 0 ? "Vorhanden" : "Keine"

// Elvis-Operator (Null-Koaleszenz-Operator)
def value = data.title ?: "Ohne Titel"

// Sichere Navigationsoperator
def length = data.content?.length() ?: 0

Schleifenverarbeitung

// for-each
for (item in items) {
    println item
}

// Closure
items.each { item ->
    println item
}

// Bereich
(1..10).each { println it }

Datenspeicher-Skripte

Beispiele fuer Skripte zur Datenspeicher-Konfiguration.

Bemerkung

In Datenspeicher-Skripten wird jede Feldname=Ausdruck-Zeile unabhaengig als einzelner Ausdruck ausgewertet. Daher koennen import-Anweisungen, mehrzeilige def-Deklarationen und mehrzeilige Kontrollstrukturen, die mehrere Felder gleichzeitig setzen (z. B. if-Bloecke), nicht verwendet werden. Wenn Sie Java-Klassen verwenden, schreiben Sie diese als einzelnen Ausdruck mit vollstaendig qualifiziertem Klassennamen (FQCN), und verwenden Sie fuer bedingte Werte den Ternaeroperator pro Feld (zum Beispiel url=data.published ? data.url : null ). Der hier verwendete Variablenname data ist nur ein Beispiel; der tatsaechliche Variablenname haengt vom verwendeten Datenspeicher-Konnektor ab. Details finden Sie unter Datenspeicher-Crawl.

Grundlegendes Mapping

url=data.url
title=data.title
content=data.content
lastModified=data.updated_at

URL-Generierung

// URL-Generierung basierend auf ID
url="https://example.com/article/" + data.id

// Kombination mehrerer Felder
url="https://example.com/" + data.category + "/" + data.slug + ".html"

// Bedingte URL
url=data.external_url ?: "https://example.com/default/" + data.id

Inhaltsverarbeitung

// HTML-Tags entfernen
content=data.html_content.replaceAll(/<[^>]+>/, "")

// Mehrere Felder kombinieren
content=data.title + "\n" + data.description + "\n" + data.body

// Laengenbeschraenkung
content=data.content.length() > 10000 ? data.content.substring(0, 10000) : data.content

Datumsverarbeitung

// Datum parsen (Einzelausdruck mit FQCN)
lastModified=new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(data.date_string)

// Konvertierung von Epochensekunden
lastModified=new Date(data.timestamp * 1000L)

Verfuegbare Objekte

Die in Skripten verfuegbaren Objekte variieren je nach Ausfuehrungskontext.

Kontext Objekt Beschreibung
Alle Kontexte container DI-Container. Wird fuer den Zugriff auf Komponenten ueber container.getComponent("...") verwendet
Geplante Aufgaben executor Job-Ausfuehrungssteuerung ( JobExecutor ). Erforderlich fuer die Unterstuetzung des Jobstopps
Datenspeicher (Connector-spezifisch) Von jedem Datenspeicher bereitgestellte Datensatzvariablen. Der Variablenname haengt vom Konnektor ab
Pfad-Mapping url , matcher Die zu konvertierende URL-Zeichenkette und das Ergebnis des Regulaerausdruck-Abgleichs ( Matcher ). Verfuegbar in Ersetzungseinstellungen mit dem Praefix groovy:
Dokument-Boost (Dokumentfelder) Jedes Feld des Zieldokuments ist als Variable verfuegbar (wird in Bedingungs- und Boost-Wert-Ausdruecken verwendet)

Geplante Aufgaben-Skripte

Beispiele fuer Groovy-Skripte in geplanten Aufgaben. In geplanten Aufgaben sind container und executor verfuegbar. Durch Uebergabe von executor an die execute()-Methode des Jobs wird die Jobstoppsteuerung aktiviert.

Bemerkung

Ein geplantes Aufgaben-Skript wird als vollstaendiges Groovy-Skript in einem einzigen Durchlauf ausgewertet. Daher koennen Sie im Gegensatz zu Datenspeicher-Skripten import-Anweisungen, mehrzeilige def-Deklarationen und mehrzeilige Kontrollstrukturen verwenden. Die nachfolgenden Beispiele unter „Java-Klassen verwenden“, „Zugriff auf Fess-Komponenten“, „Fehlerbehandlung“ und „Debugging und Protokollausgabe“ setzen ebenfalls diesen vollstaendigen Skript-Kontext voraus.

Crawl-Aufgabe ausfuehren

return container.getComponent("crawlJob").logLevel("info").gcLogging().execute(executor);

Bedingtes Crawling

import java.util.Calendar

def cal = Calendar.getInstance()
def hour = cal.get(Calendar.HOUR_OF_DAY)

// Nur ausserhalb der Geschaeftszeiten crawlen
if (hour < 9 || hour >= 18) {
    return container.getComponent("crawlJob").logLevel("info").gcLogging().execute(executor)
}
return "Waehrend der Geschaeftszeiten uebersprungen"

Mehrere Aufgaben nacheinander ausfuehren

def results = []

// Suggest aktualisieren
results << container.getComponent("suggestJob").logLevel("info").sessionId("SUGGEST").execute(executor)

// Crawl ausfuehren
results << container.getComponent("crawlJob").logLevel("info").gcLogging().execute(executor)

return results.join("\n")

Java-Klassen verwenden

Innerhalb von Groovy-Skripten koennen Sie Java-Standardbibliotheken und Fess-Klassen verwenden.

Datum und Uhrzeit

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

def now = LocalDateTime.now()
def formatted = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)

Dateioperationen

import java.nio.file.Files
import java.nio.file.Paths

def content = new String(Files.readAllBytes(Paths.get("/pfad/zur/datei.txt")))

HTTP-Kommunikation

import java.net.URL

def url = new URL("https://api.example.com/data")
def response = url.text

Warnung

Der Zugriff auf externe Ressourcen beeintraechtigt die Leistung, halten Sie ihn daher auf ein Minimum.

Zugriff auf Fess-Komponenten

Sie koennen mit container auf Fess-Komponenten zugreifen.

System-Helfer

def systemHelper = container.getComponent("systemHelper")
def currentTime = systemHelper.getCurrentTimeAsLong()

Konfigurationswerte abrufen

def fessConfig = container.getComponent("fessConfig")
def indexName = fessConfig.getIndexDocumentUpdateIndex()

Suchen ausfuehren

def searchHelper = container.getComponent("searchHelper")
// Suchparameter festlegen und Suche ausfuehren

Fehlerbehandlung

import-Anweisungen muessen am Anfang des Skripts stehen (sie koennen nicht innerhalb von Bloecken wie try-catch platziert werden). Mit try-catch koennen Ausnahmen abgefangen und Job-Fehler gesteuert werden.

import org.apache.logging.log4j.LogManager

def logger = LogManager.getLogger("script")

try {
    return container.getComponent("crawlJob").logLevel("info").gcLogging().execute(executor)
} catch (Exception e) {
    logger.error("Failed to execute crawl job: {}", e.message, e)
    return "Error: " + e.message
}

Debugging und Protokollausgabe

Protokollausgabe

import org.apache.logging.log4j.LogManager

def logger = LogManager.getLogger("script")

logger.debug("Debug message: {}", value)
logger.info("Processing: {}", title)
logger.warn("Warning: {}", message)
logger.error("Error: {}", e.message, e)

Debug-Ausgabe

// Konsolenausgabe (nur Entwicklung)
println "data.id = ${data.id}"
println "data.title = ${data.title}"

Best Practices

  1. Einfach halten: Komplexe Logik vermeiden und lesbaren Code schreiben

  2. Null-Pruefungen: ?. und ?: Operatoren nutzen

  3. Ausnahmebehandlung: Unerwartete Fehler mit geeignetem try-catch behandeln

  4. Protokollausgabe: Protokolle fuer einfacheres Debugging ausgeben

  5. Leistung: Zugriff auf externe Ressourcen minimieren

Referenzinformationen