Lucene’s Indizierungscode erforschen: Teil 1

Nächste: Lucene’s Indizierungscode erforschen: Teil 2 Ich habe mich zwar schon ein wenig mit der Suchseite von Lucene beschäftigt, bin…

Nächste: Lucene’s Indizierungscode erforschen: Teil 2

Ich habe mich zwar schon ein wenig mit der Suchseite von Lucene beschäftigt, bin aber mit der Hardcore-Indizierungsseite weit weniger vertraut (ich spreche vom Hardcore-Code, Gelegenheitsnutzer müssen sich nicht bewerben – es sei denn, Sie sind interessiert). Ich würde gerne mehr über den Indizierungscode von Lucene lernen, aber es ist nicht so einfach, ihn auf den ersten Blick zu durchschauen. Ich möchte nicht gleich ein Experte sein, sondern einen Überblick über die Details haben, die bei der Erstellung eines Lucene-Indexes eine Rolle spielen. In solchen Fällen finde ich es am besten, wenn ich von einem hohen Niveau aus anfange und mich vorarbeite, um hoffentlich den Gesamtprozess und dann die einzelnen Teile zu verstehen.

Aus unserer Verwendung von Lucene wissen wir, dass sich der Indexierungscode um die IndexWriter-Klasse drehen muss. Um zu verstehen, was IndexWriter tut, werde ich einige Schlüsselmethoden einer sehr einfachen Lucene-Testanwendung nachverfolgen, die lediglich ein kleines Dokument zu einem Index mit einem IndexWriter hinzufügt und dann diesen IndexWriter schließt. Ich werde mich auf die IndexWriter-Methoden beschränken, damit die Informationen verdaulich sind, und zunächst nur die wichtigsten Methoden aufzeichnen. Wir wollen uns nicht zu sehr in den Details verlieren – die Methoden werden sich im Laufe der Zeit ohnehin ändern. Die Idee ist, sich einen Überblick über den zugrunde liegenden Prozess zu verschaffen.

Außerdem sollten wir uns an unser Anwenderwissen über einen Lucene-Index erinnern. Der Index setzt sich aus 1-n Segmenten zusammen. Jedes Segment enthält eine Anzahl von Dokumenten. Ein Lucene-Dokument enthält eine Anzahl von Feldern, d.h. lediglich einen Feldnamen, einen Wert und Attribute. Neue Segmente werden geschrieben, wenn wir dem Index Dokumente hinzufügen, und die Segmente werden im Laufe der Zeit anhand bestimmter Kriterien zusammengeführt. Je weniger Segmente Sie durchsuchen müssen, desto besser ist die Leistung. Bei der Suche im gesamten Index wird jedes Segment durchsucht und bei einer Optimierung werden alle Segmente zu einem zusammengeführt.

Der Test Code

Directory directory = new RAMDirectory();
Analyzer analyzer = new SimpleAnalyzer();
IndexWriter writer = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);

String doc = "a b c d e";
Document d = new Document();
d.add(new Field("contents", doc, Field.Store.YES, Field.Index.ANALYZED));
writer.addDocument(d);
writer.close();

Und nun die Spur:

Nach der Initialisierung der statischen Variablen wird die IndexWriter-Instanz initialisiert:

 *Enter:void IndexWriter.init(Directory, Analyzer, boolean, boolean, IndexDeletionPolicy, boolean, int, DocumentsWriter.IndexingChain, IndexCommit) :1
 *Exit:void IndexWriter.init(Directory, Analyzer, boolean, boolean, IndexDeletionPolicy, boolean, int, DocumentsWriter.IndexingChain, IndexCommit) :1

Dann fügen wir das Dokument hinzu und schließen den IndexWriter.

 *Enter:void IndexWriter.addDocument(Document) :1
 *Exit:void IndexWriter.addDocument(Document) :1

