Indexierer

Indexierung

Indexierung als Verfahren ist deutlich älter als das Internet, und nicht wirklich kompliziert: Dokumenten werden zum Inhalt passende Schlag- und Stichwörter zugewiesen, unter denen sie in einem "Index" (=Verzeichnis) zu finden sind. Archive und Bibliotheken arbeiten seit Jahrhunderten mit Indexen.

Eine automatische Indexierung mittels Software, generiert die Schlagworte aus dem Text / Dokument heraus. Neben den Schlagworten ( =Deskriptoren) beachten Indexierer auch die Häufigkeit des Vorkommens eines Schlagwortes (= Gewichtung). Eine Suchmaschine, wie die allwissende Müllhalde names Google, erstellt permanent und endlos einen Index der Dokumente im Web. Eine Suchanfrage bewirkt ein Durchsuchen des Indexes nach den Begriffen aus der Suchanfrage.

In meiner Zeit am RUS habe ich wie in einem anderen Artikel beschrieben, einen der ersten Web Server in Deutschland aufgesetzt. Ein Content des Servers war die Online Version der "RUS BI" (Benutzer Information). Ich weiß noch das ich die HTML Versionen aus einer *.rtf Datei des Textverarbeitungsprogrammes generiert habe.

Für den ganzen RUS Web Server und das BI Archiv habe ich dann 1993 oder 1994 eine Suchfunktion implementiert - mittels eines "Information Retrieval System" (heute = Search Engine) namens HARVEST. HARVEST war in C und PERL geschrieben, es stammte von irgend einer Uni aus USA , und die Doku war LaTeX (!!) geschrieben. Das damalige Logo sagt viel ...

Im Prinzip war alles da, was später Google groß gemacht hat - allerdings im universitären Umfeld, und ohne jeden kommerziellen Gedanken. Ich hab die Suchmaschine auch auf der ersten Web Site von Daimler-Benz installiert, und musste damals dazu extra nach Untertürkheim ins Werk fahren. Als Indexierer war HARVEST brauchbar, und ich habe viel über die Auswahl von Deskriptoren, simple Textanalyse, Volltextsuche, Stoppwörter und den Aufbau eines Indexes gelernt.

30 Jahre später gibt es HARVEST nicht mehr, C ist aus der Mode gekommen. DAS Open Source Projekt zum Thema Indexerer und Volltextsuche ist Apache's Lucene, es ist der de facto Standard und wird von Wikipedia und Twitter verwendet. Der Kern des Projekts Lucene Core früher auch Lucene Java genannt, ist eine JAVA Programmbibliothek. Mit ihr kann man einen Index für eine große Anzahl an Dateien erstellen. Als Resultat der anschliesenden Suchen gibt es Ergebnis-Ranglisten, und mehrere Suchalgorithmen stehen zur Auswahl.

Das Arbeiten mit den jar File war nicht ganz straight forward, man braucht doch in Summe fünf *.jar Files:


A closer look

An der Stelle sind doch noch ein paar Worte zu Prinzip eines Indexierers fällig, sonst bleibt der Code unverständlich.

Invertierte Liste

Ein Dokument besteht aus einzelnen Wörtern. Es wird unter einem Namen abgespreichert. Eine "Suche" sucht aber nicht nach Dokumentennamen, sondern nach Wörtern in einem Dokument. Also "invertiert" man die Verwaltung: Man speichert zu jedem Wort alle Dokumente ab, die dieses Wort enthalten. Dazu wird zu jedem Wort eine Liste der Dokumente benutzt, die dieses Wort enthalten: die invertierte Liste (invertierter Index).

Reduzierung Worte

Für die Suche nach Inhalten sind nicht alle Wörter eines Dokumentes nötig. Daher werden vor dem Erzeigen der invertierten Liste ein paar Schritte durchgeführt:
  1. Es werden alle die "Worte" eleminiert die faktisch keine Sematik tragen, sog. "Stoppworte". Gängige Stoppwörter in deutschsprachigen Dokumenten sind
    • bestimmte Artikel ('der', 'die', 'das')
    • unbestimmte Artikel ('einer', 'eine', 'ein')
    • Konjunktionen (z. B. 'und', 'oder', 'doch', 'weil')
    • häufig gebrauchte Präpositionen (z. B. 'an', 'in', 'von')
    • sowie die Negation 'nicht' und Satzzeichen
    In der freien Software-Bibliothek NLTK sind Listen von Stoppwörtern für 21 Sprachen sowie fertige Methoden zu deren Benutzung enthalten.
  2. Mit Thesauri werden Synonyme und Homonyme behandelt
  3. Grund- und Stammformreduktion: Wörter werden auf ihre grammatikalische Grundform zurückgeführt
    • Substantive auf den Nominativ : Häuser->Haus
    • Verben auf den Infinitiv: erprobte->erproben
Diese Aufgaben stellen sich leider sprachabhängig dar, sie sind im Griechischen anders als im Englischen.

Indexieren mit Lucene

