Wie bekomme ich ein Token von einem Lucene TokenStream?

74

Ich versuche, Apache Lucene zum Tokenisieren zu verwenden, und ich bin verblüfft über den Prozess, Tokens von a zu erhalten TokenStream.

Das Schlimmste ist, dass ich mir die Kommentare in den JavaDocs ansehe, die meine Frage beantworten.

http://lucene.apache.org/java/3_0_1/api/core/org/apache/lucene/analysis/TokenStream.html#incrementToken%28%29

Irgendwie soll ein AttributeSourceverwendet werden, anstatt Tokens. Ich bin total ratlos.

Kann jemand erklären, wie man tokenartige Informationen von einem TokenStream erhält?

Eric Wilson
quelle

Antworten:

114

Ja, es ist ein wenig verworren (im Vergleich zum guten alten Weg), aber das sollte es tun:

TokenStream tokenStream = analyzer.tokenStream(fieldName, reader);
OffsetAttribute offsetAttribute = tokenStream.getAttribute(OffsetAttribute.class);
TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);

while (tokenStream.incrementToken()) {
    int startOffset = offsetAttribute.startOffset();
    int endOffset = offsetAttribute.endOffset();
    String term = termAttribute.term();
}

Edit: Der neue Weg

Laut Donotello wurde TermAttributezugunsten von abgelehnt CharTermAttribute. Laut jpountz (und Lucenes Dokumentation) addAttributeist dies wünschenswerter als getAttribute.

TokenStream tokenStream = analyzer.tokenStream(fieldName, reader);
OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);

tokenStream.reset();
while (tokenStream.incrementToken()) {
    int startOffset = offsetAttribute.startOffset();
    int endOffset = offsetAttribute.endOffset();
    String term = charTermAttribute.toString();
}
Adam Paynter
quelle
6
Jetzt ist TermAttribute beraubt. Wie ich sehen kann, können wir CharTermAttributeImpl.toString()stattdessen so etwas wie verwenden
Donotello
@ Donotello: Vielen Dank dafür!
Adam Paynter
6
Sie sollten addAttribute anstelle von getAttribute verwenden. Von lucene javadocs: "Es wird empfohlen, addAttribute (java.lang.Class) auch bei Verbrauchern von TokenStreams immer zu verwenden, da Sie nicht wissen können, ob ein bestimmter TokenStream wirklich ein bestimmtes Attribut verwendet" lucene.apache.org/core/old_versioned_docs/versions / 3_5_0 / api /…
jpountz
1
@jpountz: Danke für den Tipp! Ich habe die Antwort entsprechend geändert.
Adam Paynter
2
Musste reset()mit Lucene 4.3 anrufen , nahm sich also die Freiheit, es hinzuzufügen
Enno Shioji
38

So sollte es sein (eine saubere Version von Adams Antwort):

TokenStream stream = analyzer.tokenStream(null, new StringReader(text));
CharTermAttribute cattr = stream.addAttribute(CharTermAttribute.class);
stream.reset();
while (stream.incrementToken()) {
  System.out.println(cattr.toString());
}
stream.end();
stream.close();
yegor256
quelle
10
Ihr Code funktionierte nicht richtig, bis ich vor der while-Schleife eine stream.reset () hinzugefügt habe. Ich verwende Lucene 4.0, daher kann dies eine kürzlich erfolgte Änderung sein. Siehe das Beispiel am Ende dieser Seite: lucene.apache.org/core/4_0_0-BETA/core/org/apache/lucene/…
Es wurde versucht, den Aufruf reset () zu bearbeiten, wodurch eine NPE in Lucene bei incrementToken () vermieden wird, aber alle bis auf einen Peer lehnten die Bearbeitung als falsch ab. Die Lucene-Dokumente sagen explizit, dass "Der Verbraucher ruft reset ()" vor "Der Verbraucher ruft incrementToken ()" in der TokenStream-API auf
William Price
reset()
Musste auch
Vielleicht ist die Frage seltsam, aber schließlich ist nicht ganz klar, wie man das nächste Token erhält (nicht die nächste Zeichenfolge).
Serhio
3

Für die neueste Version von Lucene 7.3.1

    // Test the tokenizer
    Analyzer testAnalyzer = new CJKAnalyzer();
    String testText = "Test Tokenizer";
    TokenStream ts = testAnalyzer.tokenStream("context", new StringReader(testText));
    OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class);
    try {
        ts.reset(); // Resets this stream to the beginning. (Required)
        while (ts.incrementToken()) {
            // Use AttributeSource.reflectAsString(boolean)
            // for token stream debugging.
            System.out.println("token: " + ts.reflectAsString(true));

            System.out.println("token start offset: " + offsetAtt.startOffset());
            System.out.println("  token end offset: " + offsetAtt.endOffset());
        }
        ts.end();   // Perform end-of-stream operations, e.g. set the final offset.
    } finally {
        ts.close(); // Release resources associated with this stream.
    }

Referenz: https://lucene.apache.org/core/7_3_1/core/org/apache/lucene/analysis/package-summary.html

Flamingo
quelle
1

Es gibt zwei Variationen in der OP-Frage:

  1. Was ist "der Prozess, um Token von einem TokenStream zu erhalten"?
  2. "Kann jemand erklären, wie man tokenartige Informationen von einem TokenStream erhält?"

Aktuelle Versionen der Lucene-Dokumentation zum BeispielToken (Hervorhebung hinzugefügt):

HINWEIS: Ab Version 2.9 ist es nicht mehr erforderlich , Token zu verwenden. Mit der neuen TokenStream-API kann sie als Convenience-Klasse verwendet werden, die alle Attribute implementiert. Dies ist besonders nützlich, um einfach von der alten zur neuen TokenStream-API zu wechseln.

Und TokenStreamsagt seine API:

... ist von Token-basiert zu Attribut-basiert übergegangen. Die bevorzugte Methode zum Speichern der Informationen eines Tokens ist die Verwendung von AttributeImpls.

Die anderen Antworten auf diese Frage beziehen sich auf # 2 oben: Wie man tokenartige Informationen von einem TokenStreamauf die "neue" empfohlene Weise unter Verwendung von Attributen erhält . Beim Lesen der Dokumentation schlagen die Lucene-Entwickler vor, dass diese Änderung teilweise vorgenommen wurde, um die Anzahl der gleichzeitig erstellten Einzelobjekte zu verringern.

Aber wie einige Leute in den Kommentaren dieser Antworten betont haben, antworten sie nicht direkt auf Nummer 1: Wie bekommt man eine, Tokenwenn man diesen Typ wirklich will / braucht?

Mit der gleichen API-Änderung, die TokenStreamein AttributeSource, Tokenjetzt implementiert Attributeund mit TokenStream.addAttribute verwendet werden kann, genau wie die anderen Antworten für CharTermAttributeund OffsetAttribute. Also haben sie diesen Teil der ursprünglichen Frage wirklich beantwortet, sie haben ihn einfach nicht gezeigt.

Es ist wichtig, dass Sie mit diesem Ansatz zwar Tokenwährend der Schleife zugreifen können , es sich jedoch immer noch um ein einzelnes Objekt handelt, unabhängig davon, wie viele logische Token sich im Stream befinden. Jeder Aufruf von incrementToken()ändert den Status des Tokenzurückgegebenen von addAttribute; Wenn Sie also eine Sammlung verschiedener TokenObjekte erstellen möchten, die außerhalb der Schleife verwendet werden sollen, müssen Sie zusätzliche Arbeit leisten, um ein neues Token Objekt als (tiefe?) Kopie zu erstellen .

William Price
quelle