Wir wollen sicherlich noch tiefer in IndexWriter.addDocument einsteigen, denn wir wissen, dass dort viele interessante Dinge passieren. Aber vorher können wir uns einen guten Überblick über den Schließvorgang verschaffen, der in Zeile 5 beginnt.

 *Enter:void IndexWriter.close() :1
  *Enter:void IndexWriter.close(boolean) :2
   // another thread may be closing
   *Enter:boolean IndexWriter.shouldClose() :3
   *Exit:boolean IndexWriter.shouldClose() :3
   // close doc writer, flush / maybe merge / commit / close everything
   *Enter:void IndexWriter.closeInternal(boolean) :3
    *Enter:boolean IndexWriter.doFlush(boolean, boolean) :4
    *Exit:boolean IndexWriter.doFlush(boolean, boolean) :4
    *Enter:void IndexWriter.maybeMerge() :4
    *Exit:void IndexWriter.maybeMerge() :4
    // either abort merges or wait for merges
    *Enter:void IndexWriter.finishMerges(boolean) :4
    *Exit:void IndexWriter.finishMerges(boolean) :4
    // commit all pending adds and deletes, sync index files
    *Enter:void IndexWriter.commit(long) :4
     *Enter:void IndexWriter.startCommit(long, String) :5
     *Exit:void IndexWriter.startCommit(long, String) :5
     *Enter:void IndexWriter.finishCommit() :5
      *Enter:void IndexWriter.setRollbackSegmentInfos(SegmentInfos) :6
      *Exit:void IndexWriter.setRollbackSegmentInfos(SegmentInfos) :6
     *Exit:void IndexWriter.finishCommit() :5
    *Exit:void IndexWriter.commit(long) :4
   *Exit:void IndexWriter.closeInternal(boolean) :3
  *Exit:void IndexWriter.close(boolean) :2
 *Exit:void IndexWriter.close() :1

Wir sind also noch nicht weit gekommen, aber gleichzeitig sind wir am Ende unseres Testcodes angelangt. Wir sehen, dass IndexWriter ein wenig init benötigt und flush, vielleicht merge, und dann commit beim Schließen, wenn wir einige Dokumente hinzufügen. Wir haben ein Gefühl für den Gesamtprozess, den wir untersuchen, und in Teil 2 können wir damit beginnen, die Vorgänge beim Aufruf von IndexWriter.addDocument genauer zu untersuchen.

Nächste: Lucene’s Indizierungscode erforschen: Teil 2

Update:

Ich habe eine Anfrage für den AspectJ-Code erhalten, mit dem die Traces erstellt wurden. Hier ist ein Beispielaspekt. Beachten Sie, dass dieses Beispiel nicht thread-sicher ist (gemeinsam genutzter StringBuilder). Wenn Sie also mehrere Threads verfolgen, müssen Sie ein wenig daran arbeiten. Damit können Sie jedoch beginnen. Wenn Sie das Eclipse AspectJ-Modul installieren, die AspectJ-Natur zum Lucene-Projekt in Eclipse hinzufügen (klicken Sie mit der rechten Maustaste auf das Projekt und sehen Sie sich das entsprechende Menü an), den Aspekt irgendwo mit den Lucene-Quelldateien ablegen (nennen Sie ihn Trace.aj – Sie können den Aspekt auch direkt in Ihre Java-Datei einfügen) und dann bauen und ausführen, sollten Sie bereit sein, herumzuspielen. Es gibt auch Kommandozeilenprogramme und Ant-Tasks, wenn Sie diesen Weg bevorzugen – es ist nur ein bisschen mehr Aufwand.

// the following aspect will print entry/exit stamps for all public methods
// that begin with org.apache.lucene and all private methods of IndexWriter (just to give an example)
public aspect Trace {

  private StringBuilder sb = new StringBuilder();

  pointcut traceableCalls() : traceMethods();

  pointcut traceMethods() :
    execution(public * org.apache.lucene..*(..) ) || execution(private * org.apache.lucene.IndexWriter..*(..) );

  /**
   * log method entry.
   */
  before() : traceableCalls() {
    sb.append(" ");
    int indent = sb.length();
    String enterLine = sb.toString() + "*Enter:"
        + thisJoinPoint.getSignature().toString() + " indent:" + indent;
    System.out.println(enterLine + "n");
  }

  /**
   * log method exit.
   */
  after() : traceableCalls(){
    int indent = sb.length();
    String exitLine = sb.toString() + "*Exit:"
        + thisJoinPoint.getSignature().toString() + " indent:" + indent
        + " thread:" + Thread.currentThread().getId();
    sb.setLength(sb.length() - 1);
    System.out.println(exitLine + "n");
  }
}

You Might Also Like

KI-Agenten dominieren den Einkauf. Ist Ihre Website auf die KI-gestützte Suche vorbereitet?

Generative KI-Agenten wie ChatGPT definieren die Produktsuche neu. Erfahren Sie, wie Sie...

Read More

Vom Suchunternehmen zum praktischen KI-Pionier: Unsere Vision für 2025 und darüber hinaus

CEO Mike Sinoway gibt Einblicke in die Zukunft der KI und stellt...

Read More

Wenn KI schief geht: Fehlschläge in der realen Welt und wie man sie vermeidet

Lassen Sie nicht zu, dass Ihr KI-Chatbot einen 50.000 Dollar teuren Tahoe...

Read More

Quick Links