Der Lucene Indexierer kann "out of the box" verwendet werden, die API ist recht klar strukturiert. Einge spezielle Objekte müssen angelegt werden, und dann kann man schon erste Dateien zu einem Index hinzufügen. Zu beachten ist, daß Lucene nur Klartext akzeptiert. Was auch immer der Dokumententyp sein mag - ob XML, HTML oder PDF - die Dokumente müssen als Textdatei vorliegen, bevor Sie sie an Lucene weitergeben werden können.
        import org.apache.lucene.*;
        ...
        String      indexPath       = "/Users/eathanassiou/myWWW/prod/Index";   // Zielverzeichnis für den Index
        Directory   directory       = FSDirectory.open(Paths.get(indexPath));
        IndexWriterConfig config    = new IndexWriterConfig(new StandardAnalyzer());
        IndexWriter    writer       = new IndexWriter(directory, config);
        ...
        Path file                   = new Path(""/Users/eathanassiou/myWWW/prod/BeispielDatei.txt";");
        addFileToIndex(writer, file);          
        ....

        private static void addFileToIndex(IndexWriter writer, Path filePath) throws IOException {
    
            String      absPath     = filePath.toAbsolutePath().toString();
            Document    document    = new Document();
            Reader      reader      = new InputStreamReader(new FileInputStream(absPath), "UTF-8");
    
            document.add(new TextField("contents", reader));
    
            StringField sf     = new StringField("pathname", absPath, Field.Store.YES );
            document.add(sf);
    
            writer.addDocument(document);
        }
Der Code braucht jetzt aber schon noch ein paar Erläuterungen ... in ihm wurde der Constructor new StandardAnalyzer() verwendet um einen IndexWriter zu konfigurieren. Mit dem Analyzer wird festgelegt wie die Dateien indexiert werden, d.h. Einstellungen zum Parsen, der Stammformbildung und Stoppwortlisten werden vorgenommen. StandardAnalyzer() ist ein englischsprachiger Parser mit einer Standardstoppwortliste. Lucene verfügt über einige weitere Analyzer. Für unterschiedliche Anforderungen, verwendet man verschiedene Analyzer.

Das Lucene-Analyzers-Common-Modul enthält alle genannten Komponenten, Die am häufigsten verwendeten Analysatoren finden sich im Paket org.apache.lucene.analysis.. Sprachspezifische Analysatoren finden sich in den packages org.apache.lucene.analysis {Sprachcode} .

Das Indexieren von jpeg Dateien

jpeg Dateien sind Binärdateien. Das indexieren auf Wortebene ist nicht anwendbar, genauer, es könnte nur auf die inkludierten Tags - falls vorhanden. Das MacOS beispielweise verwendet das KEYWORD IPTC -Tag zur Suche in seinem "Finder". Ich habe das mit den Tags zur Indexierung angeschaut, und dann sein lassen. Verwendet habe ich den Dateinamen gemäß der in dem vorherigen Kapitel beschrieben Namenskonvention. Der Grund war kein technischer, sondern ein rein praktischer.t. Es fehlten doch einige Wagen, und bei den manuell oder semiautomatisch beschafften Dateinen ist einfach schneller, den Dateinamen zu ändern als mühsam die IPTC Tags neu zu setzen. Das eine geht auf OS-Ebene, das andere ist Software, gerade beim Entwickeln geht es mit Dateinamen schneller - vor allem bei Sichtkontrollen und Korrekturen.

Zur Umsetzung war aber ein kleiner Kniff nötig. Dem Lucene Indexierer musste die Existenz einer Datei, mit den einzelnen Elementen des Dateinamen als Inhalt "vorgegaukelt" werden. Aber das ging prima mit einer temporären Datei.

    private static void addFileToIndex(IndexWriter writer, Path filePath) throws IOException {
        String fileName     = filePath.getFileName().toString();
        String absPath      = filePath.toAbsolutePath().toString();
        Document document   = new Document();

        String tmpFile      = "/Users/eathanassiou/myWWW/prod/tmp/tmp.data";   //Erzeugen des temporären Files 
        String f            = fileName.replace(".jpg","");
        String[] tokens     = f.split("_");
        appendToFile(tmpFile, tokens);                                        // Elemente des Dateinamens als einzelne Zeilen in der Datei

        Reader reader       = new InputStreamReader(new FileInputStream(tmpFile), "UTF-8");
        document.add(new TextField("contents", reader));
        deleteFile(tmpFile);

        StringField sf      = new StringField("filename", fileName, Field.Store.YES );
        document.add(sf);
        StringField sf2     = new StringField("pathname", absPath, Field.Store.YES );
        document.add(sf2);

        writer.addDocument(document);
    }

Suchen mit Lucene

Um passende Dokumente zu eines Suchanfrage zu finden, braucht man den Pfad zum indexIndex, die Suchanfrage (=Query), die maximale Anzahl von Dokumenten, und das TextField was gesucht werden soll, in der Regel ist das der String "content".
    ...
    final String iField ="contents";
    searchFiles(String indexPath, String iField, "Piquet", 100) {
    ...

    public static List searchFiles(String indexPath, String inField, String queryString, int Anz) {
        try {
            StandardAnalyzer analyzer   = new StandardAnalyzer();
            Query query                 = new QueryParser(inField, analyzer).parse(queryString);
            Directory directory         = FSDirectory.open(Paths.get(indexPath));
            IndexReader reader          = DirectoryReader.open(directory);
            IndexSearcher searcher      = new IndexSearcher(reader);

            TopDocs topDocs = searcher.search(query, Anz);

            List results = new ArrayList<>();
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document document = searcher.doc(scoreDoc.doc); //
                results.add(document.get("pathname") );  
            }
            return results;
        } catch (IOException | ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
Lucene ist ein Indexierer, das heist es liefert die Dokumenten als Ergebnis wieder , die der Anfrage am ehesten entsprechen. Bei der Indexierung von Attributen aus Dateinamen(wie in meiner Anwendung) benutzt man Lucene etwas unter den Möglichkeiten des Systems, aber das kann ja noch kommen